Skip to content

Commit

Permalink
Add support for most MBC5 cart types
Browse files Browse the repository at this point in the history
  • Loading branch information
maxfierke committed Dec 18, 2023
1 parent 9b654ca commit 6dbb17d
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 8 deletions.
4 changes: 4 additions & 0 deletions cart/cartridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ func NewCartridge(r *Reader) (*Cartridge, error) {
rom := make([]byte, r.Header.RomSizeBytes())
copy(rom, r.headerBuf[:])

ram := make([]byte, r.Header.RamSizeBytes())

switch r.Header.CartType {
case CART_TYPE_MBC0:
cartridge.mbc = mbc.NewMBC0(rom)
case CART_TYPE_MBC5, CART_TYPE_MBC5_RAM, CART_TYPE_MBC5_RAM_BAT:
cartridge.mbc = mbc.NewMBC5(rom, ram)
default:
return nil, fmt.Errorf("unsupported or unknown MBC type: %s", r.Header.CartTypeName())
}
Expand Down
20 changes: 20 additions & 0 deletions cart/mbc/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package mbc

import "github.com/maxfierke/gogo-gb/mem"

const (
RAM_BANK_SIZE = 0x2000
ROM_BANK_SIZE = 0x4000
)

func readBankAddr(memory []byte, banks_region mem.MemRegion, bank_size uint16, current_bank uint16, addr uint16) byte {
bank_base_addr := current_bank * bank_size
bank_slot_addr := addr - banks_region.Start
return memory[bank_base_addr+bank_slot_addr]
}

func writeBankAddr(memory []byte, banks_region mem.MemRegion, bank_size uint16, current_bank uint16, addr uint16, value byte) {
bank_base_addr := current_bank * bank_size
bank_slot_addr := addr - banks_region.Start
memory[bank_base_addr+bank_slot_addr] = value
}
14 changes: 6 additions & 8 deletions cart/mbc/mbc0.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import (
"github.com/maxfierke/gogo-gb/mem"
)

const (
mbc0_rom_bank_start = 0x0000
mbc0_rom_bank_end = 0x7FFF
mbc0_ram_bank_start = 0xA000
mbc0_ram_bank_end = 0xBFFF
var (
mbc0_rom_bank = mem.MemRegion{Start: 0x0000, End: 0x7FFF}
mbc0_ram_bank = mem.MemRegion{Start: 0xA000, End: 0xBFFF}
)

type MBC0 struct {
Expand All @@ -22,20 +20,20 @@ func NewMBC0(rom []byte) *MBC0 {
}

func (m *MBC0) OnRead(mmu *mem.MMU, addr uint16) mem.MemRead {
if addr <= mbc0_rom_bank_end {
if addr <= mbc0_rom_bank.End {
return mem.ReadReplace(m.rom[addr])
}

return mem.ReadPassthrough()
}

func (m *MBC0) OnWrite(mmu *mem.MMU, addr uint16, value byte) mem.MemWrite {
if addr <= mbc0_rom_bank_end {
if addr <= mbc0_rom_bank.End {
// Put the Read-Only in ROM
return mem.WriteBlock()
}

if addr >= mbc0_ram_bank_start && addr <= mbc0_ram_bank_end {
if mbc0_ram_bank.Contains(addr, false) {
// RAM is RAM and this is a fake cartridge, so...
return mem.WritePassthrough()
}
Expand Down
112 changes: 112 additions & 0 deletions cart/mbc/mbc5.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package mbc

import (
"fmt"

"github.com/maxfierke/gogo-gb/mem"
)

var (
mbc5_rom_bank_00 = mem.MemRegion{Start: 0x0000, End: 0x3FFF}
mbc5_rom_banks = mem.MemRegion{Start: 0x4000, End: 0x7FFF}
mbc5_ram_banks = mem.MemRegion{Start: 0xA000, End: 0xBFFF}

mbc5_reg_ram_enable = mem.MemRegion{Start: 0x0000, End: 0x1FFF}
mbc5_reg_ram_enable_mask = byte(0xF)
mbc5_reg_ram_enabled = byte(0xA)
mbc5_reg_ram_disabled = byte(0x00)

mbc5_reg_lsb_rom_bank = mem.MemRegion{Start: 0x2000, End: 0x2FFF}
mbc5_reg_lsb_rom_bank_sel_mask = ^uint16(0xFF)

mbc5_reg_msb_rom_bank = mem.MemRegion{Start: 0x3000, End: 0x3FFF}
mbc5_reg_msb_rom_bank_sel_mask = ^uint16(0x100)

mbc5_reg_ram_bank = mem.MemRegion{Start: 0x4000, End: 0x5FFF}
mbc5_reg_ram_bank_sel_mask = byte(0xF)
)

type MBC5 struct {
cur_ram_bank uint8
cur_rom_bank uint16
ram []byte
ram_enabled bool
rom []byte
}

func NewMBC5(rom []byte, ram []byte) *MBC5 {
return &MBC5{
cur_ram_bank: 0,
cur_rom_bank: 0,
ram: ram,
ram_enabled: false,
rom: rom,
}
}

func (m *MBC5) OnRead(mmu *mem.MMU, addr uint16) mem.MemRead {
if mbc5_rom_bank_00.Contains(addr, false) {
return mem.ReadReplace(m.rom[addr])
} else if mbc5_rom_banks.Contains(addr, false) {
bankByte := readBankAddr(
m.rom,
mbc5_rom_banks,
ROM_BANK_SIZE,
m.cur_rom_bank,
addr,
)
return mem.ReadReplace(bankByte)
} else if mbc5_ram_banks.Contains(addr, false) {
if m.ram_enabled {
bankByte := readBankAddr(
m.ram,
mbc5_ram_banks,
RAM_BANK_SIZE,
uint16(m.cur_ram_bank),
addr,
)
return mem.ReadReplace(bankByte)
} else {
// Docs say this is usually 0xFF, but not guaranteed. Randomness needed?
return mem.ReadReplace(0xFF)
}
}

return mem.ReadPassthrough()
}

func (m *MBC5) OnWrite(mmu *mem.MMU, addr uint16, value byte) mem.MemWrite {
if mbc5_reg_ram_enable.Contains(addr, false) {
if value&mbc5_reg_ram_enable_mask == mbc5_reg_ram_enabled {
m.ram_enabled = true
} else if value&mbc5_reg_ram_enable_mask == mbc5_reg_ram_disabled {
m.ram_enabled = false
}

// TODO: Log something / panic if unexpected value?

return mem.WriteBlock()
} else if mbc5_reg_lsb_rom_bank.Contains(addr, false) {
m.cur_rom_bank = (m.cur_rom_bank & mbc5_reg_lsb_rom_bank_sel_mask) | uint16(value)
return mem.WriteBlock()
} else if mbc5_reg_msb_rom_bank.Contains(addr, false) {
msb := uint16(value) & 0x1 << 8
m.cur_rom_bank = (m.cur_rom_bank & mbc5_reg_msb_rom_bank_sel_mask) | msb
return mem.WriteBlock()
} else if mbc5_reg_ram_bank.Contains(addr, false) {
m.cur_ram_bank = value & mbc5_reg_ram_bank_sel_mask
return mem.WriteBlock()
} else if mbc5_ram_banks.Contains(addr, false) {
writeBankAddr(
m.ram,
mbc5_ram_banks,
RAM_BANK_SIZE,
uint16(m.cur_ram_bank),
addr,
value,
)
return mem.WriteBlock()
}

panic(fmt.Sprintf("Attempting to write 0x%x @ 0x%x, which is out-of-bounds for MBC5", value, addr))
}
8 changes: 8 additions & 0 deletions mem/mmu.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ type MemRegion struct {
End uint16
}

func (region *MemRegion) Contains(addr uint16, exclusive bool) bool {
if exclusive {
return region.Start >= addr && addr < region.End
} else {
return region.Start >= addr && addr <= region.End
}
}

type MemBus interface {
Read8(addr uint16) byte
Write8(addr uint16, value byte)
Expand Down

0 comments on commit 6dbb17d

Please sign in to comment.