From 82d389821645b9e2c7b9e0e5375a5749d70072c9 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 29 Aug 2025 17:04:30 +0200 Subject: [PATCH] Add some RAM and a stack --- gb/bus.go | 42 +++++++++++++++++++++++++++++++++- gb/cpu.go | 24 ++++++++++++++++++++ gb/ram.go | 55 ++++++++++++++++++++++++++++++++++++++++++++ gb/stack_test.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 gb/ram.go create mode 100644 gb/stack_test.go diff --git a/gb/bus.go b/gb/bus.go index b7f7aa4..169f1ec 100644 --- a/gb/bus.go +++ b/gb/bus.go @@ -4,12 +4,27 @@ import ( "fmt" ) +// 0x0000 - 0x3FFF : ROM Bank 0 +// 0x4000 - 0x7FFF : ROM Bank 1 - Switchable +// 0x8000 - 0x97FF : CHR RAM +// 0x9800 - 0x9BFF : BG Map 1 +// 0x9C00 - 0x9FFF : BG Map 2 +// 0xA000 - 0xBFFF : Cartridge RAM +// 0xC000 - 0xCFFF : RAM Bank 0 +// 0xD000 - 0xDFFF : RAM Bank 1-7 - switchable - Color only +// 0xE000 - 0xFDFF : Reserved - Echo RAM +// 0xFE00 - 0xFE9F : Object Attribute Memory +// 0xFEA0 - 0xFEFF : Reserved - Unusable +// 0xFF00 - 0xFF7F : I/O Registers +// 0xFF80 - 0xFFFE : Zero Page + type Bus struct { Cart *Cartridge + RAM *RAM } func NewBus(cart *Cartridge) *Bus { - bus := Bus{Cart: cart} + bus := Bus{Cart: cart, RAM: NewRAM()} return &bus } @@ -23,12 +38,21 @@ func (bus *Bus) Read(address uint16) byte { return 0 } return value + } else if address < 0xE000 { + return bus.RAM.WRAMRead(address) } else { fmt.Printf("Reading from bus address %X not implemented!\n", address) return 0 } } +func (bus *Bus) Read16(address uint16) uint16 { + lo := bus.Read(address) + hi := bus.Read(address + 1) + + return uint16(lo) | uint16(hi)<<8 +} + func (bus *Bus) Write(address uint16, value byte) error { if address < 0x8000 { // ROM data @@ -37,7 +61,23 @@ func (bus *Bus) Write(address uint16, value byte) error { return fmt.Errorf("Error writing to bus address %X: %s", address, err) } return nil + } else if address < 0xE000 { + bus.RAM.WRAMWrite(address, value) + return nil } else { return fmt.Errorf("Writing to bus address %X not implemented!", address) } } + +func (bus *Bus) Write16(address uint16, value uint16) error { + err := bus.Write(address+1, byte((value>>8)&0xFF)) + if err != nil { + return err + } + err = bus.Write(address, byte(value&0xFF)) + if err != nil { + return err + } + + return nil +} diff --git a/gb/cpu.go b/gb/cpu.go index a7ce5fd..93ba4ec 100644 --- a/gb/cpu.go +++ b/gb/cpu.go @@ -110,3 +110,27 @@ func (cpu *CPU) ToggleFlag(flag CPUFlags) { func (cpu *CPU) IsFlagSet(flag CPUFlags) bool { return cpu.Regs.F&flag != 0 } + +func (cpu *CPU) StackPush(data byte) { + cpu.Regs.SP-- + cpu.Bus.Write(cpu.Regs.SP, data) +} + +func (cpu *CPU) StackPush16(data uint16) { + cpu.StackPush(byte((data >> 8) & 0xFF)) + cpu.StackPush(byte(data & 0xFF)) +} + +func (cpu *CPU) StackPop() byte { + val := cpu.Bus.Read(cpu.Regs.SP) + cpu.Regs.SP++ + + return val +} + +func (cpu *CPU) StackPop16() uint16 { + lo := cpu.StackPop() + hi := cpu.StackPop() + + return uint16(hi)<<8 | uint16(lo) +} diff --git a/gb/ram.go b/gb/ram.go new file mode 100644 index 0000000..f9d8e90 --- /dev/null +++ b/gb/ram.go @@ -0,0 +1,55 @@ +package gb + +import ( + "fmt" + "os" +) + +type RAM struct { + WRAM [0x2000]byte + HRAM [0x80]byte +} + +func NewRAM() *RAM { + ram := RAM{} + + return &ram +} + +func (ram *RAM) WRAMRead(address uint16) byte { + // TODO(m): Understand this line + address -= 0xC000 + + if address >= 0x2000 { + fmt.Printf("Reading from WRAM: invalid address %X", address+0xC000) + os.Exit(1) + } + + return ram.WRAM[address] +} + +func (ram *RAM) WRAMWrite(address uint16, value byte) { + // TODO(m): Understand this line + address -= 0xC000 + + if address >= 0x2000 { + fmt.Printf("Writing to WRAM: invalid address %X", address) + os.Exit(1) + } + + ram.WRAM[address] = value +} + +func (ram *RAM) HRAMRead(address uint16) byte { + // TODO(m): Understand this line + address -= 0xFF80 + + return ram.HRAM[address] +} + +func (ram *RAM) HRAMWrite(address uint16, value byte) { + // TODO(m): Understand this line + address -= 0xFF80 + + ram.HRAM[address] = value +} diff --git a/gb/stack_test.go b/gb/stack_test.go new file mode 100644 index 0000000..479d22f --- /dev/null +++ b/gb/stack_test.go @@ -0,0 +1,59 @@ +package gb + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStackPush(t *testing.T) { + cpu := createCPU([]byte{0x00, 0x00, 0x00}) + + cpu.StackPush(0xDE) + + // Should decrement the stack pointer + assert.Equal(t, cpu.Regs.SP, uint16(0xDFFE)) + + // Should write the value to the stack + assert.Equal(t, cpu.Bus.Read(cpu.Regs.SP), byte(0xDE)) +} + +func TestStackPush16(t *testing.T) { + cpu := createCPU([]byte{0x00, 0x00, 0x00}) + + cpu.StackPush16(0xDEAD) + + // Should decrement the stack pointer twice + assert.Equal(t, cpu.Regs.SP, uint16(0xDFFD)) + + // Should write the value to the stack + assert.Equal(t, cpu.Bus.Read16(cpu.Regs.SP), uint16(0xDEAD)) +} + +func TestStackPop(t *testing.T) { + cpu := createCPU([]byte{0x00, 0x00, 0x00}) + + cpu.StackPush(0xDE) + + val := cpu.StackPop() + + // Should increment the stack pointer + assert.Equal(t, cpu.Regs.SP, uint16(0xDFFF)) + + // Should return the byte value + assert.Equal(t, val, byte(0xDE)) +} + +func TestStackPop16(t *testing.T) { + cpu := createCPU([]byte{0x00, 0x00, 0x00}) + + cpu.StackPush16(0xBEEF) + + val := cpu.StackPop16() + + // Should increment the stack pointer + assert.Equal(t, cpu.Regs.SP, uint16(0xDFFF)) + + // Should return the 16 bit value + assert.Equal(t, val, uint16(0xBEEF)) +}