Skip to content

cpu: add basic support for some 65C02 opcodes #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 149 additions & 11 deletions cpu/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,20 @@ func (c *Cpu) memoryAddress(in Instruction) uint16 {
return uint16(in.Op8 + c.X)
case zeropageY:
return uint16(in.Op8 + c.Y)

// 65C02-only modes
case zpindirect:
return c.Bus.Read16(uint16(in.Op8))
case indirect:
return c.Bus.Read16(uint16(in.Op16))

// This is like indirectX but uses a full 16-bit absolute address.
case indirectX16:
return c.Bus.Read16(in.Op16 + uint16(c.X))

default:
panic("unhandled addressing")
panic(fmt.Sprintf("unhandled addressing mode: %v",
addressingNames[in.addressing]))
}
}

Expand Down Expand Up @@ -226,6 +238,10 @@ func (c *Cpu) execute(in Instruction) {
c.BNE(in)
case bpl:
c.BPL(in)
case bvs:
c.BVS(in)
case bvc:
c.BVC(in)
case brk:
c.BRK(in)
case clc:
Expand Down Expand Up @@ -270,6 +286,10 @@ func (c *Cpu) execute(in Instruction) {
c.NOP(in)
case ora:
c.ORA(in)
case php:
c.PHP(in)
case plp:
c.PLP(in)
case pha:
c.PHA(in)
case pla:
Expand All @@ -282,6 +302,8 @@ func (c *Cpu) execute(in Instruction) {
c.RTS(in)
case sbc:
c.SBC(in)
case sed:
c.SED(in)
case sec:
c.SEC(in)
case sei:
Expand All @@ -304,6 +326,24 @@ func (c *Cpu) execute(in Instruction) {
c.TXS(in)
case tya:
c.TYA(in)

// 65C02 only
case phx:
c.PHX(in)
case phy:
c.PHY(in)
case plx:
c.PLX(in)
case ply:
c.PLY(in)
case stz:
c.STZ(in)
case bra:
c.BRA(in)
case trb:
c.TRB(in)
case tsb:
c.TSB(in)
case _end:
c._END(in)
default:
Expand Down Expand Up @@ -356,6 +396,20 @@ func (c *Cpu) BCS(in Instruction) {
}
}

// BVC: Branch if overflow clear.
func (c *Cpu) BVC(in Instruction) {
if !c.getStatus(sOverflow) {
c.branch(in)
}
}

// BVS: Branch if overflow set.
func (c *Cpu) BVS(in Instruction) {
if c.getStatus(sOverflow) {
c.branch(in)
}
}

// BEQ: Branch if equal (z=1).
func (c *Cpu) BEQ(in Instruction) {
if c.getStatus(sZero) {
Expand All @@ -371,6 +425,22 @@ func (c *Cpu) BIT(in Instruction) {
c.setStatus(sNegative, value&(1<<7) != 0)
}

// TRB: Test and Reset bits
func (c *Cpu) TRB(in Instruction) {
value := c.resolveOperand(in)
c.setStatus(sZero, value&c.AC == 0)
// note: the bits which are *set* in AC are *cleared* in value
c.Bus.Write(c.memoryAddress(in), value&(c.AC^0xFF))
}

// TSB: Test and Set bits
func (c *Cpu) TSB(in Instruction) {
value := c.resolveOperand(in)
c.setStatus(sZero, value&c.AC == 0)
// note: the bits which are *set* in AC are *set* in value
c.Bus.Write(c.memoryAddress(in), value|c.AC)
}

// BMI: Branch if negative.
func (c *Cpu) BMI(in Instruction) {
if c.getStatus(sNegative) {
Expand All @@ -392,6 +462,11 @@ func (c *Cpu) BPL(in Instruction) {
}
}

// BRA: Unconditional branch
func (c *Cpu) BRA(in Instruction) {
c.branch(in)
}

// BRK: software interrupt
func (c *Cpu) BRK(in Instruction) {
// temporarily used to dump status
Expand All @@ -410,7 +485,7 @@ func (c *Cpu) CLD(in Instruction) {

// CLI: Clear interrupt-disable flag.
func (c *Cpu) CLI(in Instruction) {
c.setStatus(sInterrupt, true)
c.setStatus(sInterrupt, false)
}

// CMP: Compare accumulator with memory.
Expand All @@ -436,10 +511,17 @@ func (c *Cpu) CPY(in Instruction) {

// DEC: Decrement.
func (c *Cpu) DEC(in Instruction) {
address := c.memoryAddress(in)
value := c.Bus.Read(address) - 1
c.Bus.Write(address, value)
c.updateStatus(value)
switch in.addressing {
case implied:
// 65C02 only: decrement accumulator
c.AC--
c.updateStatus(c.AC)
default:
address := c.memoryAddress(in)
value := c.Bus.Read(address) - 1
c.Bus.Write(address, value)
c.updateStatus(value)
}
}

// DEX: Decrement index register X.
Expand All @@ -463,10 +545,17 @@ func (c *Cpu) EOR(in Instruction) {

// INC: Increment.
func (c *Cpu) INC(in Instruction) {
address := c.memoryAddress(in)
value := c.Bus.Read(address) + 1
c.Bus.Write(address, value)
c.updateStatus(value)
switch in.addressing {
case implied:
// 65C02 only: increment accumulator
c.AC++
c.updateStatus(c.AC)
default:
address := c.memoryAddress(in)
value := c.Bus.Read(address) + 1
c.Bus.Write(address, value)
c.updateStatus(value)
}
}

// INX: Increment index register X.
Expand Down Expand Up @@ -538,6 +627,18 @@ func (c *Cpu) ORA(in Instruction) {
c.updateStatus(c.AC)
}

// PHP: Push status onto stack.
func (c *Cpu) PHP(in Instruction) {
c.Bus.Write(0x0100+uint16(c.SP), c.SR)
c.SP--
}

// PLP: Pull status from stack.
func (c *Cpu) PLP(in Instruction) {
c.SP++
c.SR = c.Bus.Read(0x0100 + uint16(c.SP))
}

// PHA: Push accumulator onto stack.
func (c *Cpu) PHA(in Instruction) {
c.Bus.Write(0x0100+uint16(c.SP), c.AC)
Expand All @@ -548,6 +649,33 @@ func (c *Cpu) PHA(in Instruction) {
func (c *Cpu) PLA(in Instruction) {
c.SP++
c.AC = c.Bus.Read(0x0100 + uint16(c.SP))
c.updateStatus(c.AC)
}

// PHX: Push X onto stack.
func (c *Cpu) PHX(in Instruction) {
c.Bus.Write(0x0100+uint16(c.SP), c.X)
c.SP--
}

// PLX: Pull X from stack.
func (c *Cpu) PLX(in Instruction) {
c.SP++
c.X = c.Bus.Read(0x0100 + uint16(c.SP))
c.updateStatus(c.X)
}

// PHY: Push Y onto stack.
func (c *Cpu) PHY(in Instruction) {
c.Bus.Write(0x0100+uint16(c.SP), c.Y)
c.SP--
}

// PLY: Pull Y from stack.
func (c *Cpu) PLY(in Instruction) {
c.SP++
c.Y = c.Bus.Read(0x0100 + uint16(c.SP))
c.updateStatus(c.Y)
}

// ROL: Rotate memory or accumulator left one bit.
Expand Down Expand Up @@ -617,9 +745,14 @@ func (c *Cpu) SEC(in Instruction) {
c.setStatus(sCarry, true)
}

// SED: Set decimal mode flag.
func (c *Cpu) SED(in Instruction) {
c.setStatus(sDecimal, true)
}

// SEI: Set interrupt-disable flag.
func (c *Cpu) SEI(in Instruction) {
c.setStatus(sInterrupt, false)
c.setStatus(sInterrupt, true)
}

// STA: Store accumulator to memory.
Expand All @@ -637,6 +770,11 @@ func (c *Cpu) STY(in Instruction) {
c.Bus.Write(c.memoryAddress(in), c.Y)
}

// STZ: Store zero to memory.
func (c *Cpu) STZ(in Instruction) {
c.Bus.Write(c.memoryAddress(in), 0)
}

// TAX: Transfer accumulator to index register X.
func (c *Cpu) TAX(in Instruction) {
c.X = c.AC
Expand Down
64 changes: 63 additions & 1 deletion cpu/op_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ const (
zeropage
zeropageX
zeropageY

// 65C02 only
zpindirect
indirectX16
)

var addressingNames = [...]string{
Expand All @@ -35,6 +39,8 @@ var addressingNames = [...]string{
"zeropage",
"zeropageX",
"zeropageY",
"(zeropage)",
"(absolute,X)",
}

// adc..tya represent the 6502 instruction set mnemonics. Each mnemonic maps to
Expand Down Expand Up @@ -97,6 +103,17 @@ const (
txa
txs
tya

// 65{S}C02-only
bra
phx
phy
plx
ply
stz
trb
tsb

_end
)

Expand Down Expand Up @@ -158,6 +175,17 @@ var instructionNames = [...]string{
"TXA",
"TXS",
"TYA",

// 65{S}C02-only
"BRA",
"PHX",
"PHY",
"PLX",
"PLY",
"STZ",
"TRB",
"TSB",

"_END",
}

Expand Down Expand Up @@ -308,7 +336,7 @@ var optypes = map[uint8]OpType{
0x48: OpType{0x48, pha, implied, 1, 3},
0x08: OpType{0x08, php, implied, 1, 3},
0x68: OpType{0x68, pla, implied, 1, 4},
0x28: OpType{0x28, php, implied, 1, 4},
0x28: OpType{0x28, plp, implied, 1, 4},
0x2A: OpType{0x2A, rol, accumulator, 1, 2},
0x26: OpType{0x26, rol, zeropage, 2, 5},
0x36: OpType{0x36, rol, zeropageX, 2, 6},
Expand Down Expand Up @@ -351,5 +379,39 @@ var optypes = map[uint8]OpType{
0x8A: OpType{0x8A, txa, implied, 1, 2},
0x9A: OpType{0x9A, txs, implied, 1, 2},
0x98: OpType{0x98, tya, implied, 1, 2},

// 65C02 only

// Additional addressing modes
0x12: OpType{0x12, ora, zpindirect, 2, 5},
0x32: OpType{0x32, and, zpindirect, 2, 5},
0x52: OpType{0x52, eor, zpindirect, 2, 5},
0x72: OpType{0x72, adc, zpindirect, 2, 5},
0x92: OpType{0x92, sta, zpindirect, 2, 5},
0xB2: OpType{0xB2, lda, zpindirect, 2, 5},
0xD2: OpType{0xD2, cmp, zpindirect, 2, 5},
0xF2: OpType{0xF2, sbc, zpindirect, 2, 5},
0x89: OpType{0x89, bit, immediate, 2, 2},
0x34: OpType{0x34, bit, zeropageX, 2, 4},
0x3C: OpType{0x3C, bit, absoluteX, 3, 4},
0x3A: OpType{0x3A, dec, implied, 1, 2},
0x1A: OpType{0x1A, inc, implied, 1, 2},
0x7C: OpType{0x7C, jmp, indirectX16, 3, 6},

// New instructions
0x80: OpType{0x80, bra, relative, 2, 3},
0xDA: OpType{0xDA, phx, implied, 1, 3},
0x5A: OpType{0x5A, phy, implied, 1, 3},
0xFA: OpType{0xFA, plx, implied, 1, 4},
0x7A: OpType{0x7A, ply, implied, 1, 4},
0x64: OpType{0x64, stz, zeropage, 2, 3},
0x74: OpType{0x74, stz, zeropageX, 2, 4},
0x9C: OpType{0x9C, stz, absolute, 3, 4},
0x9E: OpType{0x9E, stz, absoluteX, 3, 5},
0x14: OpType{0x14, trb, zeropage, 2, 5},
0x1C: OpType{0x1C, trb, absolute, 3, 6},
0x04: OpType{0x04, tsb, zeropage, 2, 5},
0x0C: OpType{0x0C, tsb, absolute, 3, 5},

0xFF: OpType{0xFF, _end, implied, 1, 1},
}