Add more instructions. Fix JR bug. Start implementation of IO R/W
This commit is contained in:
parent
7c494acc7e
commit
7678fda9e7
@ -39,7 +39,11 @@ func (bus *Bus) Read(address uint16) byte {
|
||||
}
|
||||
return value
|
||||
} else if address < 0xE000 {
|
||||
//WRAM (Working RAM)
|
||||
return bus.RAM.WRAMRead(address)
|
||||
} else if address < 0xFF80 {
|
||||
//IO Registers
|
||||
return IORead(address)
|
||||
} else {
|
||||
fmt.Printf("Reading from bus address %X not implemented!\n", address)
|
||||
return 0
|
||||
@ -62,8 +66,13 @@ func (bus *Bus) Write(address uint16, value byte) error {
|
||||
}
|
||||
return nil
|
||||
} else if address < 0xE000 {
|
||||
//WRAM (Working RAM)
|
||||
bus.RAM.WRAMWrite(address, value)
|
||||
return nil
|
||||
} else if address < 0xFF80 {
|
||||
//IO Registers
|
||||
IOWrite(address, value)
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Writing to bus address %X not implemented!", address)
|
||||
}
|
||||
|
||||
72
gb/cpu.go
72
gb/cpu.go
@ -53,8 +53,9 @@ func (cpu *CPU) Step() {
|
||||
if !cpu.Halted {
|
||||
opcode := cpu.Bus.Read(cpu.Regs.PC)
|
||||
|
||||
fmt.Printf("%04X: (%02X %02X %02X) A: %02X B: %02X C: %02X\n", cpu.Regs.PC,
|
||||
opcode, cpu.Bus.Read(cpu.Regs.PC+1), cpu.Bus.Read(cpu.Regs.PC+2), cpu.Regs.A, cpu.Regs.B, cpu.Regs.C)
|
||||
fmt.Printf("%04X: (%02X %02X %02X) A: %02X B: %02X C: %02X D: %02X E: %02X H: %02X L: %02X\n", cpu.Regs.PC,
|
||||
opcode, cpu.Bus.Read(cpu.Regs.PC+1), cpu.Bus.Read(cpu.Regs.PC+2), cpu.Regs.A, cpu.Regs.B, cpu.Regs.C,
|
||||
cpu.Regs.D, cpu.Regs.E, cpu.Regs.H, cpu.Regs.L)
|
||||
|
||||
cpu.Regs.PC++
|
||||
|
||||
@ -63,6 +64,25 @@ func (cpu *CPU) Step() {
|
||||
case 0x00:
|
||||
// NOP
|
||||
|
||||
case 0x0D:
|
||||
// DEC C
|
||||
cpu.Regs.C--
|
||||
|
||||
// Set appropriate flags
|
||||
if cpu.Regs.C == 0 {
|
||||
cpu.SetFlag(Z)
|
||||
} else {
|
||||
cpu.ClearFlag(Z)
|
||||
}
|
||||
|
||||
cpu.SetFlag(N)
|
||||
|
||||
if (cpu.Regs.C & 0x0F) == 0x0F {
|
||||
cpu.SetFlag(H)
|
||||
} else {
|
||||
cpu.ClearFlag(H)
|
||||
}
|
||||
|
||||
case 0x0E:
|
||||
// LD C, n8
|
||||
val := cpu.Bus.Read(cpu.Regs.PC)
|
||||
@ -88,6 +108,25 @@ func (cpu *CPU) Step() {
|
||||
address := uint16(cpu.Regs.D)<<8 | uint16(cpu.Regs.E)
|
||||
cpu.Bus.Write(address, cpu.Regs.A)
|
||||
|
||||
case 0x14:
|
||||
// INC D
|
||||
cpu.Regs.D++
|
||||
|
||||
// Set appropriate flags
|
||||
if cpu.Regs.D == 0 {
|
||||
cpu.SetFlag(Z)
|
||||
} else {
|
||||
cpu.ClearFlag(Z)
|
||||
}
|
||||
|
||||
cpu.ClearFlag(N)
|
||||
|
||||
if (cpu.Regs.D & 0x0F) == 0 {
|
||||
cpu.SetFlag(H)
|
||||
} else {
|
||||
cpu.ClearFlag(H)
|
||||
}
|
||||
|
||||
case 0x1C:
|
||||
// INC E
|
||||
cpu.Regs.E++
|
||||
@ -118,6 +157,7 @@ func (cpu *CPU) Step() {
|
||||
// Jump relative to 8-bit signed offset
|
||||
// emu_cycles(3);
|
||||
offset := int8(cpu.Bus.Read(cpu.Regs.PC))
|
||||
cpu.Regs.PC++
|
||||
cpu.Regs.PC = uint16(int(cpu.Regs.PC) + int(offset))
|
||||
}
|
||||
|
||||
@ -170,10 +210,21 @@ func (cpu *CPU) Step() {
|
||||
cpu.ClearFlag(H)
|
||||
}
|
||||
|
||||
case 0x3E:
|
||||
// LD A, n8
|
||||
val := cpu.Bus.Read(cpu.Regs.PC)
|
||||
// emu_cycles(1);
|
||||
cpu.Regs.A = val
|
||||
cpu.Regs.PC++
|
||||
|
||||
case 0x47:
|
||||
// LD B, A
|
||||
cpu.Regs.B = cpu.Regs.A
|
||||
|
||||
case 0x78:
|
||||
// LD A, B
|
||||
cpu.Regs.A = cpu.Regs.B
|
||||
|
||||
case 0xCB:
|
||||
// Prefix byte instructions
|
||||
cbOpcode := cpu.Bus.Read(cpu.Regs.PC)
|
||||
@ -226,11 +277,28 @@ func (cpu *CPU) Step() {
|
||||
// emu_cycles(1);
|
||||
cpu.Regs.PC = uint16(lo) | uint16(hi)<<8
|
||||
|
||||
case 0xE0:
|
||||
// LDH [a8], A
|
||||
offset := cpu.Bus.Read(cpu.Regs.PC)
|
||||
address := 0xFF00 | uint16(offset)
|
||||
cpu.Bus.Write(address, cpu.Regs.A)
|
||||
cpu.Regs.PC++
|
||||
|
||||
case 0xE9:
|
||||
// JP HL
|
||||
val := uint16(cpu.Regs.H)<<8 | uint16(cpu.Regs.L)
|
||||
cpu.Regs.PC = val
|
||||
|
||||
case 0xEA:
|
||||
// LD [a16], A
|
||||
lo := cpu.Bus.Read(cpu.Regs.PC)
|
||||
cpu.Regs.PC++
|
||||
hi := cpu.Bus.Read(cpu.Regs.PC)
|
||||
cpu.Regs.PC++
|
||||
|
||||
address := uint16(lo) | uint16(hi)<<8
|
||||
cpu.Bus.Write(address, cpu.Regs.A)
|
||||
|
||||
case 0xF3:
|
||||
// DI
|
||||
cpu.InterruptMasterEnable = false
|
||||
|
||||
149
gb/cpu_test.go
149
gb/cpu_test.go
@ -193,6 +193,9 @@ func TestInstruction47(t *testing.T) {
|
||||
|
||||
// Should load value in register A into register B
|
||||
assert.Equal(t, byte(0xDE), cpu.Regs.B)
|
||||
|
||||
// Should increase the stack pointer
|
||||
assert.Equal(t, uint16(0x1), cpu.Regs.PC)
|
||||
}
|
||||
|
||||
func TestInstruction11(t *testing.T) {
|
||||
@ -312,14 +315,14 @@ func TestInstruction20(t *testing.T) {
|
||||
|
||||
cpu.Step()
|
||||
|
||||
assert.Equal(t, uint16(0x0B), cpu.Regs.PC)
|
||||
assert.Equal(t, uint16(0x0C), 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)
|
||||
assert.Equal(t, uint16(0xFFFD), cpu.Regs.PC)
|
||||
}
|
||||
|
||||
func TestInstruction10(t *testing.T) {
|
||||
@ -329,3 +332,145 @@ func TestInstruction10(t *testing.T) {
|
||||
|
||||
assert.True(t, cpu.Halted)
|
||||
}
|
||||
|
||||
func TestInstruction14(t *testing.T) {
|
||||
// Should increment D register
|
||||
cpu := createCPU([]byte{0x14, 0x00, 0x00})
|
||||
|
||||
cpu.Step()
|
||||
assert.Equal(t, byte(0x01), cpu.Regs.D)
|
||||
|
||||
// Should clear Zero Flag
|
||||
cpu = createCPU([]byte{0x14, 0x00, 0x00})
|
||||
|
||||
cpu.SetFlag(Z)
|
||||
cpu.Step()
|
||||
assert.False(t, cpu.IsFlagSet(Z))
|
||||
|
||||
// Should set Zero Flag
|
||||
cpu = createCPU([]byte{0x14, 0x00, 0x00})
|
||||
cpu.Regs.D = 0xFF
|
||||
|
||||
cpu.Step()
|
||||
assert.True(t, cpu.IsFlagSet(Z))
|
||||
|
||||
// Should clear Subtract Flag
|
||||
cpu = createCPU([]byte{0x14, 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{0x14, 0x00, 0x00})
|
||||
cpu.Regs.D = 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{0x14, 0x00, 0x00})
|
||||
|
||||
cpu.SetFlag(H)
|
||||
cpu.Step()
|
||||
assert.False(t, cpu.IsFlagSet(H))
|
||||
}
|
||||
|
||||
func TestInstruction0D(t *testing.T) {
|
||||
// Should decrement C register
|
||||
cpu := createCPU([]byte{0x0D, 0x00, 0x00})
|
||||
cpu.Regs.C = 0x01
|
||||
|
||||
cpu.Step()
|
||||
assert.Equal(t, byte(0x00), cpu.Regs.C)
|
||||
|
||||
// Should clear Zero Flag
|
||||
cpu = createCPU([]byte{0x0D, 0x00, 0x00})
|
||||
|
||||
cpu.SetFlag(Z)
|
||||
cpu.Step()
|
||||
assert.False(t, cpu.IsFlagSet(Z))
|
||||
|
||||
// Should set Zero Flag
|
||||
cpu = createCPU([]byte{0x0D, 0x00, 0x00})
|
||||
cpu.Regs.C = 0x01
|
||||
|
||||
cpu.Step()
|
||||
assert.True(t, cpu.IsFlagSet(Z))
|
||||
|
||||
// Should set Subtract Flag
|
||||
cpu = createCPU([]byte{0x0D, 0x00, 0x00})
|
||||
|
||||
cpu.SetFlag(N)
|
||||
cpu.Step()
|
||||
assert.True(t, cpu.IsFlagSet(N))
|
||||
|
||||
// Should set Half Carry Flag if we overflow from bit 3
|
||||
cpu = createCPU([]byte{0x0D, 0x00, 0x00})
|
||||
cpu.Regs.C = 0x10
|
||||
|
||||
cpu.Step()
|
||||
assert.True(t, cpu.IsFlagSet(H))
|
||||
|
||||
// Should clear Half Carry Flag if we don't overflow from bit 3
|
||||
cpu = createCPU([]byte{0x0D, 0x00, 0x00})
|
||||
cpu.Regs.C = 0x01
|
||||
|
||||
cpu.SetFlag(H)
|
||||
cpu.Step()
|
||||
assert.False(t, cpu.IsFlagSet(H))
|
||||
}
|
||||
|
||||
func TestInstruction78(t *testing.T) {
|
||||
cpu := createCPU([]byte{0x78, 0x00, 0x00})
|
||||
|
||||
cpu.Regs.B = 0xDE
|
||||
|
||||
cpu.Step()
|
||||
|
||||
// Should load value in register B into register A
|
||||
assert.Equal(t, byte(0xDE), cpu.Regs.A)
|
||||
|
||||
// Should increase the stack pointer
|
||||
assert.Equal(t, uint16(0x1), cpu.Regs.PC)
|
||||
}
|
||||
|
||||
func TestInstructionEA(t *testing.T) {
|
||||
cpu := createCPU([]byte{0xEA, 0x23, 0xD1})
|
||||
cpu.Regs.A = 0x42
|
||||
|
||||
cpu.Step()
|
||||
|
||||
// Should load byte in register A into the memory location pointed to by the 16-bit immediate value
|
||||
val := cpu.Bus.Read(0xD123)
|
||||
assert.Equal(t, byte(0x42), val)
|
||||
|
||||
// Should increase the stack pointer
|
||||
assert.Equal(t, uint16(0x3), cpu.Regs.PC)
|
||||
}
|
||||
|
||||
func TestInstruction3E(t *testing.T) {
|
||||
cpu := createCPU([]byte{0x3E, 0xDE, 0x00})
|
||||
|
||||
cpu.Step()
|
||||
|
||||
// Should load the 8-bit immediate value into register A
|
||||
assert.Equal(t, byte(0xDE), cpu.Regs.A)
|
||||
|
||||
// Should increase the stack pointer
|
||||
assert.Equal(t, uint16(0x2), cpu.Regs.PC)
|
||||
}
|
||||
|
||||
func TestInstructionE0(t *testing.T) {
|
||||
cpu := createCPU([]byte{0xE0, 0x07, 0x00})
|
||||
cpu.Regs.A = 0x42
|
||||
|
||||
cpu.Step()
|
||||
|
||||
// Should copy the value in register A into the byte at address 0xFF00 + 0x07
|
||||
val := cpu.Bus.Read(0xFF07)
|
||||
assert.Equal(t, byte(0x42), val)
|
||||
|
||||
// Should increase the stack pointer
|
||||
assert.Equal(t, uint16(0x2), cpu.Regs.PC)
|
||||
}
|
||||
|
||||
46
gb/io.go
Normal file
46
gb/io.go
Normal file
@ -0,0 +1,46 @@
|
||||
package gb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var SerialData [2]byte
|
||||
|
||||
func IORead(address uint16) byte {
|
||||
if address == 0xFF01 {
|
||||
return SerialData[0]
|
||||
}
|
||||
|
||||
if address == 0xFF02 {
|
||||
return SerialData[1]
|
||||
}
|
||||
|
||||
if (address >= 0xFF04) && (address <= 0xFF07) {
|
||||
fmt.Printf("Reading from IO: invalid address %X. Timer not yet implemented!", address)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Reading from IO: invalid address %X", address)
|
||||
os.Exit(1)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func IOWrite(address uint16, value byte) {
|
||||
if address == 0xFF01 {
|
||||
SerialData[0] = value
|
||||
}
|
||||
|
||||
if address == 0xFF02 {
|
||||
SerialData[1] = value
|
||||
}
|
||||
|
||||
if (address >= 0xFF04) && (address <= 0xFF07) {
|
||||
fmt.Printf("Writing to IO: invalid address %X. Timer not yet implemented!", address)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Writing to IO: invalid address %X", address)
|
||||
os.Exit(1)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user