diff --git a/addressing-modes.go b/addressing-modes.go index a0e3c0a..d2ac78e 100644 --- a/addressing-modes.go +++ b/addressing-modes.go @@ -15,7 +15,7 @@ const ( relative zeropage zeropageX - zoerpageY + zeropageY ) var addressingNames = [...]string{ diff --git a/cpu.go b/cpu.go index c150de2..63a10a6 100644 --- a/cpu.go +++ b/cpu.go @@ -7,6 +7,10 @@ type Cpu struct { PC uint16 // 16-bit program counter P byte // Status Register + + A byte // Accumulator + X byte // X index register + Y byte // Y index register } const ( @@ -42,9 +46,49 @@ func (c *Cpu) LoadProgram(data []byte, location uint16) { func (c *Cpu) Step() { instruction := c.readNextInstruction() c.PC += uint16(instruction.Size) - fmt.Println(instruction) + // fmt.Println(instruction) c.execute(instruction) } func (c *Cpu) execute(instruction Instruction) { + switch instruction.opcodeId { + case adc: + c.ADC(instruction) + } +} + +func (c *Cpu) resolveOperand(in Instruction) uint8 { + switch in.addressingId { + case immediate: + return in.Op8 + default: + return c.bus.Read(c.memoryAddress(in)) + } +} + +func (c *Cpu) memoryAddress(in Instruction) uint16 { + switch in.addressingId { + case absolute: + return in.Op16 + case absoluteX: + return in.Op16 + uint16(c.X) + case absoluteY: + return in.Op16 + uint16(c.Y) + case indirect: + return c.bus.Read16(in.Op16) + case indirectX: + return c.bus.Read16(uint16(in.Op8 + c.X)) + case indirectY: + return c.bus.Read16(uint16(in.Op8)) + uint16(c.Y) + case relative: + panic("Relative addressing not yet implemented.") + case zeropage: + return uint16(in.Op8) + case zeropageX: + return uint16(in.Op8 + c.X) + case zeropageY: + return uint16(in.Op8 + c.Y) + default: + panic(fmt.Errorf("Unhandled addressing mode. Are you sure you are running a 6502 ROM?")) + } } diff --git a/cpu_instructions.go b/cpu_instructions.go new file mode 100644 index 0000000..b6808b5 --- /dev/null +++ b/cpu_instructions.go @@ -0,0 +1,57 @@ +package i6502 + +// Add Memory to Accumulator with Carry +func (c *Cpu) ADC(in Instruction) { + operand := c.resolveOperand(in) + carryIn := c.getStatusInt(sCarry) + + if c.getStatus(sDecimal) { + c.adcDecimal(c.A, operand, carryIn) + } else { + c.adcNormal(c.A, operand, carryIn) + } +} + +// Performs regular, 8-bit addition +func (c *Cpu) adcNormal(a uint8, b uint8, carryIn uint8) { + result16 := uint16(a) + uint16(b) + uint16(carryIn) + result := uint8(result16) + carryOut := (result16 & 0x100) != 0 + overflow := (a^result)&(b^result)&0x80 != 0 + + // Set the carry flag if we exceed 8-bits + c.setCarry(carryOut) + // Set the overflow bit + c.setOverflow(overflow) + // Store the resulting value (8-bits) + c.A = result + // Update sZero and sNegative + c.setArithmeticFlags(c.A) +} + +// Performs addition in decimal mode +func (c *Cpu) adcDecimal(a uint8, b uint8, carryIn uint8) { + var carryB uint8 = 0 + + low := (a & 0x0F) + (b & 0x0F) + carryIn + if (low & 0xFF) > 9 { + low += 6 + } + if low > 15 { + carryB = 1 + } + + high := (a >> 4) + (b >> 4) + carryB + if (high & 0xFF) > 9 { + high += 6 + } + + result := (low & 0x0F) | (high<<4)&0xF0 + + c.setCarry(high > 15) + c.setZero(result == 0) + c.setNegative(false) // BCD is never negative + c.setOverflow(false) // BCD never sets overflow + + c.A = result +} diff --git a/cpu_status.go b/cpu_status.go new file mode 100644 index 0000000..272307b --- /dev/null +++ b/cpu_status.go @@ -0,0 +1,55 @@ +package i6502 + +// Status Register bits +const ( + sCarry = iota + sZero + sInterrupt + sDecimal + sBreak + _ + sOverflow + sNegative +) + +func (c *Cpu) getStatusInt(bit uint8) uint8 { + return (c.P >> bit) & 1 +} + +func (c *Cpu) getStatus(bit uint8) bool { + return c.getStatusInt(bit) == 1 +} + +func (c *Cpu) setStatus(bit uint8, state bool) { + if state { + c.P |= 1 << bit + } else { + c.P &^= 1 << bit + } +} + +func (c *Cpu) setCarry(state bool) { + c.setStatus(sCarry, state) +} + +func (c *Cpu) setOverflow(state bool) { + c.setStatus(sOverflow, state) +} + +func (c *Cpu) setZero(state bool) { + c.setStatus(sZero, state) +} + +func (c *Cpu) setNegative(state bool) { + c.setStatus(sNegative, state) +} + +func (c *Cpu) setArithmeticFlags(value uint8) { + // Set sZero if value is 0 + c.setStatus(sZero, value == 0) + + // Set sNegative if the 8th bit is 1, we're dealing with + // uint8's internally, and using two's complement to identify + // negatives + c.setStatus(sNegative, (value>>7) == 1) +} diff --git a/cpu_test.go b/cpu_test.go index 29a3d43..8d462d8 100644 --- a/cpu_test.go +++ b/cpu_test.go @@ -64,9 +64,197 @@ func TestProgramLoading(t *testing.T) { assert.Equal(0x0300, cpu.PC) } +//// NOP + func TestNOP(t *testing.T) { cpu, _, _ := NewRamMachine() cpu.LoadProgram([]byte{0xEA}, 0x0300) cpu.Step() assert.Equal(t, 0x0301, cpu.PC) } + +//// ADC + +func TestADCImmediate(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x69, 0x53}, 0x0300) + cpu.A = 0x42 + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x95, cpu.A) + assert.False(t, cpu.getStatus(sCarry)) +} + +func TestADCWithCarry(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x69, 0x53}, 0x0300) + cpu.A = 0xC0 + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x13, cpu.A) + assert.True(t, cpu.getStatus(sCarry)) +} + +func TestADCWithCarryOver(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x69, 0x04}, 0x0300) + + cpu.setStatus(sCarry, true) + cpu.A = 0x05 + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x0A, cpu.A) + assert.False(t, cpu.getStatus(sCarry)) +} + +func TestADCWithOverflow(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x69, 0xD0}, 0x0300) + + cpu.A = 0x90 + + // 0x90 + 0xD0 = 0x160 + // 208 + 144 = 352 => unsigned carry + // -48 + -112 = 96 => signed overflow + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x60, cpu.A) + assert.True(t, cpu.getStatus(sCarry)) + assert.True(t, cpu.getStatus(sOverflow)) +} + +func TestADCZero(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x69, 0x00}, 0x0300) + cpu.A = 0x00 + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x00, cpu.A) + assert.True(t, cpu.getStatus(sZero)) +} + +func TestADCNegative(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x69, 0xF7}, 0x0300) + cpu.A = 0x00 + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0xF7, cpu.A) + assert.True(t, cpu.getStatus(sNegative)) +} + +func TestADCDecimal(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x69, 0x28}, 0x0300) + cpu.setStatus(sDecimal, true) + cpu.A = 0x19 + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x47, cpu.A) +} + +func TestADCZeropage(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x65, 0x53}, 0x0300) + cpu.A = 0x42 + cpu.bus.Write(0x53, 0x12) + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x54, cpu.A) +} + +func TestADCZeropageX(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x75, 0x53}, 0x0300) + cpu.A = 0x42 + cpu.X = 0x01 + cpu.bus.Write(0x54, 0x12) + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x54, cpu.A) +} + +func TestADCAbsolute(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x6D, 0x00, 0x80}, 0x0300) + cpu.A = 0x42 + cpu.bus.Write(0x8000, 0x12) + + cpu.Step() + + assert.Equal(t, 0x0303, cpu.PC) + assert.Equal(t, 0x54, cpu.A) +} + +func TestADCAbsoluteX(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x7D, 0x00, 0x80}, 0x0300) + cpu.A = 0x42 + cpu.X = 0x02 + cpu.bus.Write(0x8002, 0x12) + + cpu.Step() + + assert.Equal(t, 0x0303, cpu.PC) + assert.Equal(t, 0x54, cpu.A) +} + +func TestADCAbsoluteY(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x79, 0x00, 0x80}, 0x0300) + cpu.A = 0x42 + cpu.Y = 0x02 + cpu.bus.Write(0x8002, 0x12) + + cpu.Step() + + assert.Equal(t, 0x0303, cpu.PC) + assert.Equal(t, 0x54, cpu.A) +} + +func TestADCIndirectX(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x61, 0x80}, 0x0300) + cpu.A = 0x42 + cpu.X = 0x02 + cpu.bus.Write16(0x82, 0xC000) + cpu.bus.Write(0xC000, 0x12) + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x54, cpu.A) +} + +func TestADCIndirectY(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0x71, 0x80}, 0x0300) + cpu.A = 0x42 + cpu.Y = 0x02 + cpu.bus.Write16(0x80, 0xC000) + cpu.bus.Write(0xC002, 0x12) + + cpu.Step() + + assert.Equal(t, 0x0302, cpu.PC) + assert.Equal(t, 0x54, cpu.A) +} + +// diff --git a/instruction.go b/instruction.go index 52bdd0a..22877af 100644 --- a/instruction.go +++ b/instruction.go @@ -21,11 +21,11 @@ type Instruction struct { func (i Instruction) String() (output string) { switch i.Size { case 1: - output = fmt.Sprintf("~~ 0x%04X: 0x%02X - %s [%s] {%d}\n", i.Address, i.Opcode, instructionNames[i.opcodeId], addressingNames[i.addressingId], i.Cycles) + output = fmt.Sprintf("~~~ 0x%04X: 0x%02X - %s [%s] {%d}\n", i.Address, i.Opcode, instructionNames[i.opcodeId], addressingNames[i.addressingId], i.Cycles) case 2: - output = fmt.Sprintf("~~ 0x%04X: 0x%02X - %s 0x%02X [%s] {%d}\n", i.Address, i.Opcode, instructionNames[i.opcodeId], i.Op8, addressingNames[i.addressingId], i.Cycles) + output = fmt.Sprintf("~~~ 0x%04X: 0x%02X - %s 0x%02X [%s] {%d}\n", i.Address, i.Opcode, instructionNames[i.opcodeId], i.Op8, addressingNames[i.addressingId], i.Cycles) case 3: - output = fmt.Sprintf("~~ 0x%04X: 0x%02X - %s 0x%04X [%s] {%d}\n", i.Address, i.Opcode, instructionNames[i.opcodeId], i.Op16, addressingNames[i.addressingId], i.Cycles) + output = fmt.Sprintf("~~~ 0x%04X: 0x%02X - %s 0x%04X [%s] {%d}\n", i.Address, i.Opcode, instructionNames[i.opcodeId], i.Op16, addressingNames[i.addressingId], i.Cycles) } return @@ -37,7 +37,7 @@ func (c *Cpu) readNextInstruction() Instruction { optype, ok := opTypes[opcode] if !ok { - panic(fmt.Sprintf("Unknown or unimplemented opcde 0x%02X", opcode)) + panic(fmt.Sprintf("Unknown or unimplemented opcode 0x%02X", opcode)) } instruction := Instruction{OpType: optype} diff --git a/opcodes.go b/opcodes.go index 911d784..e9e9d72 100644 --- a/opcodes.go +++ b/opcodes.go @@ -143,4 +143,13 @@ type OpType struct { var opTypes = map[uint8]OpType{ 0xEA: OpType{0xEA, nop, implied, 1, 2}, + + 0x69: OpType{0x69, adc, immediate, 2, 2}, + 0x65: OpType{0x65, adc, zeropage, 2, 2}, + 0x75: OpType{0x75, adc, zeropageX, 2, 4}, + 0x6D: OpType{0x6D, adc, absolute, 3, 4}, + 0x7D: OpType{0x7D, adc, absoluteX, 3, 4}, + 0x79: OpType{0x79, adc, absoluteY, 3, 4}, + 0x61: OpType{0x61, adc, indirectX, 2, 6}, + 0x71: OpType{0x71, adc, indirectY, 2, 5}, }