/* * Apple // emulator for *ix * * This software package is subject to the GNU General Public License * version 3 or later (your choice) as published by the Free Software * Foundation. * * Copyright 2013-2015 Aaron Culliney * */ /* * 65c02 CPU opcodes emulated with ARM. */ #include "cpu-regs.h" #include "vm.h" #define DecodeFlags \ ldr r1, SYM(cpu65_flags_decode); \ ldrb F_Reg, [r1, r0]; #define EncodeFlags \ ldr r1, SYM(cpu65_flags_encode); \ ldrb r0, [r1, F_Reg]; #define CommonSaveCPUState \ /* save EA */ \ ldr r0, SYM(cpu65_ea); \ strh EffectiveAddr, [r0]; \ /* save stack pointer */ \ ldr r0, SYM(cpu65_sp); \ strb SP_Reg, [r0]; \ /* save X */ \ ldr r0, SYM(cpu65_x); \ strb X_Reg, [r0]; \ /* save Y */ \ ldr r0, SYM(cpu65_y); \ strb Y_Reg, [r0]; \ /* save A */ \ ldr r0, SYM(cpu65_a); \ strb A_Reg, [r0]; \ /* save flags */ \ EncodeFlags \ ldr r1, SYM(cpu65_f); \ strb r0, [r1] // Tracing is necessary for some CPU tests and to verify operation against other emulators #if CPU_TRACING # define TRACE_PROLOGUE \ ldr r1, SYM(cpu65_pc); \ strh PC_Reg, [r1]; \ bl CALL(cpu65_trace_prologue); # define TRACE_ARG \ bl CALL(cpu65_trace_arg); # define TRACE_ARG1 \ bl CALL(cpu65_trace_arg1); # define TRACE_ARG2 \ bl CALL(cpu65_trace_arg2); # define TRACE_EPILOGUE \ CommonSaveCPUState; \ bl CALL(cpu65_trace_epilogue); #else # define TRACE_PROLOGUE # define TRACE_ARG # define TRACE_ARG1 # define TRACE_ARG2 # define TRACE_EPILOGUE #endif #define CPUStatsReset \ ldr r1, SYM(cpu65_opcode); \ strb r0, [r1]; /* r0 should be next opcode */ \ eor r9, r9, r9; \ ldr r1, SYM(cpu65_opcycles); \ strb r9, [r1]; \ ldr r1, SYM(cpu65_rw); \ strb r9, [r1]; #define CPUStatsSetRead \ ldr r1, SYM(cpu65_rw); \ ldrb r9, [r1]; \ orr r9, r9, #1; \ strb r9, [r1]; #define CPUStatsSetWrite \ ldr r1, SYM(cpu65_rw); \ ldrb r9, [r1]; \ orr r9, r9, #2; \ strb r9, [r1]; \ ldr r1, SYM(cpu65_d); \ strb r0, [r1]; // ---------------------------------------------------------------------------- // CPU (6502) helper macros // Add 16bit x with <=16bit amt #define AddUint16(x, amt) \ add x, x, amt; \ bic x, #0x10000; // Increment 16bit x #define IncUint16(x) \ AddUint16(x, #1) // Add 8bit x with <=8bit amt #define AddUint8(x, amt) \ add x, x, amt; \ bic SP_Reg, #0x0100; // Increment 8bit x #define IncUint8(x) \ AddUint8(x, #1) // Subtract 8bit x by <=8bit amt #define SubUint8(x, amt) \ subs x, x, amt; \ movmi x, #0xFF; // Decrement 8bit x #define DecUint8(x) \ SubUint8(x, #1) #define GetFromPC_B \ mov EffectiveAddr, PC_Reg; \ IncUint16(PC_Reg) \ orrs r0, r0, #0x10000; /* clear ARM processor flags */ \ ldr r1, [reg_vmem_r, EffectiveAddr, LSL PTR_SHIFT]; \ blx r1; \ TRACE_ARG #define hi_byte r0 #define lo_byte r9 #define GetFromPC_W \ mov EffectiveAddr, PC_Reg; \ AddUint16(PC_Reg, #2) \ ldr r1, [reg_vmem_r, EffectiveAddr, LSL PTR_SHIFT]; \ blx r1; \ TRACE_ARG1 \ mov lo_byte, r0; \ IncUint16(EffectiveAddr) \ ldr r1, [reg_vmem_r, EffectiveAddr, LSL PTR_SHIFT]; \ blx r1; \ TRACE_ARG2; \ mov hi_byte, hi_byte, LSL #8; \ orr r0, hi_byte, lo_byte; #define JumpNextInstruction \ TRACE_PROLOGUE \ GetFromPC_B \ CPUStatsReset \ ldr r1, SYM(cpu65__opcodes); \ ldr r1, [r1, r0, LSL PTR_SHIFT]; \ bx r1; #define GetFromEA_B \ CPUStatsSetRead \ ldr r1, [reg_vmem_r, EffectiveAddr, LSL PTR_SHIFT]; \ blx r1; #define GetFromEA_W \ ldr r1, [reg_vmem_r, EffectiveAddr, LSL PTR_SHIFT]; \ blx r1; \ mov lo_byte, r0; \ IncUint16(EffectiveAddr) \ ldr r1, [reg_vmem_r, EffectiveAddr, LSL PTR_SHIFT]; \ blx r1; \ mov hi_byte, hi_byte, LSL #8; \ orr r0, hi_byte, lo_byte; #define PutToEA_B \ CPUStatsSetWrite \ ldr r1, SYM(cpu65_vmem_w); \ ldr r1, [r1, EffectiveAddr, LSL PTR_SHIFT]; \ blx r1; #define GetFromMem_B(x) \ mov EffectiveAddr, x; \ ldr r1, [reg_vmem_r, EffectiveAddr, LSL PTR_SHIFT]; \ blx r1; #define GetFromMem_W(x) \ mov EffectiveAddr, x; \ GetFromEA_W #define Continue \ b continue; #define cbw \ /* Virtual x86: Convert Byte-to-Word */ \ mov r0, r0, LSL #24; \ mov r0, r0, ASR #24; #define _IncOpCycles \ ldr mem_cycle_count, SYM(cpu65_opcycles); \ ldrb scratch_count, [mem_cycle_count]; \ add scratch_count, scratch_count, #1; #define IncOpCycles \ _IncOpCycles \ strb scratch_count, [mem_cycle_count]; #define pc_hi_byte r9 #define mem_cycle_count r1 #define scratch_count r12 #define pc_hi_prev r9 #define pc_hi_next r0 #define BranchXCycles \ _IncOpCycles \ mov pc_hi_prev, PC_Reg, LSR #8; \ cbw; \ add PC_Reg, PC_Reg, r0; /* branch PC */ \ mov PC_Reg, PC_Reg, LSL #16; /* 16bit under/overflow protection */ \ mov PC_Reg, PC_Reg, LSR #16; \ mov pc_hi_next, PC_Reg, LSR #8; \ teq pc_hi_next, pc_hi_prev; \ addne scratch_count, scratch_count, #1; /* +1 branch taken */ \ strb scratch_count, [mem_cycle_count]; #define arm_flags r12 #define lahf \ /* Virtual x86: Load %AH (r12) from ARM CPU Flags */ \ mrs arm_flags, APSR; \ mov arm_flags, arm_flags, LSR #28; #define bt \ /* Virtual x86: Bit Test (and set ... carry flag only) */ \ tst F_Reg, #C_Flag; \ mrs arm_flags, APSR; \ biceq arm_flags, arm_flags, #0x20000000; \ orrne arm_flags, arm_flags, #0x20000000; \ msr APSR_nzcvq, arm_flags; #define btc \ /* Virtual x86: Bit Test and Clear (carry flag only) */ \ tst F_Reg, #C_Flag; \ mrs arm_flags, APSR; \ bicne arm_flags, arm_flags, #0x20000000; \ /*orreq arm_flags, arm_flags, #0x20000000;*/ \ msr APSR_nzcvq, arm_flags; #define FlagC \ lahf; \ and arm_flags, arm_flags, #C_Flag; \ bic F_Reg, F_Reg, #C_Flag; \ orr F_Reg, F_Reg, arm_flags; #define FlagZ \ lahf; \ and arm_flags, arm_flags, #Z_Flag; \ bic F_Reg, F_Reg, #Z_Flag; \ orr F_Reg, F_Reg, arm_flags; #define FlagN \ lahf; \ and arm_flags, arm_flags, #N_Flag; \ bic F_Reg, F_Reg, #N_Flag; \ orr F_Reg, F_Reg, arm_flags; #define FlagNZ \ lahf; \ and arm_flags, arm_flags, #NZ_Flags; \ bic F_Reg, F_Reg, #NZ_Flags; \ orr F_Reg, F_Reg, arm_flags; #define FlagNZC \ lahf; \ and arm_flags, arm_flags, #NZC_Flags; \ bic F_Reg, F_Reg, #NZC_Flags; \ orr F_Reg, F_Reg, arm_flags; #define FlagNVZ \ lahf; \ and arm_flags, arm_flags, #NVZ_Flags; \ bic F_Reg, F_Reg, #NVZ_Flags; \ orr F_Reg, F_Reg, arm_flags; #define FlagNVZC \ lahf; \ bic F_Reg, F_Reg, #NVZC_Flags; \ orr F_Reg, F_Reg, arm_flags; #define stack_loc r1 #ifdef APPLE2_VM #define RestoreAltZP \ ldr stack_loc, SYM(base_stackzp); \ ldr stack_loc, [stack_loc]; \ add stack_loc, stack_loc, #0x100; \ add stack_loc, stack_loc, SP_Reg; #else #define RestoreAltZP #endif #define Push(x) \ RestoreAltZP \ strb x, [stack_loc]; \ DecUint8(SP_Reg) #warning TODO FIXME ... need to write a 65c02 stack underflow vm test ... also how does AppleWin handle 65c02 underflow? #define Pop(x) \ IncUint8(SP_Reg) \ RestoreAltZP \ ldrb x, [stack_loc]; #warning TODO FIXME ... need to write a 65c02 stack overflow vm test ... also how does AppleWin handle 65c02 overflow? // ---------------------------------------------------------------------------- // addressing macros /* Immediate Addressing - the operand is contained in the second byte of the instruction. */ #define _GetImm \ mov EffectiveAddr, PC_Reg; \ IncUint16(PC_Reg) #if CPU_TRACING #define GetImm \ _GetImm \ ldr r1, [reg_vmem_r, EffectiveAddr, LSL PTR_SHIFT]; \ blx r1; \ TRACE_ARG #else #define GetImm \ _GetImm #endif /* Absolute Addressing - the second byte of the instruction is the low order address, and the third byte is the high order byte. */ #define GetAbs \ GetFromPC_W; \ mov EffectiveAddr, r0; /* Zero Page Addressing - the second byte of the instruction is an address on the zero page */ #define GetZPage \ GetFromPC_B; \ mov EffectiveAddr, r0; /* Zero Page Indexed Addressing - The effective address is calculated by adding the second byte to the contents of the index register. Due to the zero page addressing nature of this mode, no carry is added to the high address byte, and the crossing of page boundaries does not occur. */ #define GetZPage_X \ GetFromPC_B; \ add r0, r0, X_Reg; \ bic r0, #0x100; \ mov EffectiveAddr, r0; #define GetZPage_Y \ GetFromPC_B; \ add r0, r0, Y_Reg; \ bic r0, #0x100; \ mov EffectiveAddr, r0; #define ea_hi_prev r1 #define ea_hi_next r9 #define AddIndexRegAndTestPageBoundary(IndexReg) \ mov ea_hi_prev, r0, LSR #8; \ add r0, r0, IndexReg; \ bic r0, #0x10000; \ mov ea_hi_next, r0, LSR #8; \ teq ea_hi_prev, ea_hi_next; \ beq 9f; /* Absolute Indexed Addressing - The effective address is formed by adding the contents of X or Y to the address contained in the second and third bytes of the instruction. */ #define GetAbs_X \ GetFromPC_W \ AddIndexRegAndTestPageBoundary(X_Reg) \ IncOpCycles \ 9: mov EffectiveAddr, r0; #define GetAbs_X_STx \ GetFromPC_W \ AddIndexRegAndTestPageBoundary(X_Reg) \ /* IncOpCycles */ \ 9: mov EffectiveAddr, r0; #define GetAbs_Y \ GetFromPC_W \ AddIndexRegAndTestPageBoundary(Y_Reg) \ IncOpCycles \ 9: mov EffectiveAddr, r0; #define GetAbs_Y_STA \ GetFromPC_W \ AddIndexRegAndTestPageBoundary(Y_Reg) \ /* IncOpCycles */ \ 9: mov EffectiveAddr, r0; /* Zero Page Indirect Addressing (65c02) - The second byte of the instruction points to a memory location on page zero containing the low order byte of the effective address. The next location on page zero contains the high order byte of the address. */ #define GetIndZPage \ GetFromPC_B \ mov EffectiveAddr, r0; \ GetFromEA_B \ mov r12, r0; \ add EffectiveAddr, EffectiveAddr, #1; \ bic EffectiveAddr, #0x100; \ GetFromEA_B \ mov r0, r0, LSL #8; \ orr r0, r12, r0; \ mov EffectiveAddr, r0; /* Zero Page Indexed Indirect Addressing - The second byte is added to the contents of the X index register; the carry is discarded. The result of this addition points to a memory location on page zero whose contents is the low order byte of the effective address. The next memory location in page zero contains the high-order byte of the effective address. Both memory locations specifying the high and low-order bytes must be in page zero. */ #define GetIndZPage_X \ GetFromPC_B \ mov EffectiveAddr, r0; \ add EffectiveAddr, EffectiveAddr, X_Reg; \ bic EffectiveAddr, #0x100; \ GetFromEA_B \ mov r12, r0; \ add EffectiveAddr, EffectiveAddr, #1; \ bic EffectiveAddr, #0x100; \ GetFromEA_B \ mov r0, r0, LSL #8; \ orr r0, r12, r0; \ mov EffectiveAddr, r0; /* Indirect Indexed Addressing - The second byte of the instruction points to a memory location in page zero. The contents of this memory location are added to the contents of the Y index register, the result being the low order byte of the effective address. The carry from this addition is added to the contents of the next page zero memory location, the result being the high order byte of the effective address. */ #define _GetIndZPage_Y \ GetFromPC_B \ mov EffectiveAddr, r0; \ GetFromEA_B \ mov r12, r0; \ add EffectiveAddr, EffectiveAddr, #1; \ bic EffectiveAddr, #0x100; \ GetFromEA_B \ mov r0, r0, LSL #8; \ orr r0, r12, r0; \ AddIndexRegAndTestPageBoundary(Y_Reg) #define GetIndZPage_Y \ _GetIndZPage_Y \ IncOpCycles \ 9: mov EffectiveAddr, r0; #define GetIndZPage_Y_STA \ _GetIndZPage_Y \ /*IncOpCycles*/ \ 9: mov EffectiveAddr, r0; // ---------------------------------------------------------------------------- // 65c02 instruction macros #if 0 #error this attempts to leverage ARM 32bit flags, but does so incorrectly ... possibly there is some efficiency gains here if correctness can be assured #define DoADC_b \ GetFromEA_B \ mov A_Reg, A_Reg, LSL #24; \ mov r0, r0, LSL #24; \ \ /* clear ARM 'C' */ \ adcs r1, r1, #0; \ \ /* set HI-C */ \ tst F_Reg, #C_Flag; \ adcnes A_Reg, A_Reg, #0x01000000; \ \ /* lahf; */ \ /* and arm_flags, arm_flags, #VC_Flags; */ \ /* mov arm_flags_int, arm_flags; */ \ \ adcs A_Reg, A_Reg, r0; \ FlagNVZC \ /* merge intermediate V,C */ \ /* orr F_Reg, F_Reg, arm_flags_int; */ \ mov A_Reg, A_Reg, LSR #24; #else #define sign_a r1 #define sign_0 r9 #define DoADC_b \ GetFromEA_B \ /* save operand sign bits */ \ mov sign_a, A_Reg, LSR #7; \ mov sign_0, r0, LSR #7; \ /* perform ADC with incoming carry */\ tst F_Reg, #C_Flag; \ addne A_Reg, A_Reg, #1; \ add A_Reg, A_Reg, r0; \ /* clear and set flags */ \ bic F_Reg, F_Reg, #NVZC_Flags; \ tst A_Reg, #0x100; \ orrne F_Reg, F_Reg, #C_Flag; \ bic A_Reg, #0x100; \ \ tst A_Reg, #0x80; \ orrne F_Reg, F_Reg, #N_Flag; \ \ teq sign_a, sign_0; \ bne 1f; \ mov sign_0, A_Reg, LSR #7; \ cmp sign_a, sign_0; \ orrne F_Reg, F_Reg, #V_Flag; \ \ 1: tst A_Reg, #0xFF; \ orreq F_Reg, F_Reg, #Z_Flag; #endif #ifndef NDEBUG #define DebugBCDCheck \ tst A_Reg, #0x80; \ tstne A_Reg, #0x60; \ blne CALL(debug_illegal_bcd); \ tst A_Reg, #0x08; \ tstne A_Reg, #0x06; \ blne CALL(debug_illegal_bcd); \ tst r0, #0x80; \ tstne r0, #0x60; \ blne CALL(debug_illegal_bcd); \ tst r0, #0x08; \ tstne r0, #0x06; \ blne CALL(debug_illegal_bcd); #else #define DebugBCDCheck #endif #define DoAND \ GetFromEA_B \ mov r0, r0, LSL #24; \ mov A_Reg, A_Reg, LSL #24; \ ands A_Reg, A_Reg, r0; \ FlagNZ \ mov A_Reg, A_Reg, LSR #24; #define _DoASL(x) \ mov x, x, LSL #24; \ adcs x, x, x; \ FlagNZC \ mov x, x, LSR #24; #define DoASL \ GetFromEA_B \ _DoASL(r0) \ PutToEA_B #define _DoBIT \ GetFromEA_B \ mov r1, A_Reg; \ and r1, r1, r0; #define DoBIT \ _DoBIT \ bic F_Reg, F_Reg, #NVZ_Flags; \ tst r1, #0xFF; \ orreq F_Reg, F_Reg, #Z_Flag; \ tst r0, #0x40; \ orrne F_Reg, F_Reg, #V_Flag; \ tst r0, #0x80; \ orrne F_Reg, F_Reg, #N_Flag; #define DoCMP \ GetFromEA_B \ /* TODO FIXME : maybe actually use cmp or cmn instruction? */ \ mov r0, r0, LSL #24; \ mov r1, A_Reg, LSL #24; \ subs r1, r1, r0; \ /*cmc;*/ \ FlagNZC #define DoCPX \ GetFromEA_B \ mov r0, r0, LSL #24; \ mov r1, X_Reg, LSL #24; \ subs r1, r1, r0; \ /*cmc;*/ \ FlagNZC #define DoCPY \ GetFromEA_B \ mov r0, r0, LSL #24; \ mov r1, Y_Reg, LSL #24; \ subs r1, r1, r0; \ /*cmc;*/ \ FlagNZC #define _DoDEC(x) \ mov r1, #1; \ mov r1, r1, LSL #24; \ mov x, x, LSL #24; \ subs x, x, r1; \ FlagNZ \ mov x, x, LSR #24; #define DoDEC \ GetFromEA_B \ _DoDEC(r0) \ PutToEA_B #define DoEOR \ GetFromEA_B \ mov r0, r0, LSL #24; \ mov A_Reg, A_Reg, LSL #24; \ eors A_Reg, A_Reg, r0; \ FlagNZ \ mov A_Reg, A_Reg, LSR #24; #define _DoINC(x) \ mov r1, #1; \ mov r1, r1, LSL #24; \ mov x, x, LSL #24; \ adds x, x, r1; \ FlagNZ \ mov x, x, LSR #24; #define DoINC \ GetFromEA_B \ _DoINC(r0) \ PutToEA_B #define DoLDA \ GetFromEA_B \ mov A_Reg, r0; \ mov r0, r0, LSL #24; \ orrs r0, r0, r0; \ FlagNZ #define DoLDX \ GetFromEA_B \ mov X_Reg, r0; \ mov r0, r0, LSL #24; \ orrs r0, r0, r0; \ FlagNZ #define DoLDY \ GetFromEA_B \ mov Y_Reg, r0; \ mov r0, r0, LSL #24; \ orrs r0, r0, r0; \ FlagNZ #define _DoLSR(x) \ lsrs x, x, #1; \ FlagNZC #define DoLSR \ GetFromEA_B \ _DoLSR(r0) \ PutToEA_B #define DoORA \ GetFromEA_B \ mov r0, r0, LSL #24; \ mov A_Reg, A_Reg, LSL #24; \ orrs A_Reg, A_Reg, r0; \ FlagNZ \ mov A_Reg, A_Reg, LSR #24; #define _DoPLx(x) \ Pop(x); \ mov r0, x, LSL #24; \ orrs r0, r0, r0; \ FlagNZ #define _DoROL(x) \ mov x, x, LSL #8; \ tst F_Reg, #C_Flag; \ orrne x, x, #0x80; \ lsls x, x, #17; \ FlagNZC \ mov x, x, LSR #24; #define DoROL \ GetFromEA_B \ _DoROL(r0) \ PutToEA_B #define _DoROR(x) \ tst F_Reg, #C_Flag; \ orrne x, x, #0x100; \ ror x, x, #1; \ bic F_Reg, F_Reg, #NZC_Flags; \ tst x, #ROR_BIT; \ bicne x, #ROR_BIT; \ orrne F_Reg, F_Reg, #C_Flag; \ tst x, #0xFF; \ orreq F_Reg, F_Reg, #Z_Flag; \ tst x, #0x80; \ orrne F_Reg, F_Reg, #N_Flag; #define DoROR \ GetFromEA_B \ _DoROR(r0) \ PutToEA_B #if 0 #error this attempts to leverage ARM 32bit flags, but does so incorrectly ... possibly there is some efficiency gains here if correctness can be assured #define DoSBC_b \ GetFromEA_B \ mvn r0, r0; \ adcs r1, r1, #0; \ bt; \ adcs A_Reg, A_Reg, r0; \ FlagNVZC #else #define DoSBC_b \ GetFromEA_B \ /* save operand sign bits */ \ mov sign_a, A_Reg, LSR #7; \ mov sign_0, r0, LSR #7; \ /* perform SBC with incoming borrow */\ sub A_Reg, A_Reg, r0; \ tst F_Reg, #C_Flag; \ subeq A_Reg, A_Reg, #1; \ /* clear and set flags */ \ bic F_Reg, F_Reg, #NVZC_Flags; \ tst A_Reg, #0x80000000; \ orreq F_Reg, F_Reg, #C_Flag; \ mov A_Reg, A_Reg, LSL #24; \ mov A_Reg, A_Reg, LSR #24; \ \ tst A_Reg, #0x80; \ orrne F_Reg, F_Reg, #N_Flag; \ \ teq sign_a, sign_0; \ beq 1f; \ mov sign_0, A_Reg, LSR #7; \ cmp sign_a, sign_0; \ orrne F_Reg, F_Reg, #V_Flag; \ \ 1: tst A_Reg, #0xFF; \ orreq F_Reg, F_Reg, #Z_Flag; #endif #define DoSTA \ mov r0, A_Reg; \ PutToEA_B #define DoSTX \ mov r0, X_Reg; \ PutToEA_B #define DoSTY \ mov r0, Y_Reg; \ PutToEA_B #define DoSTZ \ mov r0, #0; \ PutToEA_B #define DoTRB \ GetFromEA_B \ tst r0, A_Reg; \ FlagZ \ mvn r1, A_Reg; \ and r0, r0, r1; \ PutToEA_B #define DoTSB \ GetFromEA_B \ tst r0, A_Reg; \ FlagZ \ orr r0, A_Reg, r0; \ PutToEA_B /* ---------------------------------------------------------------------- 6502 routines and instructions ---------------------------------------------------------------------- */ /* ---------------------------------- ADC instructions ADd memory to accumulator with Carry ---------------------------------- */ #define carry r9 #define lo_nyb r1 // Decimal mode ENTRY(op_ADC_dec) IncOpCycles GetFromEA_B DebugBCDCheck // isolate lo nybbles mov lo_nyb, A_Reg mov r9, r0 and lo_nyb, lo_nyb, #0x0F and r9, r9, #0x0F // lo nybble addition with carry tst F_Reg, #C_Flag addne r9, r9, #1 add lo_nyb, lo_nyb, r9 // prep 65c02 flags bic F_Reg, #NVZC_Flags // lo nybble DAA (Decimal Adjustment after Addition), saving carry (+0, +1, +2) cmp lo_nyb, #0x09 addhi lo_nyb, lo_nyb, #6 mov carry, lo_nyb, LSR #4 and lo_nyb, lo_nyb, #0x0F // isolate hi nybbles mov A_Reg, A_Reg, LSR #4 mov r0, r0, LSR #4 // hi nybble addition with carry add r0, r0, carry add r0, A_Reg, r0 // hi nybble DAA cmp r0, #0x09 addhi r0, #6 orrhi F_Reg, #C_Flag // merge nybbles mov r0, r0, LSL #4 orr A_Reg, r0, lo_nyb // NZ flags tst A_Reg, #0x80 orrne F_Reg, F_Reg, #N_Flag tst A_Reg, #0xFF orreq F_Reg, F_Reg, #Z_Flag Continue #define maybe_DoADC_d \ tst F_Reg, #D_Flag; /* Decimal mode? */ \ bne CALL(op_ADC_dec) /* Yes, jump to decimal version */ ENTRY(op_ADC_imm) // 0x69 GetImm maybe_DoADC_d DoADC_b Continue ENTRY(op_ADC_zpage) // 0x65 GetZPage maybe_DoADC_d DoADC_b Continue ENTRY(op_ADC_zpage_x) // 0x75 GetZPage_X maybe_DoADC_d DoADC_b Continue // UNIMPLEMENTED : W65C02S datasheet ENTRY(op_ADC_zpage_y) b CALL(op_NOP) ENTRY(op_ADC_abs) // 0x6d GetAbs maybe_DoADC_d DoADC_b Continue ENTRY(op_ADC_abs_x) // 0x7d GetAbs_X maybe_DoADC_d DoADC_b Continue ENTRY(op_ADC_abs_y) // 0x79 GetAbs_Y maybe_DoADC_d DoADC_b Continue ENTRY(op_ADC_ind_x) // 0x61 GetIndZPage_X maybe_DoADC_d DoADC_b Continue ENTRY(op_ADC_ind_y) // 0x71 GetIndZPage_Y maybe_DoADC_d DoADC_b Continue // 65c02 : 0x72 ENTRY(op_ADC_ind_zpage) GetIndZPage maybe_DoADC_d DoADC_b Continue .ltorg /* ---------------------------------- AND instructions logical AND memory with accumulator ---------------------------------- */ ENTRY(op_AND_imm) // 0x29 GetImm DoAND Continue ENTRY(op_AND_zpage) // 0x25 GetZPage DoAND Continue ENTRY(op_AND_zpage_x) // 0x35 GetZPage_X DoAND Continue // UNIMPLEMENTED : W65C02S datasheet ENTRY(op_AND_zpage_y) b CALL(op_NOP) ENTRY(op_AND_abs) // 0x2d GetAbs DoAND Continue ENTRY(op_AND_abs_x) // 0x3d GetAbs_X DoAND Continue ENTRY(op_AND_abs_y) // 0x39 GetAbs_Y DoAND Continue ENTRY(op_AND_ind_x) // 0x21 GetIndZPage_X DoAND Continue ENTRY(op_AND_ind_y) // 0x31 GetIndZPage_Y DoAND Continue // 65c02 : 0x32 ENTRY(op_AND_ind_zpage) GetIndZPage DoAND Continue .ltorg /* ---------------------------------- ASL instructions Arithmetic Shift one bit Left, memory or accumulator ---------------------------------- */ ENTRY(op_ASL_acc) // 0x0a _DoASL(A_Reg) Continue ENTRY(op_ASL_zpage) // 0x06 GetZPage DoASL Continue ENTRY(op_ASL_zpage_x) // 0x16 GetZPage_X DoASL Continue ENTRY(op_ASL_abs) // 0x0e GetAbs DoASL Continue ENTRY(op_ASL_abs_x) // 0x1e GetAbs_X DoASL Continue .ltorg /* ---------------------------------- BBRx instructions UNIMPLEMENTED : These are documented in the W65C02S datasheet ... + 1 cycle if branch within page + 2 cycles if branch across page boundary ---------------------------------- */ ENTRY(op_BBR0_65c02) Continue ENTRY(op_BBR1_65c02) Continue ENTRY(op_BBR2_65c02) Continue ENTRY(op_BBR3_65c02) Continue ENTRY(op_BBR4_65c02) Continue ENTRY(op_BBR5_65c02) Continue ENTRY(op_BBR6_65c02) Continue ENTRY(op_BBR7_65c02) Continue /* ---------------------------------- BBSx instructions UNIMPLEMENTED : These are documented in the W65C02S datasheet ... + 1 cycle if branch within page + 2 cycles if branch across page boundary ---------------------------------- */ ENTRY(op_BBS0_65c02) Continue ENTRY(op_BBS1_65c02) Continue ENTRY(op_BBS2_65c02) Continue ENTRY(op_BBS3_65c02) Continue ENTRY(op_BBS4_65c02) Continue ENTRY(op_BBS5_65c02) Continue ENTRY(op_BBS6_65c02) Continue ENTRY(op_BBS7_65c02) Continue .ltorg /* ---------------------------------- BCC instruction Branch on Carry Clear ---------------------------------- */ ENTRY(op_BCC) // 0x90 GetFromPC_B tst F_Reg, #C_Flag bne continue // branch not taken BranchXCycles Continue /* ---------------------------------- BCS instruction Branch on Carry Set ---------------------------------- */ ENTRY(op_BCS) // 0xB0 GetFromPC_B tst F_Reg, #C_Flag beq continue // branch not taken BranchXCycles Continue /* ---------------------------------- BEQ instruction Branch if EQual ---------------------------------- */ ENTRY(op_BEQ) // 0xF0 GetFromPC_B tst F_Reg, #Z_Flag beq continue // branch not taken BranchXCycles Continue .ltorg /* ---------------------------------- BIT instructions BIt Test ---------------------------------- */ ENTRY(op_BIT_zpage) // 0x24 GetZPage DoBIT Continue ENTRY(op_BIT_abs) // 0x2c GetAbs DoBIT Continue // 65c02 : 0x34 ENTRY(op_BIT_zpage_x) GetZPage_X DoBIT Continue // 65c02 : 0x3C ENTRY(op_BIT_abs_x) GetAbs_X DoBIT Continue /* BIT immediate is anomalous in that it does not affect the * N and V flags, unlike in other addressing modes. */ // 65c02 : 0x89 ENTRY(op_BIT_imm) GetImm _DoBIT tst r1, #0xFF orreq F_Reg, F_Reg, #Z_Flag bicne F_Reg, F_Reg, #Z_Flag Continue .ltorg /* ---------------------------------- BMI instruction Branch on result MInus ---------------------------------- */ ENTRY(op_BMI) // 0x30 GetFromPC_B tst F_Reg, #N_Flag beq continue BranchXCycles Continue /* ---------------------------------- BNE instruction Branch on result Not Equal ---------------------------------- */ ENTRY(op_BNE) // 0xD0 GetFromPC_B tst F_Reg, #Z_Flag bne continue BranchXCycles Continue /* ---------------------------------- BPL instruction Branch on result PLus ---------------------------------- */ ENTRY(op_BPL) // 0x10 GetFromPC_B tst F_Reg, #N_Flag bne continue BranchXCycles Continue /* ---------------------------------- BRA instruction BRanch Always ---------------------------------- */ // 65c02 : 0x80 ENTRY(op_BRA) GetFromPC_B BranchXCycles Continue .ltorg /* ---------------------------------- BRK instruction ---------------------------------- */ ENTRY(op_UNK) /* make undefined opcodes fault */ ENTRY(op_BRK) IncUint16(PC_Reg) mov r0, PC_Reg mov r0, r0, ROR #8 Push(r0) mov r0, r0, LSR #24 Push(r0) orr F_Reg, F_Reg, #BX_Flags EncodeFlags Push(r0) orr F_Reg, F_Reg, #I_Flag ldr EffectiveAddr, SYM(interrupt_vector) ldrh EffectiveAddr, [EffectiveAddr] GetFromEA_W mov PC_Reg, r0 Continue .ltorg /* ---------------------------------- BVC instruction Branch on oVerflow Clear ---------------------------------- */ ENTRY(op_BVC) // 0x50 GetFromPC_B tst F_Reg, #V_Flag bne continue BranchXCycles Continue /* ---------------------------------- BVS instruction Branch on oVerflow Set ---------------------------------- */ ENTRY(op_BVS) // 0x70 GetFromPC_B tst F_Reg, #V_Flag beq continue BranchXCycles Continue .ltorg /* ---------------------------------- CLC instruction ---------------------------------- */ ENTRY(op_CLC) // 0x18 bic F_Reg, #C_Flag Continue /* ---------------------------------- CLD instruction ---------------------------------- */ ENTRY(op_CLD) // 0xd8 bic F_Reg, #D_Flag Continue /* ---------------------------------- CLI instruction ---------------------------------- */ ENTRY(op_CLI) // 0x58 bic F_Reg, #I_Flag Continue /* ---------------------------------- CLV instruction ---------------------------------- */ ENTRY(op_CLV) // 0xB8 bic F_Reg, #V_Flag Continue .ltorg /* ---------------------------------- CMP instructions CoMPare memory and accumulator ---------------------------------- */ ENTRY(op_CMP_imm) // 0xc9 GetImm DoCMP Continue ENTRY(op_CMP_zpage) // 0xc5 GetZPage DoCMP Continue ENTRY(op_CMP_zpage_x) // 0xd5 GetZPage_X DoCMP Continue // UNIMPLEMENTED : W65C02S datasheet ENTRY(op_CMP_zpage_y) b CALL(op_NOP) ENTRY(op_CMP_abs) // 0xcd GetAbs DoCMP Continue ENTRY(op_CMP_abs_x) // 0xdd GetAbs_X DoCMP Continue ENTRY(op_CMP_abs_y) // 0xd9 GetAbs_Y DoCMP Continue ENTRY(op_CMP_ind_x) // 0xc1 GetIndZPage_X DoCMP Continue ENTRY(op_CMP_ind_y) // 0xd1 GetIndZPage_Y DoCMP Continue // 65c02 : 0xD2 ENTRY(op_CMP_ind_zpage) GetIndZPage DoCMP Continue .ltorg /* ---------------------------------- CPX instructions ComPare memory and X register ---------------------------------- */ ENTRY(op_CPX_imm) // 0xe0 GetImm DoCPX Continue ENTRY(op_CPX_zpage) // 0xe4 GetZPage DoCPX Continue ENTRY(op_CPX_abs) // 0xec GetAbs DoCPX Continue /* ---------------------------------- CPY instructions ComPare memory and Y register ---------------------------------- */ ENTRY(op_CPY_imm) // 0xc0 GetImm DoCPY Continue ENTRY(op_CPY_zpage) // 0xc4 GetZPage DoCPY Continue ENTRY(op_CPY_abs) // 0xcc GetAbs DoCPY Continue .ltorg /* ---------------------------------- DEA: DEcrement Accumulator ---------------------------------- */ ENTRY(op_DEC_acc) ENTRY(op_DEA) // 0x3A _DoDEC(A_Reg) Continue /* ---------------------------------- DEC instructions DECrement memory or accumulator by one ---------------------------------- */ ENTRY(op_DEC_zpage) // 0xc6 GetZPage DoDEC Continue ENTRY(op_DEC_zpage_x) // 0xd6 GetZPage_X DoDEC Continue ENTRY(op_DEC_abs) // 0xce GetAbs DoDEC Continue ENTRY(op_DEC_abs_x) // 0xde GetAbs_X DoDEC Continue /* ---------------------------------- DEX instruction ---------------------------------- */ ENTRY(op_DEX) // 0xca _DoDEC(X_Reg) Continue /* ---------------------------------- DEY instruction ---------------------------------- */ ENTRY(op_DEY) // 0x88 _DoDEC(Y_Reg) Continue .ltorg /* ---------------------------------- EOR instructions Exclusive OR memory with accumulator ---------------------------------- */ ENTRY(op_EOR_imm) // 0x49 GetImm DoEOR Continue ENTRY(op_EOR_zpage) // 0x45 GetZPage DoEOR Continue ENTRY(op_EOR_zpage_x) // 0x55 GetZPage_X DoEOR Continue // UNIMPLEMENTED : W65C02S datasheet ENTRY(op_EOR_zpage_y) b CALL(op_NOP) ENTRY(op_EOR_abs) // 0x4d GetAbs DoEOR Continue ENTRY(op_EOR_abs_x) // 0x5d GetAbs_X DoEOR Continue ENTRY(op_EOR_abs_y) // 0x59 GetAbs_Y DoEOR Continue ENTRY(op_EOR_ind_x) // 0x41 GetIndZPage_X DoEOR Continue ENTRY(op_EOR_ind_y) // 0x51 GetIndZPage_Y DoEOR Continue // 65c02 : 0x52 ENTRY(op_EOR_ind_zpage) GetIndZPage DoEOR Continue .ltorg /* ---------------------------------- INA : INcrement Accumulator ---------------------------------- */ ENTRY(op_INC_acc) ENTRY(op_INA) // 0x1A _DoINC(A_Reg) Continue /* ---------------------------------- INC instructions INCrement memory ---------------------------------- */ ENTRY(op_INC_zpage) // 0xe6 GetZPage DoINC Continue ENTRY(op_INC_zpage_x) // 0xf6 GetZPage_X DoINC Continue ENTRY(op_INC_abs) // 0xee GetAbs DoINC Continue ENTRY(op_INC_abs_x) // 0xfe GetAbs_X DoINC Continue /* ---------------------------------- INX instruction ---------------------------------- */ ENTRY(op_INX) // 0xe8 _DoINC(X_Reg) Continue /* ---------------------------------- INY instruction ---------------------------------- */ ENTRY(op_INY) // 0xc8 _DoINC(Y_Reg) Continue .ltorg /* ---------------------------------- JMP instructions JuMP to new location ---------------------------------- */ ENTRY(op_JMP_abs) GetAbs mov PC_Reg, EffectiveAddr Continue ENTRY(op_JMP_ind) // 0x6c GetFromPC_W mov r1, r0, LSL #24 teq r1, #0xFF000000 beq jmp_special GetFromMem_W(r0) mov PC_Reg, r0 sub EffectiveAddr, EffectiveAddr, #1 Continue jmp_special: // see JMP indirect note in _Understanding the Apple IIe_ 4-25 mov PC_Reg, r0 subs PC_Reg, PC_Reg, #0xFF movmi PC_Reg, PC_Reg, LSL #16 movmi PC_Reg, PC_Reg, LSR #16 GetFromMem_B(PC_Reg) mov r9, r0, LSL #8 add PC_Reg, PC_Reg, #0xFF bic PC_Reg, #0x10000 GetFromMem_B(PC_Reg) orr r0, r9, r0 mov PC_Reg, r0 Continue // 65c02 : 0x7C ENTRY(op_JMP_abs_ind_x) GetFromPC_W mov EffectiveAddr, r0 mov r0, X_Reg #warning HACK FIXME TODO : need to check the Bible here ... is this a signed addition (in which case we need to cbw) //cbw add EffectiveAddr, EffectiveAddr, r0 mov EffectiveAddr, EffectiveAddr, LSL #16 // 16bit under/overflow protection mov EffectiveAddr, EffectiveAddr, LSR #16 GetFromMem_W(EffectiveAddr) mov PC_Reg, r0 Continue /* ---------------------------------- JSR instruction ---------------------------------- */ ENTRY(op_JSR) // 0x20 GetAbs mov r0, PC_Reg sub r0, r0, #1 mov r0, r0, LSL #16 // handle underflow -- can this happen in second mem page? mov r0, r0, ROR #24 Push(r0) // push hi_byte mov r0, r0, LSR #24 Push(r0) // push lo_byte mov PC_Reg, EffectiveAddr Continue .ltorg /* ---------------------------------- LDA instructions LoaD Accumulator with memory ---------------------------------- */ ENTRY(op_LDA_imm) // 0xa9 GetImm DoLDA Continue ENTRY(op_LDA_zpage) // 0xa5 GetZPage DoLDA Continue ENTRY(op_LDA_zpage_x) // 0xb5 GetZPage_X DoLDA Continue // UNIMPLEMENTED : W65C02S datasheet ENTRY(op_LDA_zpage_y) b CALL(op_NOP) ENTRY(op_LDA_abs) // 0xad GetAbs DoLDA Continue ENTRY(op_LDA_abs_x) // 0xbd GetAbs_X DoLDA Continue ENTRY(op_LDA_abs_y) // 0xb9 GetAbs_Y DoLDA Continue ENTRY(op_LDA_ind_x) // 0xa1 GetIndZPage_X DoLDA Continue ENTRY(op_LDA_ind_y) // 0xb1 GetIndZPage_Y DoLDA Continue // 65c02 : 0xB2 ENTRY(op_LDA_ind_zpage) GetIndZPage DoLDA Continue .ltorg /* ---------------------------------- LDX instructions ---------------------------------- */ ENTRY(op_LDX_imm) // 0xa2 GetImm DoLDX Continue ENTRY(op_LDX_zpage) // 0xa6 GetZPage DoLDX Continue // HACK : is this used? need to study coverage ... ENTRY(op_LDX_zpage_y) // 0xb6 GetZPage_Y DoLDX Continue ENTRY(op_LDX_abs) // 0xae GetAbs DoLDX Continue ENTRY(op_LDX_abs_y) // 0xbe GetAbs_Y DoLDX Continue /* ---------------------------------- LDY instructions ---------------------------------- */ ENTRY(op_LDY_imm) // 0xa0 GetImm DoLDY Continue ENTRY(op_LDY_zpage) // 0xa4 GetZPage DoLDY Continue ENTRY(op_LDY_zpage_x) // 0xb4 GetZPage_X DoLDY Continue ENTRY(op_LDY_abs) // 0xac GetAbs DoLDY Continue ENTRY(op_LDY_abs_x) // 0xbc GetAbs_X DoLDY Continue .ltorg /* ---------------------------------- LSR instructions ---------------------------------- */ ENTRY(op_LSR_acc) // 0x4a _DoLSR(A_Reg) Continue ENTRY(op_LSR_zpage) // 0x46 GetZPage DoLSR Continue ENTRY(op_LSR_zpage_x) // 0x56 GetZPage_X DoLSR Continue ENTRY(op_LSR_abs) // 0x4e GetAbs DoLSR Continue ENTRY(op_LSR_abs_x) // 0x5e GetAbs_X DoLSR Continue /* ---------------------------------- NOP instruction ---------------------------------- */ ENTRY(op_NOP) // 0xea Continue .ltorg /* ---------------------------------- ORA instructions ---------------------------------- */ ENTRY(op_ORA_imm) // 0x09 GetImm DoORA Continue ENTRY(op_ORA_zpage) // 0x05 GetZPage DoORA Continue ENTRY(op_ORA_zpage_x) // 0x15 GetZPage_X DoORA Continue // UNIMPLEMENTED : W65C02S datasheet ENTRY(op_ORA_zpage_y) b CALL(op_NOP) ENTRY(op_ORA_abs) // 0x0d GetAbs DoORA Continue ENTRY(op_ORA_abs_x) // 0x1d GetAbs_X DoORA Continue ENTRY(op_ORA_abs_y) // 0x19 GetAbs_Y DoORA Continue ENTRY(op_ORA_ind_x) // 0x01 GetIndZPage_X DoORA Continue ENTRY(op_ORA_ind_y) // 0x11 GetIndZPage_Y DoORA Continue // 65c02 : 0x12 ENTRY(op_ORA_ind_zpage) GetIndZPage DoORA Continue .ltorg /* ---------------------------------- PHA instruction ---------------------------------- */ ENTRY(op_PHA) // 0x48 Push(A_Reg) Continue /* ---------------------------------- PHP instruction ---------------------------------- */ ENTRY(op_PHP) // 0x08 EncodeFlags Push(r0) Continue /* ---------------------------------- PHX instruction 65c02 : 0xDA ---------------------------------- */ ENTRY(op_PHX) Push(X_Reg) Continue /* ---------------------------------- PHY instruction 65c02 : 0x5A ---------------------------------- */ ENTRY(op_PHY) Push(Y_Reg) Continue /* ---------------------------------- PLA instruction ---------------------------------- */ ENTRY(op_PLA) // 0x68 _DoPLx(A_Reg) Continue /* ---------------------------------- PLP instruction ---------------------------------- */ ENTRY(op_PLP) // 0x28 Pop(r0) DecodeFlags orr F_Reg, F_Reg, #BX_Flags Continue /* ---------------------------------- PLX instruction 65c02 : 0xFA ---------------------------------- */ ENTRY(op_PLX) _DoPLx(X_Reg) Continue /* ---------------------------------- PLY instruction 65c02 : 0x7A ---------------------------------- */ ENTRY(op_PLY) _DoPLx(Y_Reg) Continue .ltorg /* ---------------------------------- ROL instructions ---------------------------------- */ ENTRY(op_ROL_acc) // 0x2a _DoROL(A_Reg) Continue ENTRY(op_ROL_zpage) // 0x26 GetZPage DoROL Continue ENTRY(op_ROL_zpage_x) // 0x36 GetZPage_X DoROL Continue ENTRY(op_ROL_abs) // 0x2e GetAbs DoROL Continue ENTRY(op_ROL_abs_x) // 0x3e GetAbs_X DoROL Continue /* ---------------------------------- ROR instructions ---------------------------------- */ ENTRY(op_ROR_acc) // 0x6a _DoROR(A_Reg) Continue ENTRY(op_ROR_zpage) // 0x66 GetZPage DoROR Continue ENTRY(op_ROR_zpage_x) // 0x76 GetZPage_X DoROR Continue ENTRY(op_ROR_abs) // 0x6e GetAbs DoROR Continue ENTRY(op_ROR_abs_x) // 0x7e GetAbs_X DoROR Continue .ltorg /* ---------------------------------- RTI instruction ---------------------------------- */ ENTRY(op_RTI) // 0x40 Pop(r0) DecodeFlags orr F_Reg, F_Reg, #BX_Flags Pop(lo_byte) Pop(hi_byte) mov hi_byte, hi_byte, LSL #8 orr PC_Reg, hi_byte, lo_byte Continue /* ---------------------------------- RTS instruction ---------------------------------- */ ENTRY(op_RTS) // 0x60 Pop(lo_byte) Pop(hi_byte) mov hi_byte, hi_byte, LSL #8 orr PC_Reg, hi_byte, lo_byte IncUint16(PC_Reg) Continue .ltorg /* ---------------------------------- SBC instructions SuBtract memory from accumulator with Borrow ---------------------------------- */ #define borrow carry ENTRY(op_SBC_dec) IncOpCycles GetFromEA_B DebugBCDCheck // isolate lo nybbles mov lo_nyb, A_Reg mov r9, r0 and lo_nyb, lo_nyb, #0x0F and r9, r9, #0x0F // lo nybble subtraction with borrow sub lo_nyb, lo_nyb, r9 and lo_nyb, lo_nyb, #0xFF tst F_Reg, #C_Flag subeq lo_nyb, lo_nyb, #1 andeq lo_nyb, lo_nyb, #0xFF // prep 65c02 flags bic F_Reg, #NVZC_Flags orr F_Reg, #C_Flag // lo nybble DAS (Decimal Adjustment after Subtraction), saving borrow eor borrow, borrow, borrow cmp lo_nyb, #0x09 movhi borrow, #1 subhi lo_nyb, lo_nyb, #6 andhi lo_nyb, lo_nyb, #0x0F // isolate hi nybbles mov A_Reg, A_Reg, LSR #4 mov r0, r0, LSR #4 // hi nybble subtraction with borrow sub r0, A_Reg, r0 and r0, r0, #0xFF sub r0, r0, borrow and r0, r0, #0xFF // hi nybble DAS cmp r0, #0x09 subhi r0, #6 bichi F_Reg, #C_Flag andhi r0, r0, #0x0F // merge nybbles mov r0, r0, LSL #4 orr A_Reg, r0, lo_nyb // NZ flags tst A_Reg, #0x80 orrne F_Reg, F_Reg, #N_Flag tst A_Reg, #0xFF orreq F_Reg, F_Reg, #Z_Flag Continue #define maybe_DoSBC_d \ tst F_Reg, #D_Flag; /* Decimal mode? */ \ bne CALL(op_SBC_dec) /* Yes, jump to decimal version */ ENTRY(op_SBC_imm) // 0xe9 GetImm maybe_DoSBC_d DoSBC_b Continue ENTRY(op_SBC_zpage) // 0xe5 GetZPage maybe_DoSBC_d DoSBC_b Continue ENTRY(op_SBC_zpage_x) // 0xf5 GetZPage_X maybe_DoSBC_d DoSBC_b Continue // UNIMPLEMENTED : W65C02S datasheet ENTRY(op_SBC_zpage_y) b CALL(op_NOP) ENTRY(op_SBC_abs) // 0xed GetAbs maybe_DoSBC_d DoSBC_b Continue ENTRY(op_SBC_abs_x) // 0xfd GetAbs_X maybe_DoSBC_d DoSBC_b Continue ENTRY(op_SBC_abs_y) // 0xf9 GetAbs_Y maybe_DoSBC_d DoSBC_b Continue ENTRY(op_SBC_ind_x) // 0xe1 GetIndZPage_X maybe_DoSBC_d DoSBC_b Continue ENTRY(op_SBC_ind_y) // 0xf1 GetIndZPage_Y maybe_DoSBC_d DoSBC_b Continue // 65c02 : 0xF2 ENTRY(op_SBC_ind_zpage) GetIndZPage maybe_DoSBC_d DoSBC_b Continue .ltorg /* ---------------------------------- SEC instruction ---------------------------------- */ ENTRY(op_SEC) // 0x38 orr F_Reg, #C_Flag Continue /* ---------------------------------- SED instruction ---------------------------------- */ ENTRY(op_SED) // 0xf8 orr F_Reg, #D_Flag Continue /* ---------------------------------- SEI instruction ---------------------------------- */ ENTRY(op_SEI) // 0x78 orr F_Reg, #I_Flag Continue /* ---------------------------------- SMBx instructions -- Available in Rockwell 65C02 but not NCR 65C02 UNIMPLEMENTED : These are documented in the W65C02S datasheet ... ---------------------------------- */ ENTRY(op_SMB0_65c02) Continue ENTRY(op_SMB1_65c02) Continue ENTRY(op_SMB2_65c02) Continue ENTRY(op_SMB3_65c02) Continue ENTRY(op_SMB4_65c02) Continue ENTRY(op_SMB5_65c02) Continue ENTRY(op_SMB6_65c02) Continue ENTRY(op_SMB7_65c02) Continue .ltorg /* ---------------------------------- STA instructions ---------------------------------- */ ENTRY(op_STA_zpage) // 0x85 GetZPage DoSTA Continue ENTRY(op_STA_zpage_x) // 0x95 GetZPage_X DoSTA Continue // UNIMPLEMENTED : W65C02S datasheet ENTRY(op_STA_zpage_y) b CALL(op_NOP) ENTRY(op_STA_abs) // 0x8d GetAbs DoSTA Continue ENTRY(op_STA_abs_x) // 0x9d GetAbs_X_STx DoSTA Continue ENTRY(op_STA_abs_y) // 0x99 GetAbs_Y_STA DoSTA Continue ENTRY(op_STA_ind_x) // 0x81 GetIndZPage_X DoSTA Continue ENTRY(op_STA_ind_y) // 0x91 GetIndZPage_Y_STA DoSTA Continue // 65c02 : 0x92 ENTRY(op_STA_ind_zpage) GetIndZPage DoSTA Continue .ltorg /* ---------------------------------- STP instruction UNIMPLEMENTED : This is documented in the W65C02S datasheet ... ---------------------------------- */ ENTRY(op_STP_65c02) Continue /* ---------------------------------- RMBx instructions -- Available in Rockwell 65C02 but not NCR 65C02 UNIMPLEMENTED : These are documented in the W65C02S datasheet ... ---------------------------------- */ ENTRY(op_RMB0_65c02) Continue ENTRY(op_RMB1_65c02) Continue ENTRY(op_RMB2_65c02) Continue ENTRY(op_RMB3_65c02) Continue ENTRY(op_RMB4_65c02) Continue ENTRY(op_RMB5_65c02) Continue ENTRY(op_RMB6_65c02) Continue ENTRY(op_RMB7_65c02) Continue .ltorg /* ---------------------------------- STX instructions ---------------------------------- */ ENTRY(op_STX_zpage) // 0x86 GetZPage DoSTX Continue // HACK : is this used? need to study coverage ... ENTRY(op_STX_zpage_y) // 0x96 GetZPage_Y DoSTX Continue ENTRY(op_STX_abs) // 0x8e GetAbs DoSTX Continue /* ---------------------------------- STY instructions ---------------------------------- */ ENTRY(op_STY_zpage) // 0x84 GetZPage DoSTY Continue ENTRY(op_STY_zpage_x) // 0x94 GetZPage_X DoSTY Continue ENTRY(op_STY_abs) // 0x8c GetAbs DoSTY Continue /* ---------------------------------- STZ instructions 65c02 only ---------------------------------- */ // 65c02 : 0x64 ENTRY(op_STZ_zpage) GetZPage DoSTZ Continue // 65c02 : 0x74 ENTRY(op_STZ_zpage_x) GetZPage_X DoSTZ Continue // 65c02 : 0x9C ENTRY(op_STZ_abs) GetAbs DoSTZ Continue // 65c02 : 0x9E ENTRY(op_STZ_abs_x) GetAbs_X_STx DoSTZ Continue .ltorg /* ---------------------------------- TAX instruction ---------------------------------- */ ENTRY(op_TAX) // 0xaa mov X_Reg, A_Reg mov r0, A_Reg, LSL #24 orrs r0, r0, r0 FlagNZ Continue /* ---------------------------------- TAY instruction ---------------------------------- */ ENTRY(op_TAY) // 0xa8 mov Y_Reg, A_Reg mov r0, A_Reg, LSL #24 orrs r0, r0, r0 FlagNZ Continue /* ---------------------------------- TRB instructions 65c02 only ---------------------------------- */ // 65c02 : 0x1C ENTRY(op_TRB_abs) GetAbs DoTRB Continue // 65c02 : 0x14 ENTRY(op_TRB_zpage) GetZPage DoTRB Continue /* ---------------------------------- TSB instructions 65c02 only ---------------------------------- */ // 65c02 : 0x0C ENTRY(op_TSB_abs) GetAbs DoTSB Continue // 65c02 : 0x04 ENTRY(op_TSB_zpage) GetZPage DoTSB Continue /* ---------------------------------- TSX instruction ---------------------------------- */ ENTRY(op_TSX) // 0xba mov X_Reg, SP_Reg mov r0, SP_Reg, LSL #24 orrs r0, r0, r0 FlagNZ Continue /* ---------------------------------- TXA instruction ---------------------------------- */ ENTRY(op_TXA) // 0x8a mov A_Reg, X_Reg mov r0, X_Reg, LSL #24 orrs r0, r0, r0 FlagNZ Continue /* ---------------------------------- TXS instruction ---------------------------------- */ ENTRY(op_TXS) // 0x9a mov SP_Reg, X_Reg Continue /* ---------------------------------- TYA instruction ---------------------------------- */ ENTRY(op_TYA) // 0x98 mov A_Reg, Y_Reg mov r0, Y_Reg, LSL #24 orrs r0, r0, r0 FlagNZ Continue .ltorg /* ---------------------------------- ??? instruction - 65c02 Defined as NOPs by spec ---------------------------------- */ ENTRY(op_UNK_65c02) Continue /* ---------------------------------- WAI instruction - 65c02 UNIMPLEMENTED : This is documented in the W65C02S datasheet ... ---------------------------------- */ ENTRY(op_WAI_65c02) Continue #pragma mark - #pragma mark cpu main entry /* ------------------------------------------------------------------------- CPU continue Keep executing until we've executed >= cpu65_cycles_to_execute ------------------------------------------------------------------------- */ #define cycles_exe r0 continue: ldr r1, SYM(cpu65__opcycles) ldr r0, SYM(cpu65_opcode) ldrb r0, [r0] ldrb cycles_exe, [r1, r0] ldr r1, SYM(cpu65_opcycles) ldrb r9, [r1] add cycles_exe, cycles_exe, r9 strb cycles_exe, [r1] TRACE_EPILOGUE ldr r1, SYM(gc_cycles_timer_0) ldr r9, [r1] sub r9, r9, cycles_exe str r9, [r1] ldr r1, SYM(gc_cycles_timer_1) ldr r9, [r1] sub r9, r9, cycles_exe str r9, [r1] ldr r1, SYM(cpu65_cycle_count) ldr r9, [r1] add r9, r9, cycles_exe str r9, [r1] ldr r1, SYM(cpu65_cycles_to_execute) ldr r9, [r1] subs r9, r9, cycles_exe str r9, [r1] bmi exit_cpu65_run beq exit_cpu65_run continue1: ldr r1, SYM(cpu65__signal) ldrb r0, [r1] orrs r0, r0, r0 bne exception JumpNextInstruction /* ------------------------------------------------------------------------- Exception handlers ------------------------------------------------------------------------- */ exception: tst r0, #ResetSig beq ex_irq ldr r1, SYM(joy_button0) // OpenApple ldrb r0, [r1] tst r0, #0xFF bne exit_reinit ldr r1, SYM(joy_button1) // ClosedApple ldrb r0, [r1] tst r0, #0xFF bne exit_reinit ex_reset: eor r0, r0, r0 ldr r1, SYM(cpu65__signal) strb r0, [r1] ldr EffectiveAddr, SYM(reset_vector) ldrh EffectiveAddr, [EffectiveAddr] GetFromEA_W mov PC_Reg, r0 JumpNextInstruction ex_irq: tst F_Reg, #I_Flag // Already interrupted? beq 1f JumpNextInstruction 1: mov r0, PC_Reg mov r0, r0, ROR #8 Push(r0) mov r0, r0, LSR #24 Push(r0) orr F_Reg, F_Reg, #X_Flag EncodeFlags Push(r0) orr F_Reg, F_Reg, #BI_Flags //bic F_Reg, F_Reg, #D_Flag // AppleWin clears Decimal bit? ldr EffectiveAddr, SYM(interrupt_vector) ldrh EffectiveAddr, [EffectiveAddr] GetFromEA_W mov PC_Reg, r0 JumpNextInstruction /* ------------------------------------------------------------------------- 65c02 CPU processing loop entry point ------------------------------------------------------------------------- */ ENTRY(cpu65_run) push {r4, r5, r6, r7, r8, r9, r10, r11, lr} // Restore CPU state when being called from C. ldr reg_vmem_r, SYM(cpu65_vmem_r) ldr r1, SYM(cpu65_ea) ldrh EffectiveAddr, [r1] ldr r1, SYM(cpu65_pc) ldrh PC_Reg, [r1] ldr r1, SYM(cpu65_a) ldrb A_Reg, [r1] ldr r1, SYM(cpu65_f) ldrb r0, [r1] DecodeFlags ldr r1, SYM(cpu65_x) ldrb X_Reg, [r1] ldr r1, SYM(cpu65_y) ldrb Y_Reg, [r1] ldr r1, SYM(cpu65_sp) ldrb SP_Reg, [r1] ldr r1, SYM(emul_reinitialize) ldrb r0, [r1] teq r0, #0 eorne r0, r0, r0 strneb r0, [r1] bne ex_reset b continue1 /* ------------------------------------------------------------------------- 65c02 CPU processing loop exit point ------------------------------------------------------------------------- */ exit_cpu65_run: // Save CPU state when returning from being called from C ldr r1, SYM(cpu65_pc) strh PC_Reg, [r1] CommonSaveCPUState pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} exit_reinit: ldr r1, SYM(cpu65__signal) mov r0, #0 strb r0, [r1] ldr r1, SYM(emul_reinitialize) mov r0, #1 strb r0, [r1] pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} /* ------------------------------------------------------------------------- Debugger hooks ------------------------------------------------------------------------- */ ENTRY(cpu65_direct_write) #warning FIXME TODO implement cpu65_direct_write ... mov r0, #42 ldr r0, [r0] // segfault mov pc, lr interrupt_vector: .hword 0xFFFE reset_vector: .hword 0xFFFC .ltorg