diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..ca55ce9 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,13 @@ +kind: pipeline +type: docker +name: default + +steps: +- name : test + image: golang:latest + commands: + - go test +- name : build + image: golang:latest + commands + - go build \ No newline at end of file diff --git a/pkg/chip8/chip8.go b/pkg/chip8/chip8.go new file mode 100644 index 0000000..cf9b546 --- /dev/null +++ b/pkg/chip8/chip8.go @@ -0,0 +1,116 @@ +package chip8 + +import "fmt" + +const graphicsBufferSize = 64 * 32 + +type Chip8 struct { + address_register uint16 + beep_timer uint16 + draw_required bool + delay_timer uint16 + graphics [graphicsBufferSize]uint8 + keys [16]uint8 + memory [4096]uint8 + opcode uint16 + pc uint16 + registers [16]uint8 // an array - has a fixed length + stack []uint16 // a slice - basically a c++ vector +} + +// we can't have const arrays in go +// This is a good a solution as any +func getLetters() []uint8 { + return []uint8{ + 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 + 0x20, 0x60, 0x20, 0x20, 0x70, // 1 + 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 + 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3 + 0x90, 0x90, 0xF0, 0x10, 0x10, // 4 + 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5 + 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6 + 0xF0, 0x10, 0x20, 0x40, 0x40, // 7 + 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8 + 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9 + 0xF0, 0x90, 0xF0, 0x90, 0x90, // A + 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B + 0xF0, 0x80, 0x80, 0x80, 0xF0, // C + 0xE0, 0x90, 0x90, 0x90, 0xE0, // D + 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E + 0xF0, 0x80, 0xF0, 0x80, 0x80} // F +} + +func NewCHIP8(prog []uint8) *Chip8 { + cpu := Chip8{pc: 0x200} + memory_slice := cpu.memory[:80] + copy(memory_slice, getLetters()) + memory_slice = cpu.memory[200:] + copy(memory_slice, prog) + + // Do some extra checking to ensure the right + // Stuff is copied + + return &cpu +} + +func (cpu *Chip8) GetGraphicsBuffer() [graphicsBufferSize]uint8 { + return cpu.graphics +} + +func (cpu *Chip8) clearDisplay() { + // fuck it the gc can do the hard work for us + cpu.graphics = [64 * 32]uint8{} + cpu.draw_required = true +} + +// what if there's nothing in the stack? +// return something sensible +func (cpu *Chip8) leaveFunction() { + cpu.pc = cpu.stack[len(cpu.stack)-1] + cpu.stack[len(cpu.stack)-1] = 0 + cpu.stack = cpu.stack[:len(cpu.stack)-1] +} + +func (cpu *Chip8) goTo() { + cpu.pc = cpu.opcode & 0x0FFF +} + +func (cpu *Chip8) callSubroutine() { + cpu.stack = append(cpu.stack, cpu.pc) + cpu.pc = cpu.opcode & 0x0FFF +} + +func (cpu *Chip8) skipIfRegisterEqual() { + r := (cpu.opcode) >> 8 & 0x0F + if cpu.registers[r] == uint8(cpu.opcode&0xFF) { + cpu.pc += 2 + } +} + +func (cpu *Chip8) skipIfRegisterNotEqual() { + r := (cpu.opcode) >> 8 & 0x0F + if cpu.registers[r] != uint8(cpu.opcode&0xFF) { + cpu.pc += 2 + } +} + +func (cpu *Chip8) skipIfRegistersEqual() { + x, y := (cpu.opcode>>8)&0x0F, (cpu.opcode>>4)&0x0F + if cpu.registers[x] == cpu.registers[y] { + cpu.pc += 2 + } +} + +func (cpu *Chip8) setRegisterToNN() { + r := (cpu.opcode >> 8) & 0x0F + cpu.registers[r] = uint8(cpu.opcode & 0xFF) +} + +func (cpu *Chip8) RegisterPlusEqualsNN() {} // QUESTION HERE - WHAT DO IF IT WOULD WRAP ROUND? + +func main() { + fmt.Printf("Hello world!\n") + prog := []uint8{1, 2, 3, 4} + new_cpu := NewCHIP8(prog) + fmt.Printf("%d\n", new_cpu.opcode) +} diff --git a/pkg/chip8/chip8_test.go b/pkg/chip8/chip8_test.go new file mode 100644 index 0000000..ea377aa --- /dev/null +++ b/pkg/chip8/chip8_test.go @@ -0,0 +1,56 @@ +package chip8 + +import ( + "testing" +) + +func slicesEqual(x, y []uint8) bool { + if len(x) != len(y) { + return false + } + for i, xi := range x { + if xi != y[i] { + return false + } + } + return true +} + +// This test is kinda shit, there's so much stuff we ain't testing +// Maybe fix +func TestCreateCPU(t *testing.T) { + prog := []uint8{1, 2, 3, 4} + new_cpu := NewCHIP8(prog) + if !slicesEqual(new_cpu.memory[200:204], prog) { + t.Errorf("CPU not initalized properly") + } +} + +func TestClearDisplay(t *testing.T) { + cpu := Chip8{} + for i := range cpu.graphics { + cpu.graphics[i] = uint8(i % 255) + } + cpu.clearDisplay() + graphics_array := cpu.GetGraphicsBuffer() + graphics_slice := graphics_array[:] + empty_slice := make([]uint8, len(cpu.graphics)) + if !slicesEqual(graphics_slice, empty_slice) { + t.Errorf("Graphics buffer not cleared properly") + } +} + +func TestLeaveFunction(t *testing.T) { + cpu := Chip8{pc: 50} + cpu.stack = append(cpu.stack, 1, 2, 3, 4, 5) + cpu.leaveFunction() + if cpu.pc != 4 && len(cpu.stack) != 4 { + t.Errorf("TestLeaveFunction not in expected state") + } +} + +func TestPasses(t *testing.T) { + if 1 != 1 { + t.Errorf("We should never get this!") + } +}