diff --git a/gb/cpu.go b/gb/cpu.go index e375bf2..77c5342 100644 --- a/gb/cpu.go +++ b/gb/cpu.go @@ -43,7 +43,7 @@ func NewCPU(bus *Bus) *CPU { // NOTE(m): PC is usually set to 0x100 by the boot rom // TODO(m): SP is usually set programmatically by the cartridge code. // Remove this hardcoded value later! - cpu.Regs = Registers{PC: 0x100, SP: 0xDFFF} + cpu.Regs = Registers{SP: 0xDFFF} cpu.Stepping = true return &cpu @@ -63,6 +63,64 @@ func (cpu *CPU) Step() { case 0x00: // NOP + case 0x0E: + // LD C, n8 + val := cpu.Bus.Read(cpu.Regs.PC) + // emu_cycles(1); + cpu.Regs.C = val + cpu.Regs.PC++ + + case 0x10: + // STOP n8 + cpu.Halted = true + + case 0x11: + // LD DE, n16 + cpu.Regs.E = cpu.Bus.Read(cpu.Regs.PC) + // emu_cycles(1); + cpu.Regs.D = cpu.Bus.Read(cpu.Regs.PC + 1) + // emu_cycles(1); + cpu.Regs.PC += 2 + + case 0x12: + // LD [DE], A + // Get 16-bit address from DE + address := uint16(cpu.Regs.D)<<8 | uint16(cpu.Regs.E) + cpu.Bus.Write(address, cpu.Regs.A) + + case 0x1C: + // INC E + cpu.Regs.E++ + + // Set appropriate flags + if cpu.Regs.E == 0 { + cpu.SetFlag(Z) + } else { + cpu.ClearFlag(Z) + } + + cpu.ClearFlag(N) + + if (cpu.Regs.E & 0x0F) == 0 { + cpu.SetFlag(H) + } else { + cpu.ClearFlag(H) + } + + case 0x20: + // JR NZ, e8 + if cpu.IsFlagSet(Z) { + // Z is set, don't execute + // emu_cycles(2); + cpu.Regs.PC++ + } else { + // Z is not set, execute + // Jump relative to 8-bit signed offset + // emu_cycles(3); + offset := int8(cpu.Bus.Read(cpu.Regs.PC)) + cpu.Regs.PC = uint16(int(cpu.Regs.PC) + int(offset)) + } + case 0x21: // LD HL, n16 cpu.Regs.L = cpu.Bus.Read(cpu.Regs.PC) @@ -71,6 +129,19 @@ func (cpu *CPU) Step() { // emu_cycles(1); cpu.Regs.PC += 2 + case 0x2A: + // LD A, [HL+] or LD A, [HLI] + // Get 16-bit address from HL + address := uint16(cpu.Regs.H)<<8 | uint16(cpu.Regs.L) + // Read byte at address and assign value to A register + cpu.Regs.A = cpu.Bus.Read(address) + // emu_cycles(1); + // Increment HL + address++ + cpu.Regs.H = byte(address >> 8) + cpu.Regs.L = byte(address) + // emu_cycles(1); + case 0x31: // LD SP, n16 lo := cpu.Bus.Read(cpu.Regs.PC) @@ -99,6 +170,10 @@ func (cpu *CPU) Step() { cpu.ClearFlag(H) } + case 0x47: + // LD B, A + cpu.Regs.B = cpu.Regs.A + case 0xCB: // Prefix byte instructions cbOpcode := cpu.Bus.Read(cpu.Regs.PC) @@ -109,19 +184,19 @@ func (cpu *CPU) Step() { cpu.Regs.PC++ switch cbOpcode { - case 0x7E: - // BIT 7, [HL] - // Read byte pointed to by address HL - address := uint16(cpu.Regs.H)<<8 | uint16(cpu.Regs.L) - val := cpu.Bus.Read(address) + // case 0x7E: + // // BIT 7, [HL] + // // Read byte pointed to by address HL + // address := uint16(cpu.Regs.H)<<8 | uint16(cpu.Regs.L) + // val := cpu.Bus.Read(address) - // Check if bit 7 is set - if (val & 0x80) == 0 { - // Set zero flag if bit is not set - cpu.SetFlag(Z) - } - cpu.ClearFlag(N) - cpu.SetFlag(H) + // // Check if bit 7 is set + // if (val & 0x80) == 0 { + // // Set zero flag if bit is not set + // cpu.SetFlag(Z) + // } + // cpu.ClearFlag(N) + // cpu.SetFlag(H) default: fmt.Printf("\nINVALID INSTRUCTION! Unknown CB opcode: %02X\n", cbOpcode) @@ -136,6 +211,11 @@ func (cpu *CPU) Step() { // emu_cycles(1); cpu.Regs.PC = uint16(lo) | uint16(hi)<<8 + // case 0xC9: + // // RET + // // emu_cycles(4); + // cpu.Regs.PC = cpu.StackPop16() + case 0xCD: // CALL a16 cpu.StackPush16(cpu.Regs.PC + 2) diff --git a/gb/cpu_test.go b/gb/cpu_test.go index 770b84e..3f63fe0 100644 --- a/gb/cpu_test.go +++ b/gb/cpu_test.go @@ -61,7 +61,6 @@ func TestInstruction3C(t *testing.T) { // Should increment A register cpu := createCPU([]byte{0x3C, 0x00, 0x00}) - assert.Equal(t, byte(0x0), cpu.Regs.A) cpu.Step() assert.Equal(t, byte(0x01), cpu.Regs.A) @@ -76,7 +75,6 @@ func TestInstruction3C(t *testing.T) { cpu = createCPU([]byte{0x3C, 0x00, 0x00}) cpu.Regs.A = 0xFF - assert.False(t, cpu.IsFlagSet(Z)) cpu.Step() assert.True(t, cpu.IsFlagSet(Z)) @@ -91,7 +89,6 @@ func TestInstruction3C(t *testing.T) { cpu = createCPU([]byte{0x3C, 0x00, 0x00}) cpu.Regs.A = 0x0F - assert.False(t, cpu.IsFlagSet(H)) cpu.Step() assert.True(t, cpu.IsFlagSet(H)) @@ -168,21 +165,167 @@ func TestInstruction21(t *testing.T) { assert.Equal(t, uint16(0x3), cpu.Regs.PC) } -func TestInstructionCB7E(t *testing.T) { - cpu := createCPU([]byte{0xCB, 0x7E, 0x00, 0x00}) +// func TestInstructionCB7E(t *testing.T) { +// cpu := createCPU([]byte{0xCB, 0x7E, 0x00, 0x00}) - cpu.Regs.H = 0xFF - cpu.Regs.L = 0x40 - err := cpu.Bus.Write(0xFF40, 0xFF) - assert.Equal(t, err, nil) +// cpu.Regs.H = 0xFF +// cpu.Regs.L = 0x40 +// err := cpu.Bus.Write(0xFF40, 0xFF) +// assert.Equal(t, err, nil) - // FIXME(m): This needs bus access to IO, in particular the LCD - // at 0xFF40. +// // FIXME(m): This needs bus access to IO, in particular the LCD +// // at 0xFF40. - // assert.Equal(t, 0xFF, cpu.Bus.Read(0xFF40)) +// // assert.Equal(t, 0xFF, cpu.Bus.Read(0xFF40)) - // cpu.Step() +// // cpu.Step() - // // Should set the zero flag - // assert.True(t, cpu.IsFlagSet(Z)) +// // // Should set the zero flag +// // assert.True(t, cpu.IsFlagSet(Z)) +// } + +func TestInstruction47(t *testing.T) { + cpu := createCPU([]byte{0x47, 0x00, 0x00}) + + cpu.Regs.A = 0xDE + + cpu.Step() + + // Should load value in register A into register B + assert.Equal(t, byte(0xDE), cpu.Regs.B) +} + +func TestInstruction11(t *testing.T) { + cpu := createCPU([]byte{0x11, 0x34, 0x12}) + + cpu.Step() + + // Should load the 16-bit immediate value into the combined DE register + assert.Equal(t, byte(0x12), cpu.Regs.D) + assert.Equal(t, byte(0x34), cpu.Regs.E) + + // Should increase the stack pointer + assert.Equal(t, uint16(0x3), cpu.Regs.PC) +} + +func TestInstruction0E(t *testing.T) { + cpu := createCPU([]byte{0x0E, 0xDE, 0x00}) + + cpu.Step() + + // Should load the 8-bit immediate value into register C + assert.Equal(t, byte(0xDE), cpu.Regs.C) + + // Should increase the stack pointer + assert.Equal(t, uint16(0x2), cpu.Regs.PC) +} + +func TestInstruction2A(t *testing.T) { + cpu := createCPU([]byte{0x2A, 0x00, 0x00}) + cpu.Regs.H = 0xD1 + cpu.Regs.L = 0x23 + cpu.Bus.Write(0xD123, 0xDE) + + cpu.Step() + + // Should load the byte pointed to by HL into register A + assert.Equal(t, byte(0xDE), cpu.Regs.A) + + // Should increment HL + hl := uint16(cpu.Regs.H)<<8 | uint16(cpu.Regs.L) + assert.Equal(t, uint16(0xD124), hl) + + // Should increase the stack pointer + assert.Equal(t, uint16(0x1), cpu.Regs.PC) +} + +func TestInstruction12(t *testing.T) { + cpu := createCPU([]byte{0x12, 0x00, 0x00}) + cpu.Regs.D = 0xD1 + cpu.Regs.E = 0x23 + cpu.Regs.A = 0xDE + + cpu.Step() + + // Should copy the value in register A into the byte pointed to by DE + address := uint16(cpu.Regs.D)<<8 | uint16(cpu.Regs.E) + assert.Equal(t, byte(0xDE), cpu.Bus.Read(address)) + + // Should increase the stack pointer + assert.Equal(t, uint16(0x1), cpu.Regs.PC) +} + +func TestInstruction1C(t *testing.T) { + // Should increment E register + cpu := createCPU([]byte{0x1C, 0x00, 0x00}) + + cpu.Step() + assert.Equal(t, byte(0x01), cpu.Regs.E) + + // Should clear Zero Flag + cpu = createCPU([]byte{0x1C, 0x00, 0x00}) + + cpu.SetFlag(Z) + cpu.Step() + assert.False(t, cpu.IsFlagSet(Z)) + + // Should set Zero Flag + cpu = createCPU([]byte{0x1C, 0x00, 0x00}) + cpu.Regs.E = 0xFF + + cpu.Step() + assert.True(t, cpu.IsFlagSet(Z)) + + // Should clear Subtract Flag + cpu = createCPU([]byte{0x1C, 0x00, 0x00}) + + cpu.SetFlag(N) + cpu.Step() + assert.False(t, cpu.IsFlagSet(N)) + + // Should set Half Carry Flag if we overflow from bit 3 + cpu = createCPU([]byte{0x1C, 0x00, 0x00}) + cpu.Regs.E = 0x0F + + cpu.Step() + assert.True(t, cpu.IsFlagSet(H)) + + // Should clear Half Carry Flag if we don't overflow from bit 3 + cpu = createCPU([]byte{0x1C, 0x00, 0x00}) + + cpu.SetFlag(H) + cpu.Step() + assert.False(t, cpu.IsFlagSet(H)) +} + +func TestInstruction20(t *testing.T) { + // Should not jump if Z is set + cpu := createCPU([]byte{0x20, 0xFB, 0x00}) + cpu.SetFlag(Z) + + cpu.Step() + + assert.Equal(t, uint16(0x02), cpu.Regs.PC) + + // Should jump to positive offset if Z is not set + cpu = createCPU([]byte{0x20, 0x0A, 0x00}) + + cpu.Step() + + assert.Equal(t, uint16(0x0B), cpu.Regs.PC) + + // Should jump to negative offset if Z is not set + cpu = createCPU([]byte{0x20, 0xFB, 0x00}) + + cpu.Step() + + assert.Equal(t, uint16(0xFFFC), cpu.Regs.PC) +} + +func TestInstruction10(t *testing.T) { + cpu := createCPU([]byte{0x10, 0x2A, 0x12}) + + cpu.Step() + + assert.True(t, cpu.Halted) } diff --git a/main.go b/main.go index 05f19b7..006bea9 100644 --- a/main.go +++ b/main.go @@ -29,5 +29,6 @@ func main() { running := true for running { console.CPU.Step() + running = !console.CPU.Halted } }