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
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue