Cycle count accurate per the Harte test suite
This commit is contained in:
parent
1a150d0f96
commit
4f963b0bd6
|
@ -14,6 +14,7 @@ const (
|
|||
modeRelative
|
||||
modeAbsolute
|
||||
modeAbsoluteX
|
||||
modeAbsoluteX65c02
|
||||
modeAbsoluteY
|
||||
modeIndirect
|
||||
modeIndexedIndirectX
|
||||
|
@ -64,8 +65,10 @@ func resolveSetValue(s *State, line []uint8, opcode opcode, value uint8) {
|
|||
s.mem.Poke(address, value)
|
||||
|
||||
// On writes, the possible extra cycle crossing page boundaries is
|
||||
// always added and already accounted for.
|
||||
s.extraCycleCrossingBoundaries = false
|
||||
// added and already accounted for on NMOS
|
||||
if opcode.addressMode != modeAbsoluteX65c02 {
|
||||
s.extraCycleCrossingBoundaries = false
|
||||
}
|
||||
}
|
||||
|
||||
func resolveAddress(s *State, line []uint8, opcode opcode) uint16 {
|
||||
|
@ -81,6 +84,8 @@ func resolveAddress(s *State, line []uint8, opcode opcode) uint16 {
|
|||
address = uint16(line[1] + s.reg.getY())
|
||||
case modeAbsolute:
|
||||
address = getWordInLine(line)
|
||||
case modeAbsoluteX65c02:
|
||||
fallthrough
|
||||
case modeAbsoluteX:
|
||||
base := getWordInLine(line)
|
||||
address, extraCycle = addOffset(base, s.reg.getX())
|
||||
|
@ -113,7 +118,7 @@ func resolveAddress(s *State, line []uint8, opcode opcode) uint16 {
|
|||
// Two addressing modes combined. We refer to the second one, relative,
|
||||
// placed one byte after the zeropage reference
|
||||
base := s.reg.getPC()
|
||||
address, extraCycle = addOffsetRelative(base, line[2])
|
||||
address, _ = addOffsetRelative(base, line[2])
|
||||
default:
|
||||
panic("Assert failed. Missing addressing mode")
|
||||
}
|
||||
|
@ -179,6 +184,8 @@ func lineString(line []uint8, opcode opcode) string {
|
|||
t += fmt.Sprintf(" *%+x", int8(line[1]))
|
||||
case modeAbsolute:
|
||||
t += fmt.Sprintf(" $%04x", getWordInLine(line))
|
||||
case modeAbsoluteX65c02:
|
||||
fallthrough
|
||||
case modeAbsoluteX:
|
||||
t += fmt.Sprintf(" $%04x,X", getWordInLine(line))
|
||||
case modeAbsoluteY:
|
||||
|
|
|
@ -32,7 +32,6 @@ func add65c02NOPs(opcodes *[256]opcode) {
|
|||
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
|
||||
|
@ -47,7 +46,7 @@ func add65c02NOPs(opcodes *[256]opcode) {
|
|||
opcodes[0xD4] = nop24
|
||||
opcodes[0xF4] = nop24
|
||||
|
||||
opcodes[0x5c] = nop38
|
||||
opcodes[0x5c] = nop34
|
||||
opcodes[0xdc] = nop34
|
||||
opcodes[0xfc] = nop34
|
||||
|
||||
|
@ -66,8 +65,7 @@ var opcodes65c02Delta = [256]opcode{
|
|||
// Functional difference
|
||||
0x00: {"BRK", 1, 7, modeImplicit, opBRKAlt},
|
||||
0x24: {"BIT", 2, 3, modeZeroPage, opBIT},
|
||||
0x2C: {"BIT", 3, 3, modeAbsolute, opBIT},
|
||||
0x6C: {"JMP", 3, 3, modeIndirect65c02Fix, opJMP},
|
||||
0x6C: {"JMP", 3, 6, modeIndirect65c02Fix, opJMP},
|
||||
|
||||
// Fixed BCD arithmetic flags
|
||||
0x69: {"ADC", 2, 2, modeImmediate, opADCAlt},
|
||||
|
@ -88,10 +86,10 @@ var opcodes65c02Delta = [256]opcode{
|
|||
0xF1: {"SBC", 2, 5, modeIndirectIndexedY, opSBCAlt}, // Extra cycles
|
||||
|
||||
// Different cycle count
|
||||
0x1e: {"ASL", 3, 6, modeAbsoluteX, buildOpShift(true, false)},
|
||||
0x3e: {"ROL", 3, 6, modeAbsoluteX, buildOpShift(true, true)},
|
||||
0x5e: {"LSR", 3, 6, modeAbsoluteX, buildOpShift(false, false)},
|
||||
0x7e: {"ROR", 3, 6, modeAbsoluteX, buildOpShift(false, true)},
|
||||
0x1e: {"ASL", 3, 6, modeAbsoluteX65c02, buildOpShift(true, false)},
|
||||
0x3e: {"ROL", 3, 6, modeAbsoluteX65c02, buildOpShift(true, true)},
|
||||
0x5e: {"LSR", 3, 6, modeAbsoluteX65c02, buildOpShift(false, false)},
|
||||
0x7e: {"ROR", 3, 6, modeAbsoluteX65c02, buildOpShift(false, true)},
|
||||
|
||||
// New indirect zero page addresssing mode
|
||||
0x12: {"ORA", 2, 5, modeIndirectZeroPage, buildOpLogic(operationOr)},
|
||||
|
@ -116,7 +114,7 @@ var opcodes65c02Delta = [256]opcode{
|
|||
0x5a: {"PHY", 1, 3, modeImplicit, buildOpPush(regY)},
|
||||
0xfa: {"PLX", 1, 4, modeImplicit, buildOpPull(regX)},
|
||||
0x7a: {"PLY", 1, 4, modeImplicit, buildOpPull(regY)},
|
||||
0x80: {"BRA", 2, 4, modeRelative, opJMP}, // Extra cycles
|
||||
0x80: {"BRA", 2, 3, modeRelative, opJMP}, // Extra cycles
|
||||
|
||||
0x64: {"STZ", 2, 3, modeZeroPage, opSTZ},
|
||||
0x74: {"STZ", 2, 4, modeZeroPageX, opSTZ},
|
||||
|
@ -131,22 +129,22 @@ var opcodes65c02Delta = [256]opcode{
|
|||
|
||||
// Additional in Rockwell 65c02 and WDC 65c02?
|
||||
// They have a double addressing mode: zeropage and relative.
|
||||
0x0f: {"BBR0", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(0, false)}, // Extra cycles
|
||||
0x1f: {"BBR1", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(1, false)}, // Extra cycles
|
||||
0x2f: {"BBR2", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(2, false)}, // Extra cycles
|
||||
0x3f: {"BBR3", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(3, false)}, // Extra cycles
|
||||
0x4f: {"BBR4", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(4, false)}, // Extra cycles
|
||||
0x5f: {"BBR5", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(5, false)}, // Extra cycles
|
||||
0x6f: {"BBR6", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(6, false)}, // Extra cycles
|
||||
0x7f: {"BBR7", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(7, false)}, // Extra cycles
|
||||
0x8f: {"BBS0", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(0, true)}, // Extra cycles
|
||||
0x9f: {"BBS1", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(1, true)}, // Extra cycles
|
||||
0xaf: {"BBS2", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(2, true)}, // Extra cycles
|
||||
0xbf: {"BBS3", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(3, true)}, // Extra cycles
|
||||
0xcf: {"BBS4", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(4, true)}, // Extra cycles
|
||||
0xdf: {"BBS5", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(5, true)}, // Extra cycles
|
||||
0xef: {"BBS6", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(6, true)}, // Extra cycles
|
||||
0xff: {"BBS7", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(7, true)}, // Extra cycles
|
||||
0x0f: {"BBR0", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(0, false)}, // Extra cycles
|
||||
0x1f: {"BBR1", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(1, false)}, // Extra cycles
|
||||
0x2f: {"BBR2", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(2, false)}, // Extra cycles
|
||||
0x3f: {"BBR3", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(3, false)}, // Extra cycles
|
||||
0x4f: {"BBR4", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(4, false)}, // Extra cycles
|
||||
0x5f: {"BBR5", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(5, false)}, // Extra cycles
|
||||
0x6f: {"BBR6", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(6, false)}, // Extra cycles
|
||||
0x7f: {"BBR7", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(7, false)}, // Extra cycles
|
||||
0x8f: {"BBS0", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(0, true)}, // Extra cycles
|
||||
0x9f: {"BBS1", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(1, true)}, // Extra cycles
|
||||
0xaf: {"BBS2", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(2, true)}, // Extra cycles
|
||||
0xbf: {"BBS3", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(3, true)}, // Extra cycles
|
||||
0xcf: {"BBS4", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(4, true)}, // Extra cycles
|
||||
0xdf: {"BBS5", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(5, true)}, // Extra cycles
|
||||
0xef: {"BBS6", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(6, true)}, // Extra cycles
|
||||
0xff: {"BBS7", 3, 6, modeZeroPageAndRelative, buildOpBranchOnBit(7, true)}, // Extra cycles
|
||||
|
||||
0x07: {"RMB0", 2, 5, modeZeroPage, buildOpSetBit(0, false)},
|
||||
0x17: {"RMB1", 2, 5, modeZeroPage, buildOpSetBit(1, false)},
|
||||
|
|
|
@ -34,7 +34,7 @@ func TestHarteNMOS6502(t *testing.T) {
|
|||
s := NewNMOS6502(nil) // Use to get the opcodes names
|
||||
|
||||
path := "/home/casa/code/ProcessorTests/6502/v1/"
|
||||
for i := 0x00; i <= 0xff; /*0xff*/ i++ {
|
||||
for i := 0x00; i <= 0xff; i++ {
|
||||
if s.opcodes[i].name != "ADC" && // Issue with ADC crossing page boundaries
|
||||
s.opcodes[i].name != "SBC" && // Issue with ADC crossing page boundaries
|
||||
s.opcodes[i].name != "" {
|
||||
|
@ -56,10 +56,9 @@ func TestHarteCMOS65c02(t *testing.T) {
|
|||
s := NewCMOS65c02(nil) // Use to get the opcodes names
|
||||
|
||||
path := "/home/casa/code/ProcessorTests/wdc65c02/v1/"
|
||||
for i := 0x00; i <= 0xff; /*0xff*/ i++ {
|
||||
for i := 0x00; i <= 0xff; i++ {
|
||||
if s.opcodes[i].name != "ADC" && // Issue with ADC crossing page boundaries
|
||||
s.opcodes[i].name != "SBC" && // Issue with SBC crossing page boundaries
|
||||
s.opcodes[i].name != "" {
|
||||
s.opcodes[i].name != "SBC" { // Issue with SBC crossing page boundaries
|
||||
|
||||
opcode := fmt.Sprintf("%02x", i)
|
||||
t.Run(opcode+s.opcodes[i].name, func(t *testing.T) {
|
||||
|
@ -99,6 +98,7 @@ func testOpcode(t *testing.T, s *State, path string, opcode string) {
|
|||
|
||||
func testScenario(t *testing.T, s *State, sc *scenario) {
|
||||
// Setup CPU
|
||||
start := s.GetCycles()
|
||||
s.reg.setPC(sc.Initial.Pc)
|
||||
s.reg.setSP(sc.Initial.S)
|
||||
s.reg.setA(sc.Initial.A)
|
||||
|
@ -119,6 +119,11 @@ func testScenario(t *testing.T, s *State, sc *scenario) {
|
|||
assertFlags(t, sc, sc.Initial.P, s.reg.getP(), sc.Final.P)
|
||||
assertReg8(t, sc, "SP", s.reg.getSP(), sc.Final.S)
|
||||
assertReg16(t, sc, "PC", s.reg.getPC(), sc.Final.Pc)
|
||||
|
||||
cycles := s.GetCycles() - start
|
||||
if cycles != uint64(len(sc.Cycles)) {
|
||||
t.Errorf("Took %v cycles, it should be %v for %+v", cycles, len(sc.Cycles), sc)
|
||||
}
|
||||
}
|
||||
|
||||
func assertReg8(t *testing.T, sc *scenario, name string, actual uint8, wanted uint8) {
|
||||
|
|
|
@ -11,7 +11,7 @@ func NewNMOS6502(m Memory) *State {
|
|||
var opcodesNMOS6502 = [256]opcode{
|
||||
0x00: {"BRK", 1, 7, modeImplicit, opBRK},
|
||||
0x4C: {"JMP", 3, 3, modeAbsolute, opJMP},
|
||||
0x6C: {"JMP", 3, 3, modeIndirect, opJMP},
|
||||
0x6C: {"JMP", 3, 5, modeIndirect, opJMP},
|
||||
0x20: {"JSR", 3, 6, modeAbsolute, opJSR},
|
||||
0x40: {"RTI", 1, 6, modeImplicit, opRTI},
|
||||
0x60: {"RTS", 1, 6, modeImplicit, opRTS},
|
||||
|
@ -67,7 +67,7 @@ var opcodesNMOS6502 = [256]opcode{
|
|||
0xF1: {"SBC", 2, 5, modeIndirectIndexedY, opSBC}, // Extra cycles
|
||||
|
||||
0x24: {"BIT", 2, 3, modeZeroPage, opBIT},
|
||||
0x2C: {"BIT", 3, 3, modeAbsolute, opBIT},
|
||||
0x2C: {"BIT", 3, 4, modeAbsolute, opBIT},
|
||||
|
||||
0xC9: {"CMP", 2, 2, modeImmediate, buildOpCompare(regA)},
|
||||
0xC5: {"CMP", 2, 3, modeZeroPage, buildOpCompare(regA)},
|
||||
|
@ -185,5 +185,5 @@ var opcodesNMOS6502 = [256]opcode{
|
|||
// Undocumented opcodes, see http://bbc.nvg.org/doc/6502OpList.txt
|
||||
0x1A: {"NOP", 1, 2, modeImplicit, opNOP}, // INC A in the 65c02
|
||||
0xC2: {"NOP", 2, 2, modeImplicit, opNOP},
|
||||
0x02: {"NOP", 1, 1, modeImplicit, opHALT},
|
||||
0x02: {"NOP", 1, 3, modeImplicit, opHALT},
|
||||
}
|
||||
|
|
|
@ -92,7 +92,6 @@ func buildOpBranchOnBit(bit uint8, test bool) opFunc {
|
|||
bitValue := ((value >> bit) & 1) == 1
|
||||
|
||||
if bitValue == test {
|
||||
s.extraCycleBranchTaken = true
|
||||
address := resolveAddress(s, line, opcode)
|
||||
s.reg.setPC(address)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue