Skip to content

Commit

Permalink
Add some tests for MMU IO & handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
maxfierke committed Dec 16, 2023
1 parent b010b94 commit fcb1947
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 31 deletions.
47 changes: 25 additions & 22 deletions cpu/cpu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,106 +30,102 @@ func assertNextPC(t *testing.T, nextPC uint16, expectedNextPC uint16) {
}
}

var NULL_MMU = mem.NewMMU([]byte{})

func TestExecuteAdd8NonOverflowingTargetA(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
inst, _ := cpu.opcodes.InstructionFromByte(cpu.PC.Read(), 0x87, false)

cpu.Reg.A.Write(0x7)

cpu.Execute(mmu, inst)
cpu.Execute(NULL_MMU, inst)

assertRegEquals(t, cpu.Reg.A.Read(), 0xE)
assertFlags(t, cpu, false, false, false, false)
}

func TestExecuteAdd8NonOverflowingTargetC(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
inst, _ := cpu.opcodes.InstructionFromByte(cpu.PC.Read(), 0x81, false)

cpu.Reg.A.Write(0x7)
cpu.Reg.C.Write(0x3)

cpu.Execute(mmu, inst)
cpu.Execute(NULL_MMU, inst)

assertRegEquals(t, cpu.Reg.A.Read(), 0xA)
assertFlags(t, cpu, false, false, false, false)
}

func TestExecuteAdd8NonOverflowingTargetCWithCarry(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
inst, _ := cpu.opcodes.InstructionFromByte(cpu.PC.Read(), 0x81, false)

cpu.Reg.A.Write(0x7)
cpu.Reg.C.Write(0x3)
cpu.Reg.F.Carry = true

cpu.Execute(mmu, inst)
cpu.Execute(NULL_MMU, inst)

assertRegEquals(t, cpu.Reg.A.Read(), 0xA)
assertFlags(t, cpu, false, false, false, false)
}

func TestExecuteAdd8TargetBCarry(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
inst, _ := cpu.opcodes.InstructionFromByte(cpu.PC.Read(), 0x80, false)

cpu.Reg.A.Write(0xFC)
cpu.Reg.B.Write(0x9)

cpu.Execute(mmu, inst)
cpu.Execute(NULL_MMU, inst)

assertRegEquals(t, cpu.Reg.A.Read(), 0x5)
assertFlags(t, cpu, false, false, true, true)
}

func TestExecuteAdd16TargetHL(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
inst, _ := cpu.opcodes.InstructionFromByte(cpu.PC.Read(), 0x29, false)

cpu.Reg.HL.Write(0x2331)

cpu.Execute(mmu, inst)
cpu.Execute(NULL_MMU, inst)

assertRegEquals(t, cpu.Reg.HL.Read(), 0x4662)
assertFlags(t, cpu, false, false, false, false)
}

func TestExecuteAdd16TargetBCHalfCarry(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
inst, _ := cpu.opcodes.InstructionFromByte(cpu.PC.Read(), 0x09, false)

cpu.Reg.HL.Write(0x0300)
cpu.Reg.BC.Write(0x0700)

cpu.Execute(mmu, inst)
cpu.Execute(NULL_MMU, inst)

assertRegEquals(t, cpu.Reg.HL.Read(), 0x0A00)
assertFlags(t, cpu, false, false, true, false)
}

func TestExecuteAdd16TargetDECarry(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
inst, _ := cpu.opcodes.InstructionFromByte(cpu.PC.Read(), 0x19, false)

cpu.Reg.HL.Write(0xF110)
cpu.Reg.DE.Write(0x0FF0)

cpu.Execute(mmu, inst)
cpu.Execute(NULL_MMU, inst)

assertRegEquals(t, cpu.Reg.HL.Read(), 0x0100)
assertFlags(t, cpu, false, false, true, true)
}

func TestExecuteJump(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
ram := make([]byte, 0xFFFF)
mmu := mem.NewMMU(ram)
cpu.PC.Write(0xF8)

mmu.Write8(0xF9, 0xFC)
Expand All @@ -145,7 +141,8 @@ func TestExecuteJump(t *testing.T) {

func TestExecuteJumpZero(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
ram := make([]byte, 0xFFFF)
mmu := mem.NewMMU(ram)
cpu.PC.Write(0xF8)
cpu.Reg.F.Zero = true

Expand All @@ -162,7 +159,8 @@ func TestExecuteJumpZero(t *testing.T) {

func TestExecuteJumpCarry(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
ram := make([]byte, 0xFFFF)
mmu := mem.NewMMU(ram)
cpu.PC.Write(0xF8)
cpu.Reg.F.Carry = true

Expand All @@ -179,7 +177,8 @@ func TestExecuteJumpCarry(t *testing.T) {

func TestExecuteNoJumpCarry(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
ram := make([]byte, 0xFFFF)
mmu := mem.NewMMU(ram)
cpu.PC.Write(0xF8)
cpu.Reg.F.Carry = true

Expand All @@ -196,7 +195,8 @@ func TestExecuteNoJumpCarry(t *testing.T) {

func TestExecuteNoJumpNoCarry(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
ram := make([]byte, 0xFFFF)
mmu := mem.NewMMU(ram)
cpu.PC.Write(0xF8)

mmu.Write8(0xF9, 0xFC)
Expand All @@ -212,7 +212,8 @@ func TestExecuteNoJumpNoCarry(t *testing.T) {

func TestExecuteJumpNoZero(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
ram := make([]byte, 0xFFFF)
mmu := mem.NewMMU(ram)
cpu.PC.Write(0xF8)

mmu.Write8(0xF9, 0xFC)
Expand All @@ -228,7 +229,8 @@ func TestExecuteJumpNoZero(t *testing.T) {

func TestExecuteJumpNoCarry(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
ram := make([]byte, 0xFFFF)
mmu := mem.NewMMU(ram)
cpu.PC.Write(0xF8)

mmu.Write8(0xF9, 0xFC)
Expand All @@ -244,7 +246,8 @@ func TestExecuteJumpNoCarry(t *testing.T) {

func TestExecuteJumpHL(t *testing.T) {
cpu, _ := NewCPU()
mmu := mem.NewMMU()
ram := make([]byte, 0xFFFF)
mmu := mem.NewMMU(ram)
cpu.PC.Write(0xF8)
cpu.Reg.HL.Write(0x02FC)

Expand Down
5 changes: 3 additions & 2 deletions hardware/dmg.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dmg
package hardware

import (
"github.com/maxfierke/gogo-gb/cpu"
Expand All @@ -16,7 +16,8 @@ func NewDMG() (*DMG, error) {
return nil, err
}

mmu := mem.NewMMU()
ram := make([]byte, 0xFFFF)
mmu := mem.NewMMU(ram)

return &DMG{
cpu: cpu,
Expand Down
12 changes: 5 additions & 7 deletions mem/mmu.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package mem

const RAMSize = 0xFFFF

type MMU struct {
ram []byte
handleCounter uint
Expand Down Expand Up @@ -39,15 +37,15 @@ type MemWrite struct {
blocked bool
}

func WriteReplacement(value byte) MemWrite {
func WriteReplace(value byte) MemWrite {
return MemWrite{replacement: value, passthrough: false, blocked: false}
}

func WritePassthrough() MemWrite {
return MemWrite{replacement: 0x00, passthrough: true, blocked: false}
}

func WriteBlocked() MemWrite {
func WriteBlock() MemWrite {
return MemWrite{replacement: 0x00, passthrough: false, blocked: true}
}

Expand All @@ -65,16 +63,16 @@ type MemRegion struct {
End uint16
}

type MemAddressable interface {
type MemBus interface {
Read8(addr uint16) byte
Write8(addr uint16, value byte)
Read16(addr uint16) uint16
Write16(addr uint16, value uint16)
}

func NewMMU() *MMU {
func NewMMU(ram []byte) *MMU {
return &MMU{
ram: make([]byte, RAMSize),
ram: ram,
handleCounter: 0,
handles: map[MemHandlerHandle]MemRegion{},
handlers: map[uint16][]MMUHandler{},
Expand Down
135 changes: 135 additions & 0 deletions mem/mmu_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package mem

import (
"testing"
)

const (
handlerReadReplaceValue = 0xAB
handlerWriteReplaceValue = 0xEA
)

type testReplacementHandler struct{}

func (h *testReplacementHandler) OnRead(mmu *MMU, addr uint16) MemRead {
return ReadReplace(handlerReadReplaceValue)
}

func (h *testReplacementHandler) OnWrite(mmu *MMU, addr uint16, value byte) MemWrite {
return WriteReplace(handlerWriteReplaceValue)
}

type testPassthroughHandler struct{}

func (h *testPassthroughHandler) OnRead(mmu *MMU, addr uint16) MemRead {
return ReadPassthrough()
}

func (h *testPassthroughHandler) OnWrite(mmu *MMU, addr uint16, value byte) MemWrite {
return WritePassthrough()
}

type testWriteBlockHandler struct{}

func (h *testWriteBlockHandler) OnRead(mmu *MMU, addr uint16) MemRead {
return ReadPassthrough()
}

func (h *testWriteBlockHandler) OnWrite(mmu *MMU, addr uint16, value byte) MemWrite {
return WriteBlock()
}

func assertAddrEquals[T uint8 | uint16](t *testing.T, actual T, expected T) {
if actual != expected {
t.Errorf("Expected 0x%X, but got 0x%x", expected, actual)
}
}

func TestMmuBasicReads(t *testing.T) {
ram := make([]byte, 0xFFFF)
mmu := NewMMU(ram)

ram[0x100] = 0xC0
ram[0x101] = 0xEE
ram[0x102] = 0xFF

assertAddrEquals(t, mmu.Read8(0x100), 0xC0)
assertAddrEquals(t, mmu.Read16(0x101), 0xFFEE)
}

func TestMmuBasicWrites(t *testing.T) {
ram := make([]byte, 0xFFFF)
mmu := NewMMU(ram)

mmu.Write8(0x100, 0xC0)
mmu.Write16(0x101, 0xFFEE)

assertAddrEquals(t, ram[0x100], 0xC0)
assertAddrEquals(t, ram[0x101], 0xEE)
assertAddrEquals(t, ram[0x102], 0xFF)
}

func TestMmuReadHandlerReplacement(t *testing.T) {
ram := make([]byte, 0xFFFF)
mmu := NewMMU(ram)

mmu.AddHandler(MemRegion{Start: 0x100, End: 0x200}, &testReplacementHandler{})

ram[0x103] = 0x11
ram[0x201] = 0x22

assertAddrEquals(t, mmu.Read8(0x103), handlerReadReplaceValue)
assertAddrEquals(t, mmu.Read8(0x201), 0x22)
}

func TestMmuReadHandlerPassthrough(t *testing.T) {
ram := make([]byte, 0xFFFF)
mmu := NewMMU(ram)

mmu.AddHandler(MemRegion{Start: 0x100, End: 0x200}, &testPassthroughHandler{})

ram[0x103] = 0x11
ram[0x201] = 0x22

assertAddrEquals(t, mmu.Read8(0x103), 0x11)
assertAddrEquals(t, mmu.Read8(0x201), 0x22)
}

func TestMmuWriteHandlerReplacement(t *testing.T) {
ram := make([]byte, 0xFFFF)
mmu := NewMMU(ram)

mmu.AddHandler(MemRegion{Start: 0x100, End: 0x200}, &testReplacementHandler{})

mmu.Write8(0x103, 0x11)
mmu.Write8(0x201, 0x22)

assertAddrEquals(t, ram[0x103], handlerWriteReplaceValue)
assertAddrEquals(t, ram[0x201], 0x22)
}

func TestMmuWriteHandlerPassthrough(t *testing.T) {
ram := make([]byte, 0xFFFF)
mmu := NewMMU(ram)

mmu.AddHandler(MemRegion{Start: 0x100, End: 0x200}, &testPassthroughHandler{})

mmu.Write8(0x103, 0x11)
mmu.Write8(0x201, 0x22)

assertAddrEquals(t, ram[0x103], 0x11)
assertAddrEquals(t, ram[0x201], 0x22)
}

func TestMmuWriteHandlerBlock(t *testing.T) {
ram := make([]byte, 0xFFFF)
mmu := NewMMU(ram)

mmu.AddHandler(MemRegion{Start: 0x100, End: 0x200}, &testWriteBlockHandler{})

mmu.Write8(0x103, 0x11)
mmu.Write8(0x201, 0x22)

assertAddrEquals(t, ram[0x103], 0x00)
assertAddrEquals(t, ram[0x201], 0x22)
}

0 comments on commit fcb1947

Please sign in to comment.