proper MBC1 implementation

This commit is contained in:
Colby 2016-07-20 21:19:04 +10:00
parent 346b4ae443
commit e72058ec37
2 changed files with 123 additions and 16 deletions

View file

@ -3,27 +3,67 @@ module Waterfoul
class MBC1 < ROM
EXTERNAL_RAM_SIZE = 0x2000
def initialize(program, rom_bank = 1)
@rom_bank = rom_bank
@cartridge_program = program
@rom_offset = 0x4000
@ram_bank = Array.new EXTERNAL_RAM_SIZE, 0
attr_accessor :ram_enabled, :mode, :rom_bank, :ram_bank
def initialize(program)
@rom_bank = 1
@ram_bank = 1
@mode = 0
@ram_enabled = false
@game_program = program
@ram = Array.new EXTERNAL_RAM_SIZE, 0
end
def [](i)
case i & 0xE000
when 0x4000, 0x6000
relative_addr = (i - 0x4000) + @rom_offset
@cartridge_program[relative_addr]
when 0xA000
byebug
else
@cartridge_program[i]
case i
when 0x0...0x4000 # ROM Bank 0
@game_program[i]
when 0x4000...0x8000 # ROM Bank n
addr = i - 0x4000
offset = @rom_bank * 0x4000
@game_program[offset + addr]
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
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

View file

@ -1,13 +1,80 @@
require 'spec_helper'
describe Waterfoul::MBC::MBC1 do
let(:program) { double :program }
subject { Waterfoul::MBC::MBC1.new program }
describe '#[]' do
context 'read from addr 0x2000' do
it 'reads bytes from bank 0' do
context 'when reading at 0x0001' 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
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