Implement ADC, including Carry, Overflow, Decimal

This commit is contained in:
Ariejan de Vroom 2014-08-13 22:42:26 +02:00
parent c3ffb2e87b
commit 9c8f47abbc
7 changed files with 359 additions and 6 deletions

View File

@ -15,7 +15,7 @@ const (
relative
zeropage
zeropageX
zoerpageY
zeropageY
)
var addressingNames = [...]string{

46
cpu.go
View File

@ -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?"))
}
}

57
cpu_instructions.go Normal file
View File

@ -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
}

55
cpu_status.go Normal file
View File

@ -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)
}

View File

@ -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)
}
//

View File

@ -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}

View File

@ -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},
}