Implement more CPU instructions
This commit is contained in:
parent
772ff893af
commit
7c494acc7e
106
gb/cpu.go
106
gb/cpu.go
@ -43,7 +43,7 @@ func NewCPU(bus *Bus) *CPU {
|
|||||||
// NOTE(m): PC is usually set to 0x100 by the boot rom
|
// NOTE(m): PC is usually set to 0x100 by the boot rom
|
||||||
// TODO(m): SP is usually set programmatically by the cartridge code.
|
// TODO(m): SP is usually set programmatically by the cartridge code.
|
||||||
// Remove this hardcoded value later!
|
// Remove this hardcoded value later!
|
||||||
cpu.Regs = Registers{PC: 0x100, SP: 0xDFFF}
|
cpu.Regs = Registers{SP: 0xDFFF}
|
||||||
cpu.Stepping = true
|
cpu.Stepping = true
|
||||||
|
|
||||||
return &cpu
|
return &cpu
|
||||||
@ -63,6 +63,64 @@ func (cpu *CPU) Step() {
|
|||||||
case 0x00:
|
case 0x00:
|
||||||
// NOP
|
// 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:
|
case 0x21:
|
||||||
// LD HL, n16
|
// LD HL, n16
|
||||||
cpu.Regs.L = cpu.Bus.Read(cpu.Regs.PC)
|
cpu.Regs.L = cpu.Bus.Read(cpu.Regs.PC)
|
||||||
@ -71,6 +129,19 @@ func (cpu *CPU) Step() {
|
|||||||
// emu_cycles(1);
|
// emu_cycles(1);
|
||||||
cpu.Regs.PC += 2
|
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:
|
case 0x31:
|
||||||
// LD SP, n16
|
// LD SP, n16
|
||||||
lo := cpu.Bus.Read(cpu.Regs.PC)
|
lo := cpu.Bus.Read(cpu.Regs.PC)
|
||||||
@ -99,6 +170,10 @@ func (cpu *CPU) Step() {
|
|||||||
cpu.ClearFlag(H)
|
cpu.ClearFlag(H)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 0x47:
|
||||||
|
// LD B, A
|
||||||
|
cpu.Regs.B = cpu.Regs.A
|
||||||
|
|
||||||
case 0xCB:
|
case 0xCB:
|
||||||
// Prefix byte instructions
|
// Prefix byte instructions
|
||||||
cbOpcode := cpu.Bus.Read(cpu.Regs.PC)
|
cbOpcode := cpu.Bus.Read(cpu.Regs.PC)
|
||||||
@ -109,19 +184,19 @@ func (cpu *CPU) Step() {
|
|||||||
cpu.Regs.PC++
|
cpu.Regs.PC++
|
||||||
|
|
||||||
switch cbOpcode {
|
switch cbOpcode {
|
||||||
case 0x7E:
|
// case 0x7E:
|
||||||
// BIT 7, [HL]
|
// // BIT 7, [HL]
|
||||||
// Read byte pointed to by address HL
|
// // Read byte pointed to by address HL
|
||||||
address := uint16(cpu.Regs.H)<<8 | uint16(cpu.Regs.L)
|
// address := uint16(cpu.Regs.H)<<8 | uint16(cpu.Regs.L)
|
||||||
val := cpu.Bus.Read(address)
|
// val := cpu.Bus.Read(address)
|
||||||
|
|
||||||
// Check if bit 7 is set
|
// // Check if bit 7 is set
|
||||||
if (val & 0x80) == 0 {
|
// if (val & 0x80) == 0 {
|
||||||
// Set zero flag if bit is not set
|
// // Set zero flag if bit is not set
|
||||||
cpu.SetFlag(Z)
|
// cpu.SetFlag(Z)
|
||||||
}
|
// }
|
||||||
cpu.ClearFlag(N)
|
// cpu.ClearFlag(N)
|
||||||
cpu.SetFlag(H)
|
// cpu.SetFlag(H)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fmt.Printf("\nINVALID INSTRUCTION! Unknown CB opcode: %02X\n", cbOpcode)
|
fmt.Printf("\nINVALID INSTRUCTION! Unknown CB opcode: %02X\n", cbOpcode)
|
||||||
@ -136,6 +211,11 @@ func (cpu *CPU) Step() {
|
|||||||
// emu_cycles(1);
|
// emu_cycles(1);
|
||||||
cpu.Regs.PC = uint16(lo) | uint16(hi)<<8
|
cpu.Regs.PC = uint16(lo) | uint16(hi)<<8
|
||||||
|
|
||||||
|
// case 0xC9:
|
||||||
|
// // RET
|
||||||
|
// // emu_cycles(4);
|
||||||
|
// cpu.Regs.PC = cpu.StackPop16()
|
||||||
|
|
||||||
case 0xCD:
|
case 0xCD:
|
||||||
// CALL a16
|
// CALL a16
|
||||||
cpu.StackPush16(cpu.Regs.PC + 2)
|
cpu.StackPush16(cpu.Regs.PC + 2)
|
||||||
|
|||||||
173
gb/cpu_test.go
173
gb/cpu_test.go
@ -61,7 +61,6 @@ func TestInstruction3C(t *testing.T) {
|
|||||||
// Should increment A register
|
// Should increment A register
|
||||||
cpu := createCPU([]byte{0x3C, 0x00, 0x00})
|
cpu := createCPU([]byte{0x3C, 0x00, 0x00})
|
||||||
|
|
||||||
assert.Equal(t, byte(0x0), cpu.Regs.A)
|
|
||||||
cpu.Step()
|
cpu.Step()
|
||||||
assert.Equal(t, byte(0x01), cpu.Regs.A)
|
assert.Equal(t, byte(0x01), cpu.Regs.A)
|
||||||
|
|
||||||
@ -76,7 +75,6 @@ func TestInstruction3C(t *testing.T) {
|
|||||||
cpu = createCPU([]byte{0x3C, 0x00, 0x00})
|
cpu = createCPU([]byte{0x3C, 0x00, 0x00})
|
||||||
cpu.Regs.A = 0xFF
|
cpu.Regs.A = 0xFF
|
||||||
|
|
||||||
assert.False(t, cpu.IsFlagSet(Z))
|
|
||||||
cpu.Step()
|
cpu.Step()
|
||||||
assert.True(t, cpu.IsFlagSet(Z))
|
assert.True(t, cpu.IsFlagSet(Z))
|
||||||
|
|
||||||
@ -91,7 +89,6 @@ func TestInstruction3C(t *testing.T) {
|
|||||||
cpu = createCPU([]byte{0x3C, 0x00, 0x00})
|
cpu = createCPU([]byte{0x3C, 0x00, 0x00})
|
||||||
cpu.Regs.A = 0x0F
|
cpu.Regs.A = 0x0F
|
||||||
|
|
||||||
assert.False(t, cpu.IsFlagSet(H))
|
|
||||||
cpu.Step()
|
cpu.Step()
|
||||||
assert.True(t, cpu.IsFlagSet(H))
|
assert.True(t, cpu.IsFlagSet(H))
|
||||||
|
|
||||||
@ -168,21 +165,167 @@ func TestInstruction21(t *testing.T) {
|
|||||||
assert.Equal(t, uint16(0x3), cpu.Regs.PC)
|
assert.Equal(t, uint16(0x3), cpu.Regs.PC)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInstructionCB7E(t *testing.T) {
|
// func TestInstructionCB7E(t *testing.T) {
|
||||||
cpu := createCPU([]byte{0xCB, 0x7E, 0x00, 0x00})
|
// cpu := createCPU([]byte{0xCB, 0x7E, 0x00, 0x00})
|
||||||
|
|
||||||
cpu.Regs.H = 0xFF
|
// cpu.Regs.H = 0xFF
|
||||||
cpu.Regs.L = 0x40
|
// cpu.Regs.L = 0x40
|
||||||
err := cpu.Bus.Write(0xFF40, 0xFF)
|
// err := cpu.Bus.Write(0xFF40, 0xFF)
|
||||||
assert.Equal(t, err, nil)
|
// assert.Equal(t, err, nil)
|
||||||
|
|
||||||
// FIXME(m): This needs bus access to IO, in particular the LCD
|
// // FIXME(m): This needs bus access to IO, in particular the LCD
|
||||||
// at 0xFF40.
|
// // 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
|
// // // Should set the zero flag
|
||||||
// assert.True(t, cpu.IsFlagSet(Z))
|
// // 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)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user