mirror of
https://github.com/colby-swandale/waterfoul
synced 2025-01-20 22:26:30 +01:00
proper MBC1 implementation
This commit is contained in:
parent
346b4ae443
commit
e72058ec37
2 changed files with 123 additions and 16 deletions
|
@ -3,27 +3,67 @@ module Waterfoul
|
||||||
class MBC1 < ROM
|
class MBC1 < ROM
|
||||||
EXTERNAL_RAM_SIZE = 0x2000
|
EXTERNAL_RAM_SIZE = 0x2000
|
||||||
|
|
||||||
def initialize(program, rom_bank = 1)
|
attr_accessor :ram_enabled, :mode, :rom_bank, :ram_bank
|
||||||
@rom_bank = rom_bank
|
|
||||||
@cartridge_program = program
|
def initialize(program)
|
||||||
@rom_offset = 0x4000
|
@rom_bank = 1
|
||||||
@ram_bank = Array.new EXTERNAL_RAM_SIZE, 0
|
@ram_bank = 1
|
||||||
|
@mode = 0
|
||||||
|
@ram_enabled = false
|
||||||
|
@game_program = program
|
||||||
|
@ram = Array.new EXTERNAL_RAM_SIZE, 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](i)
|
def [](i)
|
||||||
case i & 0xE000
|
case i
|
||||||
when 0x4000, 0x6000
|
when 0x0...0x4000 # ROM Bank 0
|
||||||
relative_addr = (i - 0x4000) + @rom_offset
|
@game_program[i]
|
||||||
@cartridge_program[relative_addr]
|
when 0x4000...0x8000 # ROM Bank n
|
||||||
when 0xA000
|
addr = i - 0x4000
|
||||||
byebug
|
offset = @rom_bank * 0x4000
|
||||||
else
|
@game_program[offset + addr]
|
||||||
@cartridge_program[i]
|
when 0xA000...0xC000
|
||||||
|
fail 'trying to read invalid ram' unless @ram_enabled
|
||||||
|
addr = i - 0xA000
|
||||||
|
if @mode == 0
|
||||||
|
@ram[addr]
|
||||||
|
else
|
||||||
|
offset = @rom_ram_bank_number * 0x8000
|
||||||
|
@ram[offset + addr]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def []=(i,v)
|
def []=(i,v)
|
||||||
byebug
|
case i
|
||||||
|
when 0x0...0x2000
|
||||||
|
@ram_enabled = (v & 0xA == 0xA ? true : false)
|
||||||
|
when 0x2000...0x4000
|
||||||
|
if @mode == 1
|
||||||
|
@rom_bank = (v & 0x1F) | (@rom_bank & 0xE0)
|
||||||
|
else
|
||||||
|
@rom_bank = v & 0x1F
|
||||||
|
end
|
||||||
|
when 0x4000...0x6000
|
||||||
|
v = v & 0x3
|
||||||
|
if @mode == 1
|
||||||
|
@ram_bank = v
|
||||||
|
else
|
||||||
|
@rom_bank = (@rom_bank & 0x1F) | (v << 5)
|
||||||
|
@rom_bank += 1 if [0x0, 0x20, 0x40, 0x60].include? @rom_bank
|
||||||
|
end
|
||||||
|
when 0x6000...0x8000
|
||||||
|
@mode = v & 0x1
|
||||||
|
when 0xA000...0xC000
|
||||||
|
fail 'trying to write invalid ram' unless @ram_enabled
|
||||||
|
addr = i - 0xA000
|
||||||
|
if @mode == 0
|
||||||
|
@ram[addr] = v
|
||||||
|
else
|
||||||
|
offset = @ram_bank * 0x8000
|
||||||
|
@ram[offset + addr] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,80 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Waterfoul::MBC::MBC1 do
|
describe Waterfoul::MBC::MBC1 do
|
||||||
|
let(:program) { double :program }
|
||||||
|
subject { Waterfoul::MBC::MBC1.new program }
|
||||||
|
|
||||||
describe '#[]' do
|
describe '#[]' do
|
||||||
context 'read from addr 0x2000' do
|
context 'when reading at 0x0001' do
|
||||||
it 'reads bytes from bank 0' do
|
it 'reads byte from bank 0' do
|
||||||
|
expect(program).to receive(:[]).with(0x0001)
|
||||||
|
subject[0x0001]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when reading at 0x4001' do
|
||||||
|
it 'reads from bank 1' do
|
||||||
|
expect(program).to receive(:[]).with(0x4001)
|
||||||
|
subject[0x4001]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#[]=' do
|
describe '#[]=' do
|
||||||
|
context 'when writing to 0x1' do
|
||||||
|
it 'enables the external ram' do
|
||||||
|
subject[0x1] = 0xA
|
||||||
|
expect(subject.ram_enabled).to eq true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when writing to 4001' do
|
||||||
|
it 'sets rom bank from 5 bits' do
|
||||||
|
subject[0x2000] = 0xFF
|
||||||
|
expect(subject.rom_bank).to eq 0x1F
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when writing to 0x4000' do
|
||||||
|
context 'with mode set to 1' do
|
||||||
|
before { subject[0x6000] = 0x1 }
|
||||||
|
it 'sets ram bank' do
|
||||||
|
subject[0x4000] = 0x3
|
||||||
|
expect(subject.ram_bank).to eq 0x3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with mode set to 0' do
|
||||||
|
before { subject[0x6000] = 0x0 }
|
||||||
|
before { subject[0x2000] = 0x1F }
|
||||||
|
it 'sets upper 3 bits of rom bank' do
|
||||||
|
subject[0x4000] = 0x3
|
||||||
|
expect(subject.rom_bank).to eq 0x7F
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when writing to 0xA001' do
|
||||||
|
context 'when RAM is enabled' do
|
||||||
|
before { subject[0x1] = 0xA }
|
||||||
|
it 'saves the value to external memory' do
|
||||||
|
subject[0xA001] = 0x1
|
||||||
|
expect(subject[0xA001]).to eq 0x1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ram is disabled' do
|
||||||
|
it 'raises an error' do
|
||||||
|
expect { subject[0xA001] = 0x1 }.to raise_error RuntimeError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'writing to 0x6001' do
|
||||||
|
it 'sets the ROM mode to 1' do
|
||||||
|
subject[0x6001] = 0x1
|
||||||
|
expect(subject.mode).to eq 1
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue