From 7980b81ddbb2c3c5e538395f8d01d8b4c2cf1e39 Mon Sep 17 00:00:00 2001 From: Sidney Cadot Date: Tue, 24 Dec 2024 09:24:02 +0100 Subject: [PATCH] sim65: Fix instruction timings for 6502 and 65C02. This PR fixes all discrepancies of sim65 instruction timings, for both the 6502 and the 65C02 processors. The timings as implemented in this PR have been verified against actual hardware (Atari 800 XL for 6502; and WDC 65C02 for 65C02). These timings can also be verified against the 65x02 test suite. However, in this case, a single discrepancy arises; the 65x02 testsuite suggests that the 65C02 opcode 0x5c should take 4 clocks. However, tests on a hardware 65C02 have conclusively shown that this instruction takes 8 clock cycles. The 8 clock cycles duration for the 65C02 0xfc opcode is also confirmed by other sources, e.g. Section 9 of http://www.6502.org/tutorials/65c02opcodes.html. This test makes sim65 correct both in terms of functionality (all opcodes now do what they do on hardware) and in terms of timing (all instructions take as long as they would on real hardware). The one discrepancy that remains, is that on a real 6502/65C02, some instructions issue R or W cycles on the bus while the instruction processing is being done. Those spurious bus cycles are not replicated in sim65. Sim65 is thus an instruction-level simulator, rather than a bus-cycle level simulator. In other words, while the clock cycle counts for each instruction are now correct, not all clock cycles are individually simulated. --- src/sim65/6502.c | 98 ++++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/src/sim65/6502.c b/src/sim65/6502.c index b891bd3e8..6d83ef07b 100644 --- a/src/sim65/6502.c +++ b/src/sim65/6502.c @@ -579,6 +579,10 @@ static unsigned HaveIRQRequest; #define STO_CY_ZPINDY 6 #define STO_CY_ZPIND 5 +#define STO_CY_ABSX_NP STO_CY_ABSX +#define STO_CY_ABSY_NP STO_CY_ABSY +#define STO_CY_ZPINDY_NP STO_CY_ZPINDY + /* zp / zp,x / zp,y / abs / abs,x / abs,y / (zp,x) / (zp),y / (zp) */ #define STO_OP(mode, op) \ unsigned address; \ @@ -599,13 +603,11 @@ static unsigned HaveIRQRequest; /* Execution cycles for R-M-W opcodes */ #define RMW_CY_ZP 5 #define RMW_CY_ZPX 6 -#define RMW_CY_ZPY 6 #define RMW_CY_ABS 6 #define RMW_CY_ABSX 7 #define RMW_CY_ABSY 7 -#define RMW_CY_ZPXIND 6 -#define RMW_CY_ZPINDY 5 -#define RMW_CY_ZPIND 5 +#define RMW_CY_ZPXIND 8 +#define RMW_CY_ZPINDY 8 #define RMW_CY_ABSX_NP RMW_CY_ABSX #define RMW_CY_ABSY_NP RMW_CY_ABSY @@ -624,13 +626,16 @@ static unsigned HaveIRQRequest; /* Execution cycles for 2 x R-M-W opcodes */ #define RMW2_CY_ZP 5 #define RMW2_CY_ZPX 6 -#define RMW2_CY_ZPY 6 #define RMW2_CY_ABS 6 #define RMW2_CY_ABSX 7 #define RMW2_CY_ABSY 7 #define RMW2_CY_ZPXIND 8 #define RMW2_CY_ZPINDY 8 +#define RMW2_CY_ZPINDY_NP RMW2_CY_ZPINDY +#define RMW2_CY_ABSY_NP RMW2_CY_ABSY +#define RMW2_CY_ABSX_NP RMW2_CY_ABSX + /* zp / zp,x / zp,y / abs / abs,x / abs,y / (zp,x) / (zp),y */ #define ILLx2_OP(mode, op) \ unsigned address; \ @@ -1365,7 +1370,7 @@ static void OPC_65SC02_12 (void) static void OPC_6502_13 (void) /* Opcode $03: SLO (zp),y */ { - ILLx2_OP (ZPINDY, SLO); + ILLx2_OP (ZPINDY_NP, SLO); } @@ -1456,7 +1461,7 @@ static void OPC_65SC02_1A (void) static void OPC_6502_1B (void) /* Opcode $1B: SLO abs,y */ { - ILLx2_OP (ABSY, SLO); + ILLx2_OP (ABSY_NP, SLO); } @@ -1495,7 +1500,7 @@ static void OPC_6502_1D (void) static void OPC_6502_1E (void) /* Opcode $1E: ASL abs,x */ { - MEM_OP (ABSX, ASL); + MEM_OP (ABSX_NP, ASL); } @@ -1503,7 +1508,8 @@ static void OPC_6502_1E (void) static void OPC_65C02_1E (void) /* Opcode $1E: ASL abs,x */ { - MEM_OP (ABSX_NP, ASL); + MEM_OP (ABSX, ASL); + --Cycles; } @@ -1511,12 +1517,11 @@ static void OPC_65C02_1E (void) static void OPC_6502_1F (void) /* Opcode $1F: SLO abs,x */ { - ILLx2_OP (ABSX, SLO); + ILLx2_OP (ABSX_NP, SLO); } - static void OPC_65C02_1F (void) /* Opcode $1F: BBR1 zp, rel */ { @@ -1719,7 +1724,7 @@ static void OPC_65SC02_32 (void) static void OPC_6502_33 (void) /* Opcode $33: RLA (zp),y */ { - ILLx2_OP (ZPINDY, RLA); + ILLx2_OP (ZPINDY_NP, RLA); } @@ -1795,7 +1800,7 @@ static void OPC_65SC02_3A (void) static void OPC_6502_3B (void) /* Opcode $3B: RLA abs,y */ { - ILLx2_OP (ABSY, RLA); + ILLx2_OP (ABSY_NP, RLA); } @@ -1819,7 +1824,7 @@ static void OPC_6502_3D (void) static void OPC_6502_3E (void) /* Opcode $3E: ROL abs,x */ { - MEM_OP (ABSX, ROL); + MEM_OP (ABSX_NP, ROL); } @@ -1827,15 +1832,16 @@ static void OPC_6502_3E (void) static void OPC_65C02_3E (void) /* Opcode $3E: ROL abs,x */ { - MEM_OP (ABSX_NP, ROL); + MEM_OP (ABSX, ROL); + --Cycles; } static void OPC_6502_3F (void) -/* Opcode $3B: RLA abs,x */ +/* Opcode $3F: RLA abs,x */ { - ILLx2_OP (ABSX, RLA); + ILLx2_OP (ABSX_NP, RLA); } @@ -2015,7 +2021,7 @@ static void OPC_65SC02_52 (void) static void OPC_6502_53 (void) /* Opcode $43: SRE (zp),y */ { - ILLx2_OP (ZPINDY, SRE); + ILLx2_OP (ZPINDY_NP, SRE); } @@ -2083,7 +2089,7 @@ static void OPC_65SC02_5A (void) static void OPC_6502_5B (void) /* Opcode $5B: SRE abs,y */ { - ILLx2_OP (ABSY, SRE); + ILLx2_OP (ABSY_NP, SRE); } @@ -2091,6 +2097,14 @@ static void OPC_6502_5B (void) static void OPC_65C02_5C (void) /* Opcode $5C: 'Absolute' 8 cycle NOP */ { + /* This instruction takes 8 cycles, as per the following sources: + * + * - http://www.6502.org/tutorials/65c02opcodes.html + * - Tests on a WDC 65C02 in hardware. + * + * The 65x02 testsuite however claims that this instruction takes 4 cycles. + * See issue: https://github.com/SingleStepTests/65x02/issues/12 + */ Cycles = 8; Regs.PC += 3; } @@ -2108,7 +2122,7 @@ static void OPC_6502_5D (void) static void OPC_6502_5E (void) /* Opcode $5E: LSR abs,x */ { - MEM_OP (ABSX, LSR); + MEM_OP (ABSX_NP, LSR); } @@ -2116,7 +2130,8 @@ static void OPC_6502_5E (void) static void OPC_65C02_5E (void) /* Opcode $5E: LSR abs,x */ { - MEM_OP (ABSX_NP, LSR); + MEM_OP (ABSX, LSR); + --Cycles; } @@ -2124,7 +2139,7 @@ static void OPC_65C02_5E (void) static void OPC_6502_5F (void) /* Opcode $5F: SRE abs,x */ { - ILLx2_OP (ABSX, SRE); + ILLx2_OP (ABSX_NP, SRE); } @@ -2290,8 +2305,8 @@ static void OPC_6502_6C (void) static void OPC_65C02_6C (void) /* Opcode $6C: JMP (ind) */ { - /* 6502 bug fixed here */ - Cycles = 5; + /* The 6502 bug is fixed on the 65C02, at the cost of an extra cycle. */ + Cycles = 6; Regs.PC = MemReadWord (MemReadWord (Regs.PC+1)); ParaVirtHooks (&Regs); @@ -2374,7 +2389,7 @@ static void OPC_65C02_72 (void) static void OPC_6502_73 (void) /* Opcode $73: RRA (zp),y */ { - ILLx2_OP (ZPINDY, RRA); + ILLx2_OP (ZPINDY_NP, RRA); } @@ -2466,7 +2481,7 @@ static void OPC_65SC02_7A (void) static void OPC_6502_7B (void) /* Opcode $7B: RRA abs,y */ { - ILLx2_OP (ABSY, RRA); + ILLx2_OP (ABSY_NP, RRA); } @@ -2504,7 +2519,7 @@ static void OPC_65C02_7D (void) static void OPC_6502_7E (void) /* Opcode $7E: ROR abs,x */ { - MEM_OP (ABSX, ROR); + MEM_OP (ABSX_NP, ROR); } @@ -2512,7 +2527,8 @@ static void OPC_6502_7E (void) static void OPC_65C02_7E (void) /* Opcode $7E: ROR abs,x */ { - MEM_OP (ABSX_NP, ROR); + MEM_OP (ABSX, ROR); + --Cycles; } @@ -2520,7 +2536,7 @@ static void OPC_65C02_7E (void) static void OPC_6502_7F (void) /* Opcode $7F: RRA abs,x */ { - ILLx2_OP (ABSX, RRA); + ILLx2_OP (ABSX_NP, RRA); } @@ -2702,7 +2718,7 @@ static void OPC_6502_90 (void) static void OPC_6502_91 (void) /* Opcode $91: sta (zp),y */ { - STO_OP (ZPINDY, Regs.AC); + STO_OP (ZPINDY_NP, Regs.AC); } @@ -2791,7 +2807,7 @@ static void OPC_6502_98 (void) static void OPC_6502_99 (void) /* Opcode $99: STA abs,y */ { - STO_OP (ABSY, Regs.AC); + STO_OP (ABSY_NP, Regs.AC); } @@ -2858,7 +2874,7 @@ static void OPC_65SC02_9C (void) static void OPC_6502_9D (void) /* Opcode $9D: STA abs,x */ { - STO_OP (ABSX, Regs.AC); + STO_OP (ABSX_NP, Regs.AC); } @@ -2886,7 +2902,7 @@ static void OPC_6502_9E (void) static void OPC_65SC02_9E (void) /* Opcode $9E: STZ abs,x */ { - STO_OP (ABSX, 0); + STO_OP (ABSX_NP, 0); } @@ -3388,7 +3404,7 @@ static void OPC_65SC02_D2 (void) static void OPC_6502_D3 (void) /* Opcode $D3: DCP (zp),y */ { - MEM_OP (ZPINDY, DCP); + MEM_OP (ZPINDY_NP, DCP); } @@ -3456,7 +3472,7 @@ static void OPC_65SC02_DA (void) static void OPC_6502_DB (void) /* Opcode $DB: DCP abs,y */ { - MEM_OP (ABSY, DCP); + MEM_OP (ABSY_NP, DCP); } @@ -3472,7 +3488,7 @@ static void OPC_6502_DD (void) static void OPC_6502_DE (void) /* Opcode $DE: DEC abs,x */ { - MEM_OP (ABSX, DEC); + MEM_OP (ABSX_NP, DEC); } @@ -3480,7 +3496,7 @@ static void OPC_6502_DE (void) static void OPC_6502_DF (void) /* Opcode $DF: DCP abs,x */ { - MEM_OP (ABSX, DCP); + MEM_OP (ABSX_NP, DCP); } @@ -3735,7 +3751,7 @@ static void OPC_65C02_F2 (void) static void OPC_6502_F3 (void) /* Opcode $F3: ISC (zp),y */ { - MEM_OP (ZPINDY, ISC); + MEM_OP (ZPINDY_NP, ISC); } @@ -3821,7 +3837,7 @@ static void OPC_65SC02_FA (void) static void OPC_6502_FB (void) /* Opcode $FB: ISC abs,y */ { - MEM_OP (ABSY, ISC); + MEM_OP (ABSY_NP, ISC); } @@ -3845,7 +3861,7 @@ static void OPC_65C02_FD (void) static void OPC_6502_FE (void) /* Opcode $FE: INC abs,x */ { - MEM_OP (ABSX, INC); + MEM_OP (ABSX_NP, INC); } @@ -3853,7 +3869,7 @@ static void OPC_6502_FE (void) static void OPC_6502_FF (void) /* Opcode $FF: ISC abs,x */ { - MEM_OP (ABSX, ISC); + MEM_OP (ABSX_NP, ISC); }