From d064ca424fe2aaea19725ac2cd32d40100ff23c9 Mon Sep 17 00:00:00 2001 From: Sidney Cadot Date: Sun, 22 Dec 2024 19:15:20 +0100 Subject: [PATCH 1/2] sim65: implemented missing 65C02 instructions This PR implements support for 32 65C02-specific instructions to sim65: BBRx, BBSx, RMBx, SMBx, with x = 0..7. These instructions are implemented using two macros: * The "ZP_BITOP" macro implements the RMBx and SMBx isntructions. * The "ZP_BIT_BRANCH" macro implements the BBRx abd BBSx instructions. The implementation of these instructions has been verified usingthe 65x02 test suite. --- src/sim65/6502.c | 386 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 341 insertions(+), 45 deletions(-) diff --git a/src/sim65/6502.c b/src/sim65/6502.c index bfb3f3e9b..1c915e618 100644 --- a/src/sim65/6502.c +++ b/src/sim65/6502.c @@ -35,9 +35,8 @@ /*****************************************************************************/ /* Known bugs and limitations of the 65C02 simulation: - * support currently only on the level of 65SC02: - BBRx, BBSx, RMBx, SMBx, WAI, and STP are unsupported -*/ + * the WAI ($CB) and STP ($DB) instructions are unsupported. + */ #include #include @@ -623,14 +622,14 @@ static unsigned HaveIRQRequest; /* 2 x Read-Modify-Write opcode helpers (illegal opcodes) */ /* Execution cycles for 2 x R-M-W opcodes */ -#define RMW2_CY_ZP 5 -#define RMW2_CY_ZPX 6 +#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_ABS 6 +#define RMW2_CY_ABSX 7 +#define RMW2_CY_ABSY 7 +#define RMW2_CY_ZPXIND 8 +#define RMW2_CY_ZPINDY 8 /* zp / zp,x / zp,y / abs / abs,x / abs,y / (zp,x) / (zp),y */ #define ILLx2_OP(mode, op) \ @@ -1112,6 +1111,46 @@ static unsigned HaveIRQRequest; +/* Set/reset a specific bit in a zero-page byte. This macro + * macro is used to implement the 65C02 RMBx and SMBx instructions. + */ +#define ZP_BITOP(bitnr, bitval) \ + do { \ + uint8_t zp_address = MemReadByte (Regs.PC + 1); \ + uint8_t zp_value = MemReadByte (zp_address); \ + if (bitval) { \ + zp_value |= (1 << bitnr); \ + } else { \ + zp_value &= ~(1 << bitnr); \ + } \ + MemWriteByte (zp_address, zp_value); \ + Regs.PC += 2; \ + Cycles = 5; \ + } while (0) + +/* Branch depending on the state of a specific bit of a zero page + * address. This macro is used to implement the 65C02 BBRx and + * BBSx instructions. + */ +#define ZP_BIT_BRANCH(bitnr, bitval) \ + do { \ + uint8_t zp_address = MemReadByte (Regs.PC + 1); \ + uint8_t zp_value = MemReadByte (zp_address); \ + int displacement = (int8_t)MemReadByte (Regs.PC + 2); \ + if (((zp_value & (1 << bitnr)) != 0) == bitval) { \ + Regs.PC += 3; \ + uint8_t OldPCH = PCH; \ + Regs.PC += displacement; \ + Cycles = 6; \ + if (PCH != OldPCH) { \ + Cycles += 1; \ + } \ + } else { \ + Regs.PC += 3; \ + Cycles = 5; \ + } \ + } while (0) + /*****************************************************************************/ /* Opcode handling functions */ /*****************************************************************************/ @@ -1204,6 +1243,14 @@ static void OPC_6502_07 (void) +static void OPC_65C02_07 (void) +/* Opcode $07: RMB0 zp */ +{ + ZP_BITOP(0, 0); +} + + + static void OPC_6502_08 (void) /* Opcode $08: PHP */ { @@ -1283,6 +1330,14 @@ static void OPC_6502_0F (void) +static void OPC_65C02_0F (void) +/* Opcode $0F: BBR0 zp, rel */ +{ + ZP_BIT_BRANCH (0, 0); +} + + + static void OPC_6502_10 (void) /* Opcode $10: BPL */ { @@ -1362,6 +1417,14 @@ static void OPC_6502_17 (void) +static void OPC_65C02_17 (void) +/* Opcode $17: RMB1 zp */ +{ + ZP_BITOP(1, 0); +} + + + static void OPC_6502_18 (void) /* Opcode $18: CLC */ { @@ -1453,6 +1516,15 @@ static void OPC_6502_1F (void) + +static void OPC_65C02_1F (void) +/* Opcode $1F: BBR1 zp, rel */ +{ + ZP_BIT_BRANCH (1, 0); +} + + + static void OPC_6502_20 (void) /* Opcode $20: JSR */ { @@ -1541,6 +1613,14 @@ static void OPC_6502_27 (void) +static void OPC_65C02_27 (void) +/* Opcode $27: RMB2 zp */ +{ + ZP_BITOP(2, 0); +} + + + static void OPC_6502_28 (void) /* Opcode $28: PLP */ { @@ -1604,6 +1684,14 @@ static void OPC_6502_2F (void) +static void OPC_65C02_2F (void) +/* Opcode $2F: BBR2 zp, rel */ +{ + ZP_BIT_BRANCH (2, 0); +} + + + static void OPC_6502_30 (void) /* Opcode $30: BMI */ { @@ -1668,6 +1756,14 @@ static void OPC_6502_37 (void) +static void OPC_65C02_37 (void) +/* Opcode $37: RMB3 zp */ +{ + ZP_BITOP(3, 0); +} + + + static void OPC_6502_38 (void) /* Opcode $38: SEC */ { @@ -1744,6 +1840,14 @@ static void OPC_6502_3F (void) +static void OPC_65C02_3F (void) +/* Opcode $3F: BBR3 zp, rel */ +{ + ZP_BIT_BRANCH (3, 0); +} + + + static void OPC_6502_40 (void) /* Opcode $40: RTI */ { @@ -1797,6 +1901,14 @@ static void OPC_6502_47 (void) +static void OPC_65C02_47 (void) +/* Opcode $47: RMB4 zp */ +{ + ZP_BITOP(4, 0); +} + + + static void OPC_6502_48 (void) /* Opcode $48: PHA */ { @@ -1868,6 +1980,14 @@ static void OPC_6502_4F (void) +static void OPC_65C02_4F (void) +/* Opcode $4F: BBR4 zp, rel */ +{ + ZP_BIT_BRANCH (4, 0); +} + + + static void OPC_6502_50 (void) /* Opcode $50: BVC */ { @@ -1924,6 +2044,14 @@ static void OPC_6502_57 (void) +static void OPC_65C02_57 (void) +/* Opcode $57: RMB5 zp */ +{ + ZP_BITOP(5, 0); +} + + + static void OPC_6502_58 (void) /* Opcode $58: CLI */ { @@ -2001,6 +2129,14 @@ static void OPC_6502_5F (void) +static void OPC_65C02_5F (void) +/* Opcode $5F: BBR5 zp, rel */ +{ + ZP_BIT_BRANCH (5, 0); +} + + + static void OPC_6502_60 (void) /* Opcode $60: RTS */ { @@ -2074,6 +2210,14 @@ static void OPC_6502_67 (void) +static void OPC_65C02_67 (void) +/* Opcode $67: RMB6 zp */ +{ + ZP_BITOP(6, 0); +} + + + static void OPC_6502_68 (void) /* Opcode $68: PLA */ { @@ -2187,6 +2331,14 @@ static void OPC_6502_6F (void) +static void OPC_65C02_6F (void) +/* Opcode $6F: BBR6 zp, rel */ +{ + ZP_BIT_BRANCH (6, 0); +} + + + static void OPC_6502_70 (void) /* Opcode $70: BVS */ { @@ -2265,6 +2417,14 @@ static void OPC_6502_77 (void) +static void OPC_65C02_77 (void) +/* Opcode $77: RMB7 zp */ +{ + ZP_BITOP(7, 0); +} + + + static void OPC_6502_78 (void) /* Opcode $78: SEI */ { @@ -2365,6 +2525,14 @@ static void OPC_6502_7F (void) +static void OPC_65C02_7F (void) +/* Opcode $7F: BBR7 zp, rel */ +{ + ZP_BIT_BRANCH (7, 0); +} + + + /* Aliases of opcode $80 */ #define OPC_6502_82 OPC_6502_80 #define OPC_6502_C2 OPC_6502_80 @@ -2435,6 +2603,14 @@ static void OPC_6502_87 (void) +static void OPC_65C02_87 (void) +/* Opcode $87: SMB0 zp */ +{ + ZP_BITOP(0, 1); +} + + + static void OPC_6502_88 (void) /* Opcode $88: DEY */ { @@ -2507,6 +2683,14 @@ static void OPC_6502_8F (void) +static void OPC_65C02_8F (void) +/* Opcode $8F: BBS0 zp, rel */ +{ + ZP_BIT_BRANCH (0, 1); +} + + + static void OPC_6502_90 (void) /* Opcode $90: BCC */ { @@ -2571,6 +2755,14 @@ static void OPC_6502_97 (void) +static void OPC_65C02_97 (void) +/* Opcode $97: SMB1 zp */ +{ + ZP_BITOP(1, 1); +} + + + static void OPC_6502_98 (void) /* Opcode $98: TYA */ { @@ -2641,6 +2833,14 @@ static void OPC_6502_9E (void) +static void OPC_65SC02_9E (void) +/* Opcode $9E: STZ abs,x */ +{ + STO_OP (ABSX, 0); +} + + + static void OPC_6502_9F (void) /* Opcode $9F: SHA abs,y */ { @@ -2649,10 +2849,10 @@ static void OPC_6502_9F (void) -static void OPC_65SC02_9E (void) -/* Opcode $9E: STZ abs,x */ +static void OPC_65C02_9F (void) +/* Opcode $9F: BBS1 zp, rel */ { - STO_OP (ABSX, 0); + ZP_BIT_BRANCH (1, 1); } @@ -2721,6 +2921,14 @@ static void OPC_6502_A7 (void) +static void OPC_65C02_A7 (void) +/* Opcode $A7: SMB2 zp */ +{ + ZP_BITOP(2, 1); +} + + + static void OPC_6502_A8 (void) /* Opcode $A8: TAY */ { @@ -2793,6 +3001,14 @@ static void OPC_6502_AF (void) +static void OPC_65C02_AF (void) +/* Opcode $AF: BBS2 zp, rel */ +{ + ZP_BIT_BRANCH (2, 1); +} + + + static void OPC_6502_B0 (void) /* Opcode $B0: BCS */ { @@ -2857,6 +3073,14 @@ static void OPC_6502_B7 (void) +static void OPC_65C02_B7 (void) +/* Opcode $B7: SMB3 zp */ +{ + ZP_BITOP(3, 1); +} + + + static void OPC_6502_B8 (void) /* Opcode $B8: CLV */ { @@ -2927,6 +3151,14 @@ static void OPC_6502_BF (void) +static void OPC_65C02_BF (void) +/* Opcode $BF: BBS3 zp, rel */ +{ + ZP_BIT_BRANCH (3, 1); +} + + + static void OPC_6502_C0 (void) /* Opcode $C0: CPY #imm */ { @@ -2983,6 +3215,14 @@ static void OPC_6502_C7 (void) +static void OPC_65C02_C7 (void) +/* Opcode $C7: SMB4 zp */ +{ + ZP_BITOP(4, 1); +} + + + static void OPC_6502_C8 (void) /* Opcode $C8: INY */ { @@ -3051,6 +3291,14 @@ static void OPC_6502_CF (void) +static void OPC_65C02_CF (void) +/* Opcode $CF: BBS4 zp, rel */ +{ + ZP_BIT_BRANCH (4, 1); +} + + + static void OPC_6502_D0 (void) /* Opcode $D0: BNE */ { @@ -3107,6 +3355,14 @@ static void OPC_6502_D7 (void) +static void OPC_65C02_D7 (void) +/* Opcode $D7: SMB5 zp */ +{ + ZP_BITOP(5, 1); +} + + + static void OPC_6502_D8 (void) /* Opcode $D8: CLD */ { @@ -3167,6 +3423,14 @@ static void OPC_6502_DF (void) +static void OPC_65C02_DF (void) +/* Opcode $DF: BBS5 zp, rel */ +{ + ZP_BIT_BRANCH (5, 1); +} + + + static void OPC_6502_E0 (void) /* Opcode $E0: CPX #imm */ { @@ -3237,6 +3501,14 @@ static void OPC_6502_E7 (void) +static void OPC_65C02_E7 (void) +/* Opcode $E7: SMB6 zp */ +{ + ZP_BITOP(6, 1); +} + + + static void OPC_6502_E8 (void) /* Opcode $E8: INX */ { @@ -3357,6 +3629,14 @@ static void OPC_6502_EF (void) +static void OPC_65C02_EF (void) +/* Opcode $EF: BBS6 zp, rel */ +{ + ZP_BIT_BRANCH (6, 1); +} + + + static void OPC_6502_F0 (void) /* Opcode $F0: BEQ */ { @@ -3430,6 +3710,14 @@ static void OPC_6502_F7 (void) +static void OPC_65C02_F7 (void) +/* Opcode $F7: SMB7 zp */ +{ + ZP_BITOP(7, 1); +} + + + static void OPC_6502_F8 (void) /* Opcode $F8: SED */ { @@ -3508,6 +3796,14 @@ static void OPC_6502_FF (void) +static void OPC_65C02_FF (void) +/* Opcode $FF: BBS7 zp, rel */ +{ + ZP_BIT_BRANCH (7, 1); +} + + + /*****************************************************************************/ /* Opcode handler tables */ /*****************************************************************************/ @@ -4047,7 +4343,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_04, OPC_6502_05, OPC_6502_06, - OPC_Illegal, // $07: RMB0 currently unsupported + OPC_65C02_07, OPC_6502_08, OPC_6502_09, OPC_6502_0A, @@ -4055,7 +4351,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_0C, OPC_6502_0D, OPC_6502_0E, - OPC_Illegal, // $0F: BBR0 currently unsupported + OPC_65C02_0F, OPC_6502_10, OPC_6502_11, OPC_65SC02_12, @@ -4063,7 +4359,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_14, OPC_6502_15, OPC_6502_16, - OPC_Illegal, // $17: RMB1 currently unsupported + OPC_65C02_17, OPC_6502_18, OPC_6502_19, OPC_65SC02_1A, @@ -4071,7 +4367,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_1C, OPC_6502_1D, OPC_65C02_1E, - OPC_Illegal, // $1F: BBR1 currently unsupported + OPC_65C02_1F, OPC_6502_20, OPC_6502_21, OPC_65C02_NOP22, // $22 @@ -4079,7 +4375,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_24, OPC_6502_25, OPC_6502_26, - OPC_Illegal, // $27: RMB2 currently unsupported + OPC_65C02_27, OPC_6502_28, OPC_6502_29, OPC_6502_2A, @@ -4087,7 +4383,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_2C, OPC_6502_2D, OPC_6502_2E, - OPC_Illegal, // $2F: BBR2 currently unsupported + OPC_65C02_2F, OPC_6502_30, OPC_6502_31, OPC_65SC02_32, @@ -4095,7 +4391,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_34, OPC_6502_35, OPC_6502_36, - OPC_Illegal, // $37: RMB3 currently unsupported + OPC_65C02_37, OPC_6502_38, OPC_6502_39, OPC_65SC02_3A, @@ -4103,7 +4399,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_3C, OPC_6502_3D, OPC_65C02_3E, - OPC_Illegal, // $3F: BBR3 currently unsupported + OPC_65C02_3F, OPC_6502_40, OPC_6502_41, OPC_65C02_NOP22, // $42 @@ -4111,7 +4407,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_44, // $44 OPC_6502_45, OPC_6502_46, - OPC_Illegal, // $47: RMB4 currently unsupported + OPC_65C02_47, OPC_6502_48, OPC_6502_49, OPC_6502_4A, @@ -4119,7 +4415,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_4C, OPC_6502_4D, OPC_6502_4E, - OPC_Illegal, // $4F: BBR4 currently unsupported + OPC_65C02_4F, OPC_6502_50, OPC_6502_51, OPC_65SC02_52, @@ -4127,7 +4423,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65C02_NOP24, // $54 OPC_6502_55, OPC_6502_56, - OPC_Illegal, // $57: RMB5 currently unsupported + OPC_65C02_57, OPC_6502_58, OPC_6502_59, OPC_65SC02_5A, @@ -4135,7 +4431,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65C02_5C, OPC_6502_5D, OPC_65C02_5E, - OPC_Illegal, // $5F: BBR5 currently unsupported + OPC_65C02_5F, OPC_6502_60, OPC_65C02_61, OPC_65C02_NOP22, // $62 @@ -4143,7 +4439,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_64, OPC_65C02_65, OPC_6502_66, - OPC_Illegal, // $67: RMB6 currently unsupported + OPC_65C02_67, OPC_6502_68, OPC_65C02_69, OPC_6502_6A, @@ -4151,7 +4447,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65C02_6C, OPC_65C02_6D, OPC_6502_6E, - OPC_Illegal, // $6F: BBR6 currently unsupported + OPC_65C02_6F, OPC_6502_70, OPC_65C02_71, OPC_65C02_72, @@ -4159,7 +4455,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_74, OPC_65C02_75, OPC_6502_76, - OPC_Illegal, // $77: RMB7 currently unsupported + OPC_65C02_77, OPC_6502_78, OPC_65C02_79, OPC_65SC02_7A, @@ -4167,7 +4463,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_7C, OPC_65C02_7D, OPC_65C02_7E, - OPC_Illegal, // $7F: BBR7 currently unsupported + OPC_65C02_7F, OPC_65SC02_80, OPC_6502_81, OPC_65C02_NOP22, // $82 @@ -4175,7 +4471,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_84, OPC_6502_85, OPC_6502_86, - OPC_Illegal, // $87: SMB0 currently unsupported + OPC_65C02_87, OPC_6502_88, OPC_65SC02_89, OPC_6502_8A, @@ -4183,7 +4479,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_8C, OPC_6502_8D, OPC_6502_8E, - OPC_Illegal, // $8F: BBS0 currently unsupported + OPC_65C02_8F, OPC_6502_90, OPC_6502_91, OPC_65SC02_92, @@ -4191,7 +4487,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_94, OPC_6502_95, OPC_6502_96, - OPC_Illegal, // $97: SMB1 currently unsupported + OPC_65C02_97, OPC_6502_98, OPC_6502_99, OPC_6502_9A, @@ -4199,7 +4495,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65SC02_9C, OPC_6502_9D, OPC_65SC02_9E, - OPC_Illegal, // $9F: BBS1 currently unsupported + OPC_65C02_9F, OPC_6502_A0, OPC_6502_A1, OPC_6502_A2, @@ -4207,7 +4503,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_A4, OPC_6502_A5, OPC_6502_A6, - OPC_Illegal, // $A7: SMB2 currently unsupported + OPC_65C02_A7, OPC_6502_A8, OPC_6502_A9, OPC_6502_AA, @@ -4215,7 +4511,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_AC, OPC_6502_AD, OPC_6502_AE, - OPC_Illegal, // $AF: BBS2 currently unsupported + OPC_65C02_AF, OPC_6502_B0, OPC_6502_B1, OPC_65SC02_B2, @@ -4223,7 +4519,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_B4, OPC_6502_B5, OPC_6502_B6, - OPC_Illegal, // $B7: SMB3 currently unsupported + OPC_65C02_B7, OPC_6502_B8, OPC_6502_B9, OPC_6502_BA, @@ -4231,7 +4527,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_BC, OPC_6502_BD, OPC_6502_BE, - OPC_Illegal, // $BF: BBS3 currently unsupported + OPC_65C02_BF, OPC_6502_C0, OPC_6502_C1, OPC_65C02_NOP22, // $C2 @@ -4239,7 +4535,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_C4, OPC_6502_C5, OPC_6502_C6, - OPC_Illegal, // $C7: SMB4 currently unsupported + OPC_65C02_C7, OPC_6502_C8, OPC_6502_C9, OPC_6502_CA, @@ -4247,7 +4543,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_CC, OPC_6502_CD, OPC_6502_CE, - OPC_Illegal, // $CF: BBS4 currently unsupported + OPC_65C02_CF, OPC_6502_D0, OPC_6502_D1, OPC_65SC02_D2, @@ -4255,7 +4551,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65C02_NOP24, // $D4 OPC_6502_D5, OPC_6502_D6, - OPC_Illegal, // $D7: SMB5 currently unsupported + OPC_65C02_D7, OPC_6502_D8, OPC_6502_D9, OPC_65SC02_DA, @@ -4263,7 +4559,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65C02_NOP34, // $DC OPC_6502_DD, OPC_6502_DE, - OPC_Illegal, // $DF: BBS5 currently unsupported + OPC_65C02_DF, OPC_6502_E0, OPC_65C02_E1, OPC_65C02_NOP22, // $E2 @@ -4271,7 +4567,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_E4, OPC_65C02_E5, OPC_6502_E6, - OPC_Illegal, // $E7: SMB6 currently unsupported + OPC_65C02_E7, OPC_6502_E8, OPC_65C02_E9, OPC_6502_EA, @@ -4279,7 +4575,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_EC, OPC_65C02_ED, OPC_6502_EE, - OPC_Illegal, // $EF: BBS6 currently unsupported + OPC_65C02_EF, OPC_6502_F0, OPC_65C02_F1, OPC_65C02_F2, @@ -4287,7 +4583,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65C02_NOP24, // $F4 OPC_65C02_F5, OPC_6502_F6, - OPC_Illegal, // $F7: SMB7 currently unsupported + OPC_65C02_F7, OPC_6502_F8, OPC_65C02_F9, OPC_65SC02_FA, @@ -4295,7 +4591,7 @@ static const OPFunc OP65C02Table[256] = { OPC_65C02_NOP34, // $FC OPC_65C02_FD, OPC_6502_FE, - OPC_Illegal, // $FF: BBS7 currently unsupported + OPC_65C02_FF }; From fbf3bde97c1e4d33df72d50db9ff8004afcfa83b Mon Sep 17 00:00:00 2001 From: Sidney Cadot Date: Sun, 22 Dec 2024 19:48:41 +0100 Subject: [PATCH 2/2] sim65: properly implement 5 'illegal' 6502X opcodes. This PR fixes the implementation of 5 illegal opcodes in the 6502, which the 6502X supports: * $93 SHA (zp),y * $9B TAS abs,y * $9C SHY abs,x * $9E SHX abs,x * $9F SHA abs,y The common denominator of the previous implementation was that it didn't correctly handle the case when the Y or X indexing induced a page crossing. In those cases, the effective address calculation of the instructions becomes truly messed up (with the high byte of the address equal to the value being written). The correctness of the implementations in this PR was verified using the 65x02 test suite, and corresponds to a (detailed) reading of the "No More Secrets" document. Stylistically, there is room for improvement in these implementations, specifically in factoring out common behavior into macros. However, for now the "explicit" coding style will suffice. It is clear enough, and we want to reach a situation soon where the sim65 code is able to pass the full '65x02' testsuite. Once we get to that point, we can refactor this code with a lot more confidence, since we will have the benefit of a working exhaustive test to make sure we don't break stuff. --- src/sim65/6502.c | 72 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/src/sim65/6502.c b/src/sim65/6502.c index bfb3f3e9b..fe3489d85 100644 --- a/src/sim65/6502.c +++ b/src/sim65/6502.c @@ -2534,7 +2534,20 @@ static void OPC_65SC02_92 (void) static void OPC_6502_93 (void) /* Opcode $93: SHA (zp),y */ { - STO_CB (ZPINDY, SHA); + ++Regs.PC; + uint8_t zp_ptr_lo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t zp_ptr_hi = zp_ptr_lo + 1; + uint8_t baselo = MemReadByte(zp_ptr_lo); + uint8_t basehi = MemReadByte(zp_ptr_hi); + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.AC & Regs.XR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.YR); + bool pagecross = (baselo + Regs.YR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Cycles=6; } @@ -2604,7 +2617,20 @@ static void OPC_6502_9A (void) static void OPC_6502_9B (void) /* Opcode $9B: TAS abs,y */ { - STO_CB (ABSY, TAS); + ++Regs.PC; + uint8_t baselo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.AC & Regs.XR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.YR); + bool pagecross = (baselo + Regs.YR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Regs.SP = Regs.AC & Regs.XR; + Cycles=5; } @@ -2612,7 +2638,19 @@ static void OPC_6502_9B (void) static void OPC_6502_9C (void) /* Opcode $9D: SHY abs,x */ { - STO_OP (ABSX, Regs.YR & ((address >> 8) + 1)); + ++Regs.PC; + uint8_t baselo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.YR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.XR); + bool pagecross = (baselo + Regs.XR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Cycles=5; } @@ -2636,7 +2674,19 @@ static void OPC_6502_9D (void) static void OPC_6502_9E (void) /* Opcode $9E: SHX abs,x */ { - STO_OP (ABSY, Regs.XR & ((address >> 8) + 1)); + ++Regs.PC; + uint8_t baselo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.XR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.YR); + bool pagecross = (baselo + Regs.YR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Cycles=5; } @@ -2644,7 +2694,19 @@ static void OPC_6502_9E (void) static void OPC_6502_9F (void) /* Opcode $9F: SHA abs,y */ { - STO_CB (ABSY, SHA); + ++Regs.PC; + uint8_t baselo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.AC & Regs.XR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.YR); + bool pagecross = (baselo + Regs.YR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Cycles=5; }