From af4d734d40c4d2a9d13f55af0ca4c7a4822ec69f Mon Sep 17 00:00:00 2001 From: Rich Wareham Date: Wed, 15 Apr 2015 15:58:28 +0100 Subject: [PATCH 1/8] cpu: add basic support for 65C02 opcodes Add support for the new addressing modes and new instructions in the 65C02 family of processors. The distinction is noted in the source but is not exposed to the programmer yet. New instructions: BRA, PHX, PHY, PLX, PLY, STZ, TRB and TSB. Add the (zp) addressing mode to ADC, AND, CMP, EOR, LDA, ORA, SBC and STA. Add (abs,X) and (zp,X) addressing modes to BIT. Add accumulator implicit target for DEC/INC. Add (abs,X) addressing mode for JMP. --- cpu/cpu.go | 108 ++++++++++++++++++++++++++++++++++++++++++++----- cpu/op_type.go | 60 +++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 9 deletions(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index 702582f..efba538 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -156,8 +156,16 @@ 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)) + default: - panic("unhandled addressing") + panic(fmt.Sprintf("unhandled addressing mode: %v", + addressingNames[in.addressing])) } } @@ -304,6 +312,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: @@ -371,6 +397,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) { @@ -392,6 +434,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 @@ -436,10 +483,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. @@ -463,10 +517,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. @@ -550,6 +611,30 @@ func (c *Cpu) PLA(in Instruction) { c.AC = c.Bus.Read(0x0100 + uint16(c.SP)) } +// 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)) +} + +// 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)) +} + // ROL: Rotate memory or accumulator left one bit. func (c *Cpu) ROL(in Instruction) { carry := c.getStatusInt(sCarry) @@ -637,6 +722,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 diff --git a/cpu/op_type.go b/cpu/op_type.go index 03e547b..6cdb3de 100644 --- a/cpu/op_type.go +++ b/cpu/op_type.go @@ -18,6 +18,9 @@ const ( zeropage zeropageX zeropageY + + // 65C02 only + zpindirect ) var addressingNames = [...]string{ @@ -35,6 +38,7 @@ var addressingNames = [...]string{ "zeropage", "zeropageX", "zeropageY", + "(zeropage)", } // adc..tya represent the 6502 instruction set mnemonics. Each mnemonic maps to @@ -97,6 +101,17 @@ const ( txa txs tya + + // 65{S}C02-only + bra + phx + phy + plx + ply + stz + trb + tsb + _end ) @@ -158,6 +173,17 @@ var instructionNames = [...]string{ "TXA", "TXS", "TYA", + + // 65{S}C02-only + "BRA", + "PHX", + "PHY", + "PLX", + "PLY", + "STZ", + "TRB", + "TSB", + "_END", } @@ -351,5 +377,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, absoluteX, 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}, } From bf189bc7a3f5ccd6ed357bf82aab96ca39f91939 Mon Sep 17 00:00:00 2001 From: Rich Wareham Date: Sun, 19 Apr 2015 19:30:01 +0100 Subject: [PATCH 2/8] cpu: implement PHP/PLP instruction --- cpu/cpu.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cpu/cpu.go b/cpu/cpu.go index efba538..8c800a6 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -278,6 +278,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: @@ -599,6 +603,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) From 257909909cbf7ad43945c823d09194b77b1d0d95 Mon Sep 17 00:00:00 2001 From: Rich Wareham Date: Mon, 20 Apr 2015 23:55:21 +0100 Subject: [PATCH 3/8] cpu/op_type.go: fix typo: plp/php confusion --- cpu/op_type.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpu/op_type.go b/cpu/op_type.go index 6cdb3de..21acded 100644 --- a/cpu/op_type.go +++ b/cpu/op_type.go @@ -334,7 +334,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}, From 11e5966dc79530ed415fe5663de14c416237f46d Mon Sep 17 00:00:00 2001 From: Rich Wareham Date: Tue, 21 Apr 2015 00:34:04 +0100 Subject: [PATCH 4/8] cpu: implement BVS and BVC opcodes --- cpu/cpu.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cpu/cpu.go b/cpu/cpu.go index 8c800a6..00753ee 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -234,6 +234,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: @@ -386,6 +390,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) { From 2138b65575bcc91c459a55d755c0e948136aba5b Mon Sep 17 00:00:00 2001 From: Rich Wareham Date: Tue, 21 Apr 2015 22:38:07 +0100 Subject: [PATCH 5/8] cpu: set NZ flags on PL{A,X,Y} Make PL{A,X,Y} instructions set processor status flags. --- cpu/cpu.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpu/cpu.go b/cpu/cpu.go index 00753ee..06c2f1e 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -643,6 +643,7 @@ 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. @@ -655,6 +656,7 @@ func (c *Cpu) PHX(in Instruction) { 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. @@ -667,6 +669,7 @@ func (c *Cpu) PHY(in Instruction) { 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. From ac73bf5a4d8dbce8bd76b5dae9133866de8fd92b Mon Sep 17 00:00:00 2001 From: Rich Wareham Date: Tue, 21 Apr 2015 00:50:47 +0100 Subject: [PATCH 6/8] cpu: add SED implementation --- cpu/cpu.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cpu/cpu.go b/cpu/cpu.go index 06c2f1e..e30c748 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -298,6 +298,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: @@ -739,6 +741,11 @@ 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) From 80a58a5abf5e5924e1b1a70e0263bca01c616655 Mon Sep 17 00:00:00 2001 From: Rich Wareham Date: Tue, 21 Apr 2015 22:45:28 +0100 Subject: [PATCH 7/8] cpu: fix logic for SEI/CLI The SEI and CLI instructions, counter intuitively, set interrupt disable and clear interrupt disable. Hence to *enable* interrupts (and clear the I flag) one issues CLI and to *disable* them (set the I flag) one issues SEI. --- cpu/cpu.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index e30c748..0d23147 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -481,7 +481,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. @@ -748,7 +748,7 @@ func (c *Cpu) SED(in Instruction) { // SEI: Set interrupt-disable flag. func (c *Cpu) SEI(in Instruction) { - c.setStatus(sInterrupt, false) + c.setStatus(sInterrupt, true) } // STA: Store accumulator to memory. From c686edefac17e043a2d54b6b4ce5ac031ca08498 Mon Sep 17 00:00:00 2001 From: Rich Wareham Date: Tue, 21 Apr 2015 23:19:48 +0100 Subject: [PATCH 8/8] cpu: correct implementation of jmp (abs, X) Fix a misunderstanding about what indirectX addressing was. --- cpu/cpu.go | 4 ++++ cpu/op_type.go | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index 0d23147..90d739c 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -163,6 +163,10 @@ func (c *Cpu) memoryAddress(in Instruction) uint16 { 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(fmt.Sprintf("unhandled addressing mode: %v", addressingNames[in.addressing])) diff --git a/cpu/op_type.go b/cpu/op_type.go index 21acded..8879727 100644 --- a/cpu/op_type.go +++ b/cpu/op_type.go @@ -21,6 +21,7 @@ const ( // 65C02 only zpindirect + indirectX16 ) var addressingNames = [...]string{ @@ -39,6 +40,7 @@ var addressingNames = [...]string{ "zeropageX", "zeropageY", "(zeropage)", + "(absolute,X)", } // adc..tya represent the 6502 instruction set mnemonics. Each mnemonic maps to @@ -394,7 +396,7 @@ var optypes = map[uint8]OpType{ 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, absoluteX, 3, 6}, + 0x7C: OpType{0x7C, jmp, indirectX16, 3, 6}, // New instructions 0x80: OpType{0x80, bra, relative, 2, 3},