Implement remaining opcodes; tests

This commit is contained in:
Ariejan de Vroom 2014-08-16 10:35:00 +02:00
parent f3f95dda39
commit 33ca177f90
9 changed files with 1632 additions and 51 deletions

107
cpu.go
View File

@ -16,6 +16,7 @@ type Cpu struct {
const (
ResetVector = 0xFFFC // 0xFFFC-FFFD
IrqVector = 0xFFFE // 0xFFFE-FFFF
)
// Create an new Cpu instance with the specified AddressBus
@ -23,6 +24,11 @@ func NewCpu(bus *AddressBus) (*Cpu, error) {
return &Cpu{bus: bus}, nil
}
func (c *Cpu) String() string {
str := ">>> CPU [ A ] [ X ] [ Y ] [ SP ] [ PC ] NVxBDIZC\n>>> 0x%02X 0x%02X 0x%02X 0x%02X 0x%04X %08b\n"
return fmt.Sprintf(str, c.A, c.X, c.Y, c.SP, c.PC, c.P)
}
func (c *Cpu) HasAddressBus() bool {
return c.bus != nil
}
@ -31,6 +37,27 @@ func (c *Cpu) HasAddressBus() bool {
func (c *Cpu) Reset() {
c.PC = c.bus.Read16(ResetVector)
c.P = 0x34
// Not specified, but let's clean up
c.A = 0x00
c.X = 0x00
c.Y = 0x00
c.SP = 0xFF
}
// Simulate the IRQ pin
func (c *Cpu) Interrupt() {
c.handleIrq(c.PC)
}
func (c *Cpu) handleIrq(PC uint16) {
c.stackPush(byte(PC >> 8))
c.stackPush(byte(PC))
c.stackPush(c.P)
c.setIrqDisable(true)
c.PC = c.bus.Read16(IrqVector)
}
// Load the specified program data at the given memory location
@ -45,6 +72,7 @@ func (c *Cpu) LoadProgram(data []byte, location uint16) {
// Execute the instruction pointed to by the Program Counter (PC)
func (c *Cpu) Step() {
// fmt.Println(c)
instruction := c.readNextInstruction()
c.PC += uint16(instruction.Size)
// fmt.Println(instruction)
@ -64,13 +92,13 @@ func (c *Cpu) execute(instruction Instruction) {
case sed:
c.setDecimal(true)
case sei:
c.setInterrupt(true)
c.setIrqDisable(true)
case clc:
c.setCarry(false)
case cld:
c.setDecimal(false)
case cli:
c.setInterrupt(false)
c.setIrqDisable(false)
case clv:
c.setOverflow(false)
case inx:
@ -114,16 +142,89 @@ func (c *Cpu) execute(instruction Instruction) {
case tsx:
c.setX(c.SP)
case txs:
c.setSP(c.X)
c.SP = c.X
case asl:
c.ASL(instruction)
case lsr:
c.LSR(instruction)
case rol:
c.ROL(instruction)
case ror:
c.ROR(instruction)
case cmp:
c.CMP(instruction)
case cpx:
c.CPX(instruction)
case cpy:
c.CPY(instruction)
case brk:
c.BRK()
case bcc:
if !c.getCarry() {
c.branch(instruction)
}
case bcs:
if c.getCarry() {
c.branch(instruction)
}
case bne:
if !c.getZero() {
c.branch(instruction)
}
case beq:
if c.getZero() {
c.branch(instruction)
}
case bpl:
if !c.getNegative() {
c.branch(instruction)
}
case bmi:
if c.getNegative() {
c.branch(instruction)
}
case bvc:
if !c.getOverflow() {
c.branch(instruction)
}
case bvs:
if c.getOverflow() {
c.branch(instruction)
}
case bit:
c.BIT(instruction)
case php:
c.stackPush(c.P | 0x30)
case plp:
c.setP(c.stackPop())
case pha:
c.stackPush(c.A)
case pla:
value := c.stackPop()
c.setA(value)
case jmp:
c.JMP(instruction)
case jsr:
c.JSR(instruction)
case rts:
c.PC = (uint16(c.stackPop()) | uint16(c.stackPop())<<8) + 1
case rti:
c.setP(c.stackPop())
c.PC = uint16(c.stackPop()) | uint16(c.stackPop())<<8
default:
panic(fmt.Errorf("Unimplemented instruction: %s", instruction))
}
}
func (c *Cpu) branch(in Instruction) {
relative := int8(in.Op8) // Signed!
if relative >= 0 {
c.PC += uint16(relative)
} else {
c.PC -= -uint16(relative)
}
}
func (c *Cpu) resolveOperand(in Instruction) uint8 {
switch in.addressingId {
case immediate:

View File

@ -17,6 +17,8 @@ func (c *Cpu) SBC(in Instruction) {
operand := c.resolveOperand(in)
carryIn := c.getCarryInt()
// fmt.Printf("SBC: A: 0x%02X V: 0x%02X C: %b D: %v\n", c.A, operand, carryIn, c.getDecimal())
if c.getDecimal() {
c.sbcDecimal(c.A, operand, carryIn)
} else {
@ -117,6 +119,83 @@ func (c *Cpu) LSR(in Instruction) {
}
}
func (c *Cpu) ROL(in Instruction) {
carry := c.getCarryInt()
switch in.addressingId {
case accumulator:
c.setCarry((c.A & 0x80) != 0)
c.A = c.A<<1 | carry
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.bus.Read(address)
c.setCarry((value & 0x80) != 0)
value = value<<1 | carry
c.bus.Write(address, value)
c.setArithmeticFlags(value)
}
}
func (c *Cpu) ROR(in Instruction) {
carry := c.getCarryInt()
switch in.addressingId {
case accumulator:
c.setCarry(c.A&0x01 == 1)
c.A = c.A>>1 | carry<<7
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.bus.Read(address)
c.setCarry(value&0x01 == 1)
value = value>>1 | carry<<7
c.bus.Write(address, value)
c.setArithmeticFlags(value)
}
}
func (c *Cpu) CMP(in Instruction) {
value := c.resolveOperand(in)
// fmt.Printf("CMP A: 0x%02X V: 0x%02X\n", c.A, value)
c.setCarry(c.A >= value)
c.setArithmeticFlags(c.A - value)
}
func (c *Cpu) CPX(in Instruction) {
value := c.resolveOperand(in)
c.setCarry(c.X >= value)
c.setArithmeticFlags(c.X - value)
}
func (c *Cpu) CPY(in Instruction) {
value := c.resolveOperand(in)
c.setCarry(c.Y >= value)
c.setArithmeticFlags(c.Y - value)
}
func (c *Cpu) BRK() {
c.setBreak(true)
c.handleIrq(c.PC + 1)
}
func (c *Cpu) BIT(in Instruction) {
value := c.resolveOperand(in)
c.setNegative((value & 0x80) != 0)
c.setOverflow((value & 0x40) != 0)
c.setZero((c.A & value) == 0)
}
func (c *Cpu) JMP(in Instruction) {
c.PC = c.memoryAddress(in)
}
func (c *Cpu) JSR(in Instruction) {
c.stackPush(byte((c.PC - 1) >> 8))
c.stackPush(byte(c.PC - 1))
c.PC = c.memoryAddress(in)
}
// Performs regular, 8-bit addition
func (c *Cpu) adcNormal(a uint8, b uint8, carryIn uint8) {
result16 := uint16(a) + uint16(b) + uint16(carryIn)
@ -162,7 +241,11 @@ func (c *Cpu) adcDecimal(a uint8, b uint8, carryIn uint8) {
func (c *Cpu) sbcDecimal(a uint8, b uint8, carryIn uint8) {
var carryB uint8 = 0
carryIn = (carryIn + 1) % 1
if carryIn == 0 {
carryIn = 1
} else {
carryIn = 0
}
low := (a & 0x0F) - (b & 0x0F) - carryIn
if (low & 0x10) != 0 {

19
cpu_stack.go Normal file
View File

@ -0,0 +1,19 @@
package i6502
const (
StackBase = 0x0100
)
func (c *Cpu) stackPush(data byte) {
c.bus.Write(StackBase+uint16(c.SP), data)
c.SP -= 1
}
func (c *Cpu) stackPeek() byte {
return c.bus.Read(StackBase + uint16(c.SP+1))
}
func (c *Cpu) stackPop() byte {
c.SP += 1
return c.bus.Read(StackBase + uint16(c.SP))
}

28
cpu_stack_test.go Normal file
View File

@ -0,0 +1,28 @@
package i6502
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestStackPushPopPeek(t *testing.T) {
assert := assert.New(t)
cpu, _, _ := NewRamMachine()
assert.Equal(0xFF, cpu.SP)
cpu.stackPush(0x42)
cpu.stackPush(0xA0)
assert.Equal(0xFD, cpu.SP)
assert.Equal(0x42, cpu.bus.Read(0x1FF))
assert.Equal(0xA0, cpu.bus.Read(0x1FE))
peekValue := cpu.stackPeek()
assert.Equal(0xFD, cpu.SP)
assert.Equal(0xA0, peekValue)
popValue := cpu.stackPop()
assert.Equal(0xFE, cpu.SP)
assert.Equal(0xA0, popValue)
}

View File

@ -4,7 +4,7 @@ package i6502
const (
sCarry = iota
sZero
sInterrupt
sIrqDisable
sDecimal
sBreak
_
@ -36,8 +36,8 @@ func (c *Cpu) setZero(state bool) {
c.setStatus(sZero, state)
}
func (c *Cpu) setInterrupt(state bool) {
c.setStatus(sInterrupt, state)
func (c *Cpu) setIrqDisable(state bool) {
c.setStatus(sIrqDisable, state)
}
func (c *Cpu) setDecimal(state bool) {
@ -68,8 +68,8 @@ func (c *Cpu) getZero() bool {
return c.getStatus(sZero)
}
func (c *Cpu) getInterrupt() bool {
return c.getStatus(sInterrupt)
func (c *Cpu) getIrqDisable() bool {
return c.getStatus(sIrqDisable)
}
func (c *Cpu) getDecimal() bool {
@ -117,3 +117,8 @@ func (c *Cpu) setSP(value byte) {
c.SP = value
c.setArithmeticFlags(c.SP)
}
func (c *Cpu) setP(value byte) {
c.P = value
c.P |= 0x20
}

File diff suppressed because it is too large Load Diff

View File

@ -37,10 +37,10 @@ func (c *Cpu) readNextInstruction() Instruction {
optype, ok := opTypes[opcode]
if !ok {
panic(fmt.Sprintf("Unknown or unimplemented opcode 0x%02X", opcode))
panic(fmt.Sprintf("Unknown or unimplemented opcode 0x%02X\n%s", opcode, c.String()))
}
instruction := Instruction{OpType: optype}
instruction := Instruction{OpType: optype, Address: c.PC}
switch instruction.Size {
case 1: // Zero operand instruction
case 2: // 8-bit operand

View File

@ -213,9 +213,9 @@ var opTypes = map[uint8]OpType{
// LDY
0xA0: OpType{0xA0, ldy, immediate, 2, 2},
0xA4: OpType{0xA4, ldy, zeropage, 2, 3},
0xB4: OpType{0xB4, ldy, zeropageY, 2, 4},
0xB4: OpType{0xB4, ldy, zeropageX, 2, 4},
0xAC: OpType{0xAC, ldy, absolute, 3, 4},
0xBC: OpType{0xBC, ldy, absoluteY, 3, 4},
0xBC: OpType{0xBC, ldy, absoluteX, 3, 4},
// ORA
0x09: OpType{0x09, ora, immediate, 2, 2},
@ -297,4 +297,82 @@ var opTypes = map[uint8]OpType{
0x56: OpType{0x56, lsr, zeropageX, 2, 6},
0x4E: OpType{0x4E, lsr, absolute, 3, 6},
0x5E: OpType{0x5E, lsr, absoluteX, 3, 7},
// ROL
0x2A: OpType{0x2A, rol, accumulator, 1, 2},
0x26: OpType{0x26, rol, zeropage, 2, 5},
0x36: OpType{0x36, rol, zeropageX, 2, 6},
0x2E: OpType{0x2E, rol, absolute, 3, 6},
0x3E: OpType{0x3E, rol, absoluteX, 3, 7},
// ROR
0x6A: OpType{0x6A, ror, accumulator, 1, 2},
0x66: OpType{0x66, ror, zeropage, 2, 5},
0x76: OpType{0x76, ror, zeropageX, 2, 6},
0x6E: OpType{0x6E, ror, absolute, 3, 6},
0x7E: OpType{0x7E, ror, absoluteX, 3, 7},
// CMP
0xC9: OpType{0xC9, cmp, immediate, 2, 2},
0xC5: OpType{0xC5, cmp, zeropage, 2, 3},
0xD5: OpType{0xD5, cmp, zeropageX, 2, 4},
0xCD: OpType{0xCD, cmp, absolute, 3, 4},
0xDD: OpType{0xDD, cmp, absoluteX, 3, 4},
0xD9: OpType{0xD9, cmp, absoluteY, 3, 4},
0xC1: OpType{0xC1, cmp, indirectX, 2, 6},
0xD1: OpType{0xD1, cmp, indirectY, 2, 5},
// CPX
0xE0: OpType{0xE0, cpx, immediate, 2, 2},
0xE4: OpType{0xE4, cpx, zeropage, 2, 3},
0xEC: OpType{0xEC, cpx, absolute, 3, 4},
// CPY
0xC0: OpType{0xC0, cpy, immediate, 2, 2},
0xC4: OpType{0xC4, cpy, zeropage, 2, 3},
0xCC: OpType{0xCC, cpy, absolute, 3, 4},
// BRK
0x00: OpType{0x00, brk, implied, 1, 7},
// BCC / BCS
0x90: OpType{0x90, bcc, relative, 2, 2},
0xB0: OpType{0xB0, bcs, relative, 2, 2},
// BNE / BEQ
0xD0: OpType{0xD0, bne, relative, 2, 2},
0xF0: OpType{0xF0, beq, relative, 2, 2},
// BPL / BMI
0x10: OpType{0x10, bpl, relative, 2, 2},
0x30: OpType{0x30, bmi, relative, 2, 2},
// BVC / BCS
0x50: OpType{0x50, bvc, relative, 2, 2},
0x70: OpType{0x70, bvs, relative, 2, 2},
// BIT
0x24: OpType{0x24, bit, zeropage, 2, 3},
0x2C: OpType{0x2C, bit, absolute, 3, 4},
// PHP / PLP
0x08: OpType{0x08, php, implied, 1, 3},
0x28: OpType{0x28, plp, implied, 1, 4},
// PHA / PLA
0x48: OpType{0x48, pha, implied, 1, 3},
0x68: OpType{0x68, pla, implied, 1, 4},
// JMP
0x4C: OpType{0x4C, jmp, absolute, 3, 3},
0x6C: OpType{0x6C, jmp, indirect, 3, 5},
// JSR
0x20: OpType{0x20, jsr, absolute, 3, 6},
// RTS
0x60: OpType{0x60, rts, implied, 1, 6},
// RTI
0x40: OpType{0x40, rti, implied, 1, 6},
}

Binary file not shown.