Emulation of the 65c02 processor variant.

This commit is contained in:
Ivan Izaguirre 2019-10-11 00:06:42 +02:00
parent f92bc2b08e
commit df41114043
11 changed files with 11918 additions and 111 deletions

View File

@ -1,33 +0,0 @@
package core6502
import (
"fmt"
"testing"
)
func TestFunctional(t *testing.T) {
m := new(FlatMemory)
s := NewNMOS6502(m)
// Test suite from https://github.com/Klaus2m5/6502_65C02_functional_tests
m.loadBinary("testdata/6502_functional_test.bin")
s.reg.setPC(0x0400)
for true {
testCase := s.mem.Peek(0x0200)
if testCase >= 240 {
break
}
log := testCase > 43
if log {
fmt.Printf("[ %d ] ", testCase)
}
s.SetTrace(log)
pc := s.reg.getPC()
s.ExecuteInstruction()
if pc == s.reg.getPC() {
t.Errorf("Failure in test %v.", testCase)
}
}
}

View File

@ -1,5 +1,7 @@
package core6502 package core6502
import "fmt"
const ( const (
modeImplicit = iota + 1 modeImplicit = iota + 1
modeImplicitX modeImplicitX
@ -16,6 +18,10 @@ const (
modeIndirect modeIndirect
modeIndexedIndirectX modeIndexedIndirectX
modeIndirectIndexedY modeIndirectIndexedY
// Added on the 65c02
modeIndirectZeroPage
modeAbsoluteIndexedIndirectX
modeZeroPageAndRelative
) )
func getWordInLine(line []uint8) uint16 { func getWordInLine(line []uint8) uint16 {
@ -84,6 +90,22 @@ func resolve(s *State, line []uint8, opcode opcode) (getValue func() uint8, setV
case modeIndirectIndexedY: case modeIndirectIndexedY:
address = getZeroPageWord(s.mem, line[1]) + address = getZeroPageWord(s.mem, line[1]) +
uint16(s.reg.getY()) uint16(s.reg.getY())
// 65c02 additions
case modeIndirectZeroPage:
address = getZeroPageWord(s.mem, line[1])
case modeAbsoluteIndexedIndirectX:
addressAddress := getWordInLine(line) + uint16(s.reg.getX())
address = getWord(s.mem, addressAddress)
case modeRelative:
// This assumes that PC is already pointing to the next instruction
address = s.reg.getPC() + uint16(int8(line[1])) // Note: line[1] is signed
case modeZeroPageAndRelative:
// Two addressing modes combined. We refer to the second one, relative,
// placed one byte after the zeropage reference
address = s.reg.getPC() + uint16(int8(line[2])) // Note: line[2] is signed
default:
panic("Assert failed. Missing addressing mode")
} }
if hasAddress { if hasAddress {
@ -101,3 +123,47 @@ func resolve(s *State, line []uint8, opcode opcode) (getValue func() uint8, setV
} }
return return
} }
func lineString(line []uint8, opcode opcode) string {
t := opcode.name
switch opcode.addressMode {
case modeImplicit:
case modeImplicitX:
case modeImplicitY:
//Nothing
case modeAccumulator:
t += fmt.Sprintf(" A")
case modeImmediate:
t += fmt.Sprintf(" #%02x", line[1])
case modeZeroPage:
t += fmt.Sprintf(" $%02x", line[1])
case modeZeroPageX:
t += fmt.Sprintf(" $%02x,X", line[1])
case modeZeroPageY:
t += fmt.Sprintf(" $%02x,Y", line[1])
case modeRelative:
t += fmt.Sprintf(" *%+x", int8(line[1]))
case modeAbsolute:
t += fmt.Sprintf(" $%04x", getWordInLine(line))
case modeAbsoluteX:
t += fmt.Sprintf(" $%04x,X", getWordInLine(line))
case modeAbsoluteY:
t += fmt.Sprintf(" $%04x,Y", getWordInLine(line))
case modeIndirect:
t += fmt.Sprintf(" ($%04x)", getWordInLine(line))
case modeIndexedIndirectX:
t += fmt.Sprintf(" ($%02x,X)", line[1])
case modeIndirectIndexedY:
t += fmt.Sprintf(" ($%02x),Y", line[1])
// 65c02 additions:
case modeIndirectZeroPage:
t += fmt.Sprintf(" ($%02x)", line[1])
case modeAbsoluteIndexedIndirectX:
t += fmt.Sprintf(" ($%04x,X)", getWordInLine(line))
case modeZeroPageAndRelative:
t += fmt.Sprintf(" $%02x *%+x", line[1], int8(line[2]))
default:
t += "UNKNOWN MODE"
}
return t
}

165
core6502/cmos65c02.go Normal file
View File

@ -0,0 +1,165 @@
package core6502
/*
For the diffrences with NMOS6502 see:
http://6502.org/tutorials/65c02opcodes.html
http://wilsonminesco.com/NMOS-CMOSdif/
http://www.obelisk.me.uk/65C02/reference.html
http://www.obelisk.me.uk/65C02/addressing.html
http://anyplatform.net/media/guides/cpus/65xx%20Processor%20Data.txt
*/
// NewCMOS65c02 returns an initialized 65c02
func NewCMOS65c02(m Memory) *State {
var s State
s.mem = m
var opcodes [256]opcode
for i := 0; i < 256; i++ {
opcodes[i] = opcodesNMOS6502[i]
if opcodes65c02Delta[i].cycles != 0 {
opcodes[i] = opcodes65c02Delta[i]
}
}
add65c02NOPs(&opcodes)
s.opcodes = &opcodes
return &s
}
func add65c02NOPs(opcodes *[256]opcode) {
nop11 := opcode{"NOP", 1, 1, modeImplicit, opNOP}
nop22 := opcode{"NOP", 2, 2, modeImmediate, opNOP}
nop23 := opcode{"NOP", 2, 3, modeImmediate, opNOP}
nop24 := opcode{"NOP", 2, 4, modeImmediate, opNOP}
nop34 := opcode{"NOP", 3, 4, modeAbsolute, opNOP}
nop38 := opcode{"NOP", 3, 8, modeAbsolute, opNOP}
opcodes[0x02] = nop22
opcodes[0x22] = nop22
opcodes[0x42] = nop22
opcodes[0x62] = nop22
opcodes[0x82] = nop22
opcodes[0xc2] = nop22
opcodes[0xe2] = nop22
opcodes[0x44] = nop23
opcodes[0x54] = nop24
opcodes[0xD4] = nop24
opcodes[0xF4] = nop24
opcodes[0x5c] = nop38
opcodes[0xdc] = nop34
opcodes[0xfc] = nop34
for i := 0; i < 0x100; i = i + 0x10 {
opcodes[i+0x03] = nop11
// RMB and SMB; opcodes[i+0x07] = nop11
opcodes[i+0x0b] = nop11
// BBR and BBS: opcodes[i+0x0f] = nop11
}
}
var opcodes65c02Delta = [256]opcode{
// Functional difference
0x00: opcode{"BRK", 1, 7, modeImplicit, opBRKAlt},
0x24: opcode{"BIT", 2, 3, modeZeroPage, opBIT},
0x2C: opcode{"BIT", 3, 3, modeAbsolute, opBIT},
// Fixed BCD arithmetic flags
0x69: opcode{"ADC", 2, 2, modeImmediate, opADCAlt},
0x65: opcode{"ADC", 2, 3, modeZeroPage, opADCAlt},
0x75: opcode{"ADC", 2, 4, modeZeroPageX, opADCAlt},
0x6D: opcode{"ADC", 3, 4, modeAbsolute, opADCAlt},
0x7D: opcode{"ADC", 3, 4, modeAbsoluteX, opADCAlt},
0x79: opcode{"ADC", 3, 4, modeAbsoluteY, opADCAlt},
0x61: opcode{"ADC", 2, 6, modeIndexedIndirectX, opADCAlt},
0x71: opcode{"ADC", 2, 5, modeIndirectIndexedY, opADCAlt},
0xE9: opcode{"SBC", 2, 2, modeImmediate, opSBCAlt},
0xE5: opcode{"SBC", 2, 3, modeZeroPage, opSBCAlt},
0xF5: opcode{"SBC", 2, 4, modeZeroPageX, opSBCAlt},
0xED: opcode{"SBC", 3, 4, modeAbsolute, opSBCAlt},
0xFD: opcode{"SBC", 3, 4, modeAbsoluteX, opSBCAlt},
0xF9: opcode{"SBC", 3, 4, modeAbsoluteY, opSBCAlt},
0xE1: opcode{"SBC", 2, 6, modeIndexedIndirectX, opSBCAlt},
0xF1: opcode{"SBC", 2, 5, modeIndirectIndexedY, opSBCAlt},
// Different cycle count
0x1e: opcode{"ASL", 3, 6, modeAbsoluteX, buildOpShift(true, false)},
0x3e: opcode{"ROL", 3, 6, modeAbsoluteX, buildOpShift(true, true)},
0x5e: opcode{"LSR", 3, 6, modeAbsoluteX, buildOpShift(false, false)},
0x7e: opcode{"ROR", 3, 6, modeAbsoluteX, buildOpShift(false, true)},
// New indirect zero page addresssing mode
0x12: opcode{"ORA", 2, 5, modeIndirectZeroPage, buildOpLogic(operationOr)},
0x32: opcode{"AND", 2, 5, modeIndirectZeroPage, buildOpLogic(operationAnd)},
0x52: opcode{"EOR", 2, 5, modeIndirectZeroPage, buildOpLogic(operationXor)},
0x72: opcode{"ADC", 2, 5, modeIndirectZeroPage, opADCAlt},
0x92: opcode{"STA", 2, 5, modeIndirectZeroPage, buildOpStore(regA)},
0xb2: opcode{"LDA", 2, 5, modeIndirectZeroPage, buildOpLoad(regA)},
0xd2: opcode{"CMP", 2, 5, modeIndirectZeroPage, buildOpCompare(regA)},
0xf2: opcode{"SBC", 2, 5, modeIndirectZeroPage, opSBCAlt},
// New addressing options
0x89: opcode{"BIT", 2, 2, modeImmediate, opBIT},
0x34: opcode{"BIT", 2, 4, modeZeroPageX, opBIT},
0x3c: opcode{"BIT", 3, 4, modeAbsoluteX, opBIT},
0x1a: opcode{"INC", 1, 2, modeAccumulator, buildOpIncDec(true)},
0x3a: opcode{"DEC", 1, 2, modeAccumulator, buildOpIncDec(false)},
0x7c: opcode{"JMP", 3, 6, modeAbsoluteIndexedIndirectX, opJMP},
// Additional instructions: BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB
0xda: opcode{"PHX", 1, 3, modeImplicit, buildOpPush(regX)},
0x5a: opcode{"PHY", 1, 3, modeImplicit, buildOpPush(regY)},
0xfa: opcode{"PLX", 1, 4, modeImplicit, buildOpPull(regX)},
0x7a: opcode{"PLY", 1, 4, modeImplicit, buildOpPull(regY)},
0x80: opcode{"BRA", 2, 4, modeRelative, opJMP},
0x64: opcode{"STZ", 2, 3, modeZeroPage, opSTZ},
0x74: opcode{"STZ", 2, 4, modeZeroPageX, opSTZ},
0x9c: opcode{"STZ", 3, 4, modeAbsolute, opSTZ},
0x9e: opcode{"STZ", 3, 5, modeAbsoluteX, opSTZ},
0x14: opcode{"TRB", 2, 5, modeZeroPage, opTRB},
0x1c: opcode{"TRB", 3, 6, modeAbsolute, opTRB},
0x04: opcode{"TSB", 2, 5, modeZeroPage, opTSB},
0x0c: opcode{"TSB", 3, 6, modeAbsolute, opTSB},
// Additional in Rockwell 65c02 and WDC 65c02?
// They have a double addressing mode: zeropage and relative.
0x0f: opcode{"BBR0", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(0, false)},
0x1f: opcode{"BBR1", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(1, false)},
0x2f: opcode{"BBR2", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(2, false)},
0x3f: opcode{"BBR3", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(3, false)},
0x4f: opcode{"BBR4", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(4, false)},
0x5f: opcode{"BBR5", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(5, false)},
0x6f: opcode{"BBR6", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(6, false)},
0x7f: opcode{"BBR7", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(7, false)},
0x8f: opcode{"BBS0", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(0, true)},
0x9f: opcode{"BBS1", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(1, true)},
0xaf: opcode{"BBS2", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(2, true)},
0xbf: opcode{"BBS3", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(3, true)},
0xcf: opcode{"BBS4", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(4, true)},
0xdf: opcode{"BBS5", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(5, true)},
0xef: opcode{"BBS6", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(6, true)},
0xff: opcode{"BBS7", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(7, true)},
0x07: opcode{"RMB0", 2, 5, modeZeroPage, buildOpSetBit(0, false)},
0x17: opcode{"RMB1", 2, 5, modeZeroPage, buildOpSetBit(1, false)},
0x27: opcode{"RMB2", 2, 5, modeZeroPage, buildOpSetBit(2, false)},
0x37: opcode{"RMB3", 2, 5, modeZeroPage, buildOpSetBit(3, false)},
0x47: opcode{"RMB4", 2, 5, modeZeroPage, buildOpSetBit(4, false)},
0x57: opcode{"RMB5", 2, 5, modeZeroPage, buildOpSetBit(5, false)},
0x67: opcode{"RMB6", 2, 5, modeZeroPage, buildOpSetBit(6, false)},
0x77: opcode{"RMB7", 2, 5, modeZeroPage, buildOpSetBit(7, false)},
0x87: opcode{"SMB0", 2, 5, modeZeroPage, buildOpSetBit(0, true)},
0x97: opcode{"SMB1", 2, 5, modeZeroPage, buildOpSetBit(1, true)},
0xa7: opcode{"SMB2", 2, 5, modeZeroPage, buildOpSetBit(2, true)},
0xb7: opcode{"SMB3", 2, 5, modeZeroPage, buildOpSetBit(3, true)},
0xc7: opcode{"SMB4", 2, 5, modeZeroPage, buildOpSetBit(4, true)},
0xd7: opcode{"SMB5", 2, 5, modeZeroPage, buildOpSetBit(5, true)},
0xe7: opcode{"SMB6", 2, 5, modeZeroPage, buildOpSetBit(6, true)},
0xf7: opcode{"SMB7", 2, 5, modeZeroPage, buildOpSetBit(7, true)},
// Maybe additional Rockwell: STP, WAI
}

View File

@ -0,0 +1,32 @@
package core6502
import (
"testing"
)
func TestCMOs65c02NoUndocumented(t *testing.T) {
m := new(FlatMemory)
s := NewCMOS65c02(m)
for i := 0; i < 256; i++ {
if s.opcodes[i].cycles == 0 {
t.Errorf("Opcode missing for $%02x.", i)
}
}
}
func TestCMOS65c02asNMOS(t *testing.T) {
m := new(FlatMemory)
s := NewCMOS65c02(m)
m.loadBinary("testdata/6502_functional_test.bin")
executeSuite(t, s, 0x200, 240, false, 255)
}
func TestCMOS65c02(t *testing.T) {
m := new(FlatMemory)
s := NewCMOS65c02(m)
m.loadBinary("testdata/65C02_extended_opcodes_test.bin")
executeSuite(t, s, 0x202, 240, false, 255)
}

View File

@ -63,7 +63,7 @@ func (s *State) ExecuteInstruction() {
if s.trace { if s.trace {
//fmt.Printf("%#04x %#02x\n", pc-opcode.bytes, opcodeID) //fmt.Printf("%#04x %#02x\n", pc-opcode.bytes, opcodeID)
fmt.Printf("%#04x %-12s: ", pc-opcode.bytes, lineString(line, opcode)) fmt.Printf("%#04x %-13s: ", pc-opcode.bytes, lineString(line, opcode))
} }
opcode.action(s, line, opcode) opcode.action(s, line, opcode)
s.cycles += uint64(opcode.cycles) s.cycles += uint64(opcode.cycles)
@ -84,6 +84,16 @@ func (s *State) GetCycles() uint64 {
return s.cycles return s.cycles
} }
// SetTrace activates tracing of the cpu execution
func (s *State) SetTrace(trace bool) {
s.trace = trace
}
// GetTrace gets trhe tracing state of the cpu execution
func (s *State) GetTrace() bool {
return s.trace
}
// Save saves the CPU state (registers and cycle counter) // Save saves the CPU state (registers and cycle counter)
func (s *State) Save(w io.Writer) error { func (s *State) Save(w io.Writer) error {
err := binary.Write(w, binary.BigEndian, s.cycles) err := binary.Write(w, binary.BigEndian, s.cycles)
@ -109,50 +119,3 @@ func (s *State) Load(r io.Reader) error {
} }
return nil return nil
} }
func lineString(line []uint8, opcode opcode) string {
t := opcode.name
switch opcode.addressMode {
case modeImplicit:
case modeImplicitX:
case modeImplicitY:
//Nothing
case modeAccumulator:
t += fmt.Sprintf(" A")
case modeImmediate:
t += fmt.Sprintf(" #%02x", line[1])
case modeZeroPage:
t += fmt.Sprintf(" $%02x", line[1])
case modeZeroPageX:
t += fmt.Sprintf(" $%02x,X", line[1])
case modeZeroPageY:
t += fmt.Sprintf(" $%02x,Y", line[1])
case modeRelative:
t += fmt.Sprintf(" *%+x", int8(line[1]))
case modeAbsolute:
t += fmt.Sprintf(" $%04x", getWordInLine(line))
case modeAbsoluteX:
t += fmt.Sprintf(" $%04x,X", getWordInLine(line))
case modeAbsoluteY:
t += fmt.Sprintf(" $%04x,Y", getWordInLine(line))
case modeIndirect:
t += fmt.Sprintf(" ($%04x)", getWordInLine(line))
case modeIndexedIndirectX:
t += fmt.Sprintf(" ($%02x,X)", line[1])
case modeIndirectIndexedY:
t += fmt.Sprintf(" ($%02x),Y", line[1])
default:
t += "UNKNOWN MODE"
}
return t
}
// SetTrace activates tracing of the cpu execution
func (s *State) SetTrace(trace bool) {
s.trace = trace
}
// GetTrace gets trhe tracing state of the cpu execution
func (s *State) GetTrace() bool {
return s.trace
}

View File

@ -483,5 +483,3 @@ func TestStack(t *testing.T) {
t.Errorf("Error in PLP, %v", s.reg) t.Errorf("Error in PLP, %v", s.reg)
} }
} }
// TODO: Tests for BRK, JMP, JSR, RTI, RTS

View File

@ -16,10 +16,10 @@ var opcodesNMOS6502 = [256]opcode{
0x40: opcode{"RTI", 1, 6, modeImplicit, opRTI}, 0x40: opcode{"RTI", 1, 6, modeImplicit, opRTI},
0x60: opcode{"RTS", 1, 6, modeImplicit, opRTS}, 0x60: opcode{"RTS", 1, 6, modeImplicit, opRTS},
0x48: opcode{"PHA", 1, 3, modeImplicit, opPHA}, 0x48: opcode{"PHA", 1, 3, modeImplicit, buildOpPush(regA)},
0x08: opcode{"PHP", 1, 3, modeImplicit, opPHP}, 0x08: opcode{"PHP", 1, 3, modeImplicit, buildOpPush(regP)},
0x68: opcode{"PLA", 1, 4, modeImplicit, opPLA}, 0x68: opcode{"PLA", 1, 4, modeImplicit, buildOpPull(regA)},
0x28: opcode{"PLP", 1, 4, modeImplicit, opPLP}, 0x28: opcode{"PLP", 1, 4, modeImplicit, buildOpPull(regP)},
0x09: opcode{"ORA", 2, 2, modeImmediate, buildOpLogic(operationOr)}, 0x09: opcode{"ORA", 2, 2, modeImmediate, buildOpLogic(operationOr)},
0x05: opcode{"ORA", 2, 3, modeZeroPage, buildOpLogic(operationOr)}, 0x05: opcode{"ORA", 2, 3, modeZeroPage, buildOpLogic(operationOr)},

38
core6502/nmos6502_test.go Normal file
View File

@ -0,0 +1,38 @@
package core6502
import (
"fmt"
"testing"
)
func TestNMOS6502(t *testing.T) {
m := new(FlatMemory)
s := NewNMOS6502(m)
m.loadBinary("testdata/6502_functional_test.bin")
executeSuite(t, s, 0x200, 240, false, 255)
}
// To execute test suites from https://github.com/Klaus2m5/6502_65C02_functional_tests
func executeSuite(t *testing.T, s *State, stepAddress uint16, steps uint8, showStep bool, traceCPUStep uint8) {
s.reg.setPC(0x0400)
currentStep := uint8(255)
for true {
testCase := s.mem.Peek(stepAddress)
if testCase != currentStep {
currentStep = testCase
if showStep {
fmt.Printf("[ Step %d ]\n", testCase)
}
s.SetTrace(testCase == traceCPUStep)
}
if testCase >= steps {
break
}
pc := s.reg.getPC()
s.ExecuteInstruction()
if pc == s.reg.getPC() {
t.Fatalf("Failure in test %v.", testCase)
}
}
}

View File

@ -70,13 +70,37 @@ func buildOpUpdateFlag(flag uint8, value bool) opFunc {
} }
} }
func buildOpBranch(flag uint8, value bool) opFunc { func buildOpBranch(flag uint8, test bool) opFunc {
return func(s *State, line []uint8, opcode opcode) { return func(s *State, line []uint8, opcode opcode) {
if s.reg.getFlag(flag) == value { if s.reg.getFlag(flag) == test {
// This assumes that PC is already pointing to the next instruction address := resolveAddress(s, line, opcode)
pc := s.reg.getPC() s.reg.setPC(address)
pc += uint16(int8(line[1])) }
s.reg.setPC(pc) }
}
func buildOpBranchOnBit(bit uint8, test bool) opFunc {
return func(s *State, line []uint8, opcode opcode) {
// Note that those operations have two addressing modes:
// one for the zero page value, another for the relative jump.
// We will have to resolve the first one here.
value := s.mem.Peek(uint16(line[1]))
bitValue := ((value >> bit) & 1) == 1
if bitValue == test {
address := resolveAddress(s, line, opcode)
s.reg.setPC(address)
}
}
}
func buildOpSetBit(bit uint8, set bool) opFunc {
return func(s *State, line []uint8, opcode opcode) {
value, setValue := resolveGetSetValue(s, line, opcode)
if set {
setValue(value | (1 << bit))
} else {
setValue(value &^ (1 << bit))
} }
} }
} }
@ -84,10 +108,26 @@ func buildOpBranch(flag uint8, value bool) opFunc {
func opBIT(s *State, line []uint8, opcode opcode) { func opBIT(s *State, line []uint8, opcode opcode) {
value := resolveValue(s, line, opcode) value := resolveValue(s, line, opcode)
acc := s.reg.getA() acc := s.reg.getA()
// Future note: The immediate addressing mode (65C02 or 65816 only) does not affect V.
s.reg.updateFlag(flagZ, value&acc == 0) s.reg.updateFlag(flagZ, value&acc == 0)
s.reg.updateFlag(flagN, value&(1<<7) != 0) // The immediate addressing mode (65C02 or 65816 only) does not affect N & V.
s.reg.updateFlag(flagV, value&(1<<6) != 0) if opcode.addressMode != modeImmediate {
s.reg.updateFlag(flagN, value&(1<<7) != 0)
s.reg.updateFlag(flagV, value&(1<<6) != 0)
}
}
func opTRB(s *State, line []uint8, opcode opcode) {
value, setValue := resolveGetSetValue(s, line, opcode)
a := s.reg.getA()
s.reg.updateFlag(flagZ, (value&a) == 0)
setValue(value &^ a)
}
func opTSB(s *State, line []uint8, opcode opcode) {
value, setValue := resolveGetSetValue(s, line, opcode)
a := s.reg.getA()
s.reg.updateFlag(flagZ, (value&a) == 0)
setValue(value | a)
} }
func buildOpCompare(reg int) opFunc { func buildOpCompare(reg int) opFunc {
@ -140,6 +180,12 @@ func opADC(s *State, line []uint8, opcode opcode) {
s.reg.updateFlag(flagV, signedTotal < -128 || signedTotal > 127) s.reg.updateFlag(flagV, signedTotal < -128 || signedTotal > 127)
} }
func opADCAlt(s *State, line []uint8, opcode opcode) {
opADC(s, line, opcode)
// The Z and N flags on BCD are fixed in 65c02.
s.reg.updateFlagZN(s.reg.getA())
}
func opSBC(s *State, line []uint8, opcode opcode) { func opSBC(s *State, line []uint8, opcode opcode) {
value := resolveValue(s, line, opcode) value := resolveValue(s, line, opcode)
aValue := s.reg.getA() aValue := s.reg.getA()
@ -168,6 +214,12 @@ func opSBC(s *State, line []uint8, opcode opcode) {
s.reg.updateFlag(flagV, signedTotal < -128 || signedTotal > 127) s.reg.updateFlag(flagV, signedTotal < -128 || signedTotal > 127)
} }
func opSBCAlt(s *State, line []uint8, opcode opcode) {
opSBC(s, line, opcode)
// The Z and N flags on BCD are fixed in 65c02.
s.reg.updateFlagZN(s.reg.getA())
}
const stackAddress uint16 = 0x0100 const stackAddress uint16 = 0x0100
func pushByte(s *State, value uint8) { func pushByte(s *State, value uint8) {
@ -193,23 +245,24 @@ func pullWord(s *State) uint16 {
} }
func opPLA(s *State, line []uint8, opcode opcode) { func buildOpPull(regDst int) opFunc {
value := pullByte(s) return func(s *State, line []uint8, opcode opcode) {
s.reg.setA(value) value := pullByte(s)
s.reg.updateFlagZN(value) s.reg.setRegister(regDst, value)
if regDst != regP {
s.reg.updateFlagZN(value)
}
}
} }
func opPLP(s *State, line []uint8, opcode opcode) { func buildOpPush(regSrc int) opFunc {
value := pullByte(s) return func(s *State, line []uint8, opcode opcode) {
s.reg.setP(value) value := s.reg.getRegister(regSrc)
} if regSrc == regP {
value |= flagB + flag5
func opPHA(s *State, line []uint8, opcode opcode) { }
pushByte(s, s.reg.getA()) pushByte(s, value)
} }
func opPHP(s *State, line []uint8, opcode opcode) {
pushByte(s, s.reg.getP()|(flagB+flag5))
} }
func opJMP(s *State, line []uint8, opcode opcode) { func opJMP(s *State, line []uint8, opcode opcode) {
@ -240,3 +293,18 @@ func opBRK(s *State, line []uint8, opcode opcode) {
s.reg.setFlag(flagI) s.reg.setFlag(flagI)
s.reg.setPC(getWord(s.mem, vectorBreak)) s.reg.setPC(getWord(s.mem, vectorBreak))
} }
func opBRKAlt(s *State, line []uint8, opcode opcode) {
opBRK(s, line, opcode)
/*
The only difference in the BRK instruction on the 65C02 and the 6502
is that the 65C02 clears the D (decimal) flag on the 65C02, whereas
the D flag is not affected on the 6502.
*/
s.reg.clearFlag(flagD)
}
func opSTZ(s *State, line []uint8, opcode opcode) {
setValue := resolveSetValue(s, line, opcode)
setValue(0)
}

Binary file not shown.

File diff suppressed because it is too large Load Diff