mirror of
https://github.com/ariejan/i6502.git
synced 2025-04-06 23:37:10 +00:00
Implement remaining opcodes; tests
This commit is contained in:
parent
f3f95dda39
commit
33ca177f90
107
cpu.go
107
cpu.go
@ -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:
|
||||
|
@ -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
19
cpu_stack.go
Normal 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
28
cpu_stack_test.go
Normal 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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
1343
cpu_test.go
1343
cpu_test.go
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
82
opcodes.go
82
opcodes.go
@ -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},
|
||||
}
|
||||
|
BIN
test/6502_functional_test.bin
Normal file
BIN
test/6502_functional_test.bin
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user