From 4f963b0bd6f5384cae04068b51348543b4b5cbb2 Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Sun, 26 Sep 2021 01:22:08 +0200 Subject: [PATCH] Cycle count accurate per the Harte test suite --- core6502/addressing.go | 13 +++++++--- core6502/cmos65c02.go | 48 ++++++++++++++++++------------------- core6502/harteSuite_test.go | 13 ++++++---- core6502/nmos6502.go | 6 ++--- core6502/operations.go | 1 - 5 files changed, 45 insertions(+), 36 deletions(-) diff --git a/core6502/addressing.go b/core6502/addressing.go index 90b4405..4276e96 100644 --- a/core6502/addressing.go +++ b/core6502/addressing.go @@ -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: diff --git a/core6502/cmos65c02.go b/core6502/cmos65c02.go index 9a07801..41eedf5 100644 --- a/core6502/cmos65c02.go +++ b/core6502/cmos65c02.go @@ -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)}, diff --git a/core6502/harteSuite_test.go b/core6502/harteSuite_test.go index 7a5b9d9..ae2359e 100644 --- a/core6502/harteSuite_test.go +++ b/core6502/harteSuite_test.go @@ -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) { diff --git a/core6502/nmos6502.go b/core6502/nmos6502.go index 88b1ac1..1b8858c 100644 --- a/core6502/nmos6502.go +++ b/core6502/nmos6502.go @@ -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}, } diff --git a/core6502/operations.go b/core6502/operations.go index 0a53dd2..1cf94ab 100644 --- a/core6502/operations.go +++ b/core6502/operations.go @@ -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) }