commit 4b2a4c37307bc660cf7f8101663d88d7c45a1b6d Author: Will Angenent Date: Sun Apr 29 20:41:11 2018 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05cf7a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +test-cpu +cpu.prof +mem.prof +mos6502go.test diff --git a/6502_functional_test.a65 b/6502_functional_test.a65 new file mode 100644 index 0000000..8a6c850 --- /dev/null +++ b/6502_functional_test.a65 @@ -0,0 +1,6102 @@ +; +; 6 5 0 2 F U N C T I O N A L T E S T +; +; Copyright (C) 2012-2015 Klaus Dormann +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +; This program is designed to test all opcodes of a 6502 emulator using all +; addressing modes with focus on propper setting of the processor status +; register bits. +; +; version 04-dec-2017 +; contact info at http://2m5.de or email K@2m5.de +; +; assembled with AS65 from http://www.kingswood-consulting.co.uk/assemblers/ +; command line switches: -l -m -s2 -w -h0 +; | | | | no page headers in listing +; | | | wide listing (133 char/col) +; | | write intel hex file instead of binary +; | expand macros in listing +; generate pass2 listing +; +; No IO - should be run from a monitor with access to registers. +; To run load intel hex image with a load command, than alter PC to 400 hex +; (code_segment) and enter a go command. +; Loop on program counter determines error or successful completion of test. +; Check listing for relevant traps (jump/branch *). +; Please note that in early tests some instructions will have to be used before +; they are actually tested! +; +; RESET, NMI or IRQ should not occur and will be trapped if vectors are enabled. +; Tests documented behavior of the original NMOS 6502 only! No unofficial +; opcodes. Additional opcodes of newer versions of the CPU (65C02, 65816) will +; not be tested. Decimal ops will only be tested with valid BCD operands and +; N V Z flags will be ignored. +; +; Debugging hints: +; Most of the code is written sequentially. if you hit a trap, check the +; immediately preceeding code for the instruction to be tested. Results are +; tested first, flags are checked second by pushing them onto the stack and +; pulling them to the accumulator after the result was checked. The "real" +; flags are no longer valid for the tested instruction at this time! +; If the tested instruction was indexed, the relevant index (X or Y) must +; also be checked. Opposed to the flags, X and Y registers are still valid. +; +; versions: +; 28-jul-2012 1st version distributed for testing +; 29-jul-2012 fixed references to location 0, now #0 +; added license - GPLv3 +; 30-jul-2012 added configuration options +; 01-aug-2012 added trap macro to allow user to change error handling +; 01-dec-2012 fixed trap in branch field must be a branch +; 02-mar-2013 fixed PLA flags not tested +; 19-jul-2013 allowed ROM vectors to be loaded when load_data_direct = 0 +; added test sequence check to detect if tests jump their fence +; 23-jul-2013 added RAM integrity check option +; 16-aug-2013 added error report to standard output option +; 13-dec-2014 added binary/decimal opcode table switch test +; 14-dec-2014 improved relative address test +; 23-aug-2015 added option to disable self modifying tests +; 24-aug-2015 all self modifying immediate opcodes now execute in data RAM +; added small branch offset pretest +; 21-oct-2015 added option to disable decimal mode ADC & SBC tests +; 04-dec-2017 fixed BRK only tested with interrupts enabled +; added option to skip the remainder of a failing test +; in report.i65 + + +; C O N F I G U R A T I O N + +;ROM_vectors writable (0=no, 1=yes) +;if ROM vectors can not be used interrupts will not be trapped +;as a consequence BRK can not be tested but will be emulated to test RTI +ROM_vectors = 1 + +;load_data_direct (0=move from code segment, 1=load directly) +;loading directly is preferred but may not be supported by your platform +;0 produces only consecutive object code, 1 is not suitable for a binary image +load_data_direct = 1 + +;I_flag behavior (0=force enabled, 1=force disabled, 2=prohibit change, 3=allow +;change) 2 requires extra code and is not recommended. SEI & CLI can only be +;tested if you allow changing the interrupt status (I_flag = 3) +I_flag = 3 + +;configure memory - try to stay away from memory used by the system +;zero_page memory start address, $50 (80) consecutive Bytes required +; add 2 if I_flag = 2 +zero_page = $0 + +;data_segment memory start address, $6A (106) consecutive Bytes required +data_segment = $200 + if (data_segment & $ff) != 0 + ERROR ERROR ERROR low byte of data_segment MUST be $00 !! + endif + +;code_segment memory start address, 13kB of consecutive space required +; add 2.5 kB if I_flag = 2 +code_segment = $800 + +;self modifying code may be disabled to allow running in ROM +;0=part of the code is self modifying and must reside in RAM +;1=tests disabled: branch range +disable_selfmod = 0 + +;report errors through I/O channel (0=use standard self trap loops, 1=include +;report.i65 as I/O channel, add 3.5 kB) +report = 0 + +;RAM integrity test option. Checks for undesired RAM writes. +;set lowest non RAM or RAM mirror address page (-1=disable, 0=64k, $40=16k) +;leave disabled if a monitor, OS or background interrupt is allowed to alter RAM +ram_top = -1 + +;disable test decimal mode ADC & SBC, 0=enable, 1=disable, +;2=disable including decimal flag in processor status +disable_decimal = 0 + + noopt ;do not take shortcuts + +;macros for error & success traps to allow user modification +;example: +;trap macro +; jsr my_error_handler +; endm +;trap_eq macro +; bne skip\? +; trap ;failed equal (zero) +;skip\? +; endm +; +; my_error_handler should pop the calling address from the stack and report it. +; putting larger portions of code (more than 3 bytes) inside the trap macro +; may lead to branch range problems for some tests. + if report = 0 +trap macro + jmp * ;failed anyway + endm +trap_eq macro + beq * ;failed equal (zero) + endm +trap_ne macro + bne * ;failed not equal (non zero) + endm +trap_cs macro + bcs * ;failed carry set + endm +trap_cc macro + bcc * ;failed carry clear + endm +trap_mi macro + bmi * ;failed minus (bit 7 set) + endm +trap_pl macro + bpl * ;failed plus (bit 7 clear) + endm +trap_vs macro + bvs * ;failed overflow set + endm +trap_vc macro + bvc * ;failed overflow clear + endm +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible +success macro + jmp * ;test passed, no errors + endm + endif + if report = 1 +trap macro + jsr report_error + endm +trap_eq macro + bne skip\? + trap ;failed equal (zero) +skip\? + endm +trap_ne macro + beq skip\? + trap ;failed not equal (non zero) +skip\? + endm +trap_cs macro + bcc skip\? + trap ;failed carry set +skip\? + endm +trap_cc macro + bcs skip\? + trap ;failed carry clear +skip\? + endm +trap_mi macro + bpl skip\? + trap ;failed minus (bit 7 set) +skip\? + endm +trap_pl macro + bmi skip\? + trap ;failed plus (bit 7 clear) +skip\? + endm +trap_vs macro + bvc skip\? + trap ;failed overflow set +skip\? + endm +trap_vc macro + bvs skip\? + trap ;failed overflow clear +skip\? + endm +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible +success macro + jsr report_success + endm + endif + + +carry equ %00000001 ;flag bits in status +zero equ %00000010 +intdis equ %00000100 +decmode equ %00001000 +break equ %00010000 +reserv equ %00100000 +overfl equ %01000000 +minus equ %10000000 + +fc equ carry +fz equ zero +fzc equ carry+zero +fv equ overfl +fvz equ overfl+zero +fn equ minus +fnc equ minus+carry +fnz equ minus+zero +fnzc equ minus+zero+carry +fnv equ minus+overfl + +fao equ break+reserv ;bits always on after PHP, BRK +fai equ fao+intdis ;+ forced interrupt disable +faod equ fao+decmode ;+ ignore decimal +faid equ fai+decmode ;+ ignore decimal +m8 equ $ff ;8 bit mask +m8i equ $ff&~intdis ;8 bit mask - interrupt disable + +;macros to allow masking of status bits. +;masking test of decimal bit +;masking of interrupt enable/disable on load and compare +;masking of always on bits after PHP or BRK (unused & break) on compare + if disable_decimal < 2 + if I_flag = 0 +load_flag macro + lda #\1&m8i ;force enable interrupts (mask I) + endm +cmp_flag macro + cmp #(\1|fao)&m8i ;I_flag is always enabled + always on bits + endm +eor_flag macro + eor #(\1&m8i|fao) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 1 +load_flag macro + lda #\1|intdis ;force disable interrupts + endm +cmp_flag macro + cmp #(\1|fai)&m8 ;I_flag is always disabled + always on bits + endm +eor_flag macro + eor #(\1|fai) ;invert expected flags + always on bits + I + endm + endif + if I_flag = 2 +load_flag macro + lda #\1 + ora flag_I_on ;restore I-flag + and flag_I_off + endm +cmp_flag macro + eor flag_I_on ;I_flag is never changed + cmp #(\1|fao)&m8i ;expected flags + always on bits, mask I + endm +eor_flag macro + eor flag_I_on ;I_flag is never changed + eor #(\1&m8i|fao) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 3 +load_flag macro + lda #\1 ;allow test to change I-flag (no mask) + endm +cmp_flag macro + cmp #(\1|fao)&m8 ;expected flags + always on bits + endm +eor_flag macro + eor #\1|fao ;invert expected flags + always on bits + endm + endif + else + if I_flag = 0 +load_flag macro + lda #\1&m8i ;force enable interrupts (mask I) + endm +cmp_flag macro + ora #decmode ;ignore decimal mode bit + cmp #(\1|faod)&m8i ;I_flag is always enabled + always on bits + endm +eor_flag macro + ora #decmode ;ignore decimal mode bit + eor #(\1&m8i|faod) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 1 +load_flag macro + lda #\1|intdis ;force disable interrupts + endm +cmp_flag macro + ora #decmode ;ignore decimal mode bit + cmp #(\1|faid)&m8 ;I_flag is always disabled + always on bits + endm +eor_flag macro + ora #decmode ;ignore decimal mode bit + eor #(\1|faid) ;invert expected flags + always on bits + I + endm + endif + if I_flag = 2 +load_flag macro + lda #\1 + ora flag_I_on ;restore I-flag + and flag_I_off + endm +cmp_flag macro + eor flag_I_on ;I_flag is never changed + ora #decmode ;ignore decimal mode bit + cmp #(\1|faod)&m8i ;expected flags + always on bits, mask I + endm +eor_flag macro + eor flag_I_on ;I_flag is never changed + ora #decmode ;ignore decimal mode bit + eor #(\1&m8i|faod) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 3 +load_flag macro + lda #\1 ;allow test to change I-flag (no mask) + endm +cmp_flag macro + ora #decmode ;ignore decimal mode bit + cmp #(\1|faod)&m8 ;expected flags + always on bits + endm +eor_flag macro + ora #decmode ;ignore decimal mode bit + eor #\1|faod ;invert expected flags + always on bits + endm + endif + endif + +;macros to set (register|memory|zeropage) & status +set_stat macro ;setting flags in the processor status register + load_flag \1 + pha ;use stack to load status + plp + endm + +set_a macro ;precharging accu & status + load_flag \2 + pha ;use stack to load status + lda #\1 ;precharge accu + plp + endm + +set_x macro ;precharging index & status + load_flag \2 + pha ;use stack to load status + ldx #\1 ;precharge index x + plp + endm + +set_y macro ;precharging index & status + load_flag \2 + pha ;use stack to load status + ldy #\1 ;precharge index y + plp + endm + +set_ax macro ;precharging indexed accu & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;precharge accu + plp + endm + +set_ay macro ;precharging indexed accu & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,y ;precharge accu + plp + endm + +set_z macro ;precharging indexed zp & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to zeropage + sta zpt + plp + endm + +set_zx macro ;precharging zp,x & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to indexed zeropage + sta zpt,x + plp + endm + +set_abs macro ;precharging indexed memory & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to memory + sta abst + plp + endm + +set_absx macro ;precharging abs,x & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to indexed memory + sta abst,x + plp + endm + +;macros to test (register|memory|zeropage) & status & (mask) +tst_stat macro ;testing flags in the processor status register + php ;save status + pla ;use stack to retrieve status + pha + cmp_flag \1 + trap_ne + plp ;restore status + endm + +tst_a macro ;testing result in accu & flags + php ;save flags + cmp #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + endm + +tst_x macro ;testing result in x index & flags + php ;save flags + cpx #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + endm + +tst_y macro ;testing result in y index & flags + php ;save flags + cpy #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + endm + +tst_ax macro ;indexed testing result in accu & flags + php ;save flags + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne ; + endm + +tst_ay macro ;indexed testing result in accu & flags + php ;save flags + cmp \1,y ;test result + trap_ne ; + pla ;load status + eor_flag \3 + cmp \2,y ;test flags + trap_ne + endm + +tst_z macro ;indexed testing result in zp & flags + php ;save flags + lda zpt + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +tst_zx macro ;testing result in zp,x & flags + php ;save flags + lda zpt,x + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +tst_abs macro ;indexed testing result in memory & flags + php ;save flags + lda abst + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +tst_absx macro ;testing result in abs,x & flags + php ;save flags + lda abst,x + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +; RAM integrity test +; verifies that none of the previous tests has altered RAM outside of the +; designated write areas. +; uses zpt word as indirect pointer, zpt+2 word as checksum + if ram_top > -1 +check_ram macro + cld + lda #0 + sta zpt ;set low byte of indirect pointer + sta zpt+3 ;checksum high byte + if disable_selfmod = 0 + sta range_adr ;reset self modifying code + endif + clc + ldx #zp_bss-zero_page ;zeropage - write test area +ccs3\? adc zero_page,x + bcc ccs2\? + inc zpt+3 ;carry to high byte + clc +ccs2\? inx + bne ccs3\? + ldx #hi(abs1) ;set high byte of indirect pointer + stx zpt+1 + ldy #lo(abs1) ;data after write & execute test area +ccs5\? adc (zpt),y + bcc ccs4\? + inc zpt+3 ;carry to high byte + clc +ccs4\? iny + bne ccs5\? + inx ;advance RAM high address + stx zpt+1 + cpx #ram_top + bne ccs5\? + sta zpt+2 ;checksum low is + cmp ram_chksm ;checksum low expected + trap_ne ;checksum mismatch + lda zpt+3 ;checksum high is + cmp ram_chksm+1 ;checksum high expected + trap_ne ;checksum mismatch + endm + else +check_ram macro + ;RAM check disabled - RAM size not set + endm + endif + +next_test macro ;make sure, tests don't jump the fence + lda test_case ;previous test + cmp #test_num + trap_ne ;test is out of sequence +test_num = test_num + 1 + lda #test_num ;*** next tests' number + sta test_case + ;check_ram ;uncomment to find altered RAM after each test + endm + + if load_data_direct = 1 + data + else + bss ;uninitialized segment, copy of data at end of code! + endif + org zero_page +;break test interrupt save +irq_a ds 1 ;a register +irq_x ds 1 ;x register + if I_flag = 2 +;masking for I bit in status +flag_I_on ds 1 ;or mask to load flags +flag_I_off ds 1 ;and mask to load flags + endif +zpt ;5 bytes store/modify test area +;add/subtract operand generation and result/flag prediction +adfc ds 1 ;carry flag before op +ad1 ds 1 ;operand 1 - accumulator +ad2 ds 1 ;operand 2 - memory / immediate +adrl ds 1 ;expected result bits 0-7 +adrh ds 1 ;expected result bit 8 (carry) +adrf ds 1 ;expected flags NV0000ZC (only binary mode) +sb2 ds 1 ;operand 2 complemented for subtract +zp_bss +zp1 db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +zp7f db $7f ;test pattern for compare +;logical zeropage operands +zpOR db 0,$1f,$71,$80 ;test pattern for OR +zpAN db $0f,$ff,$7f,$80 ;test pattern for AND +zpEO db $ff,$0f,$8f,$8f ;test pattern for EOR +;indirect addressing pointers +ind1 dw abs1 ;indirect pointer to pattern in absolute memory + dw abs1+1 + dw abs1+2 + dw abs1+3 + dw abs7f +inw1 dw abs1-$f8 ;indirect pointer for wrap-test pattern +indt dw abst ;indirect pointer to store area in absolute memory + dw abst+1 + dw abst+2 + dw abst+3 +inwt dw abst-$f8 ;indirect pointer for wrap-test store +indAN dw absAN ;indirect pointer to AND pattern in absolute memory + dw absAN+1 + dw absAN+2 + dw absAN+3 +indEO dw absEO ;indirect pointer to EOR pattern in absolute memory + dw absEO+1 + dw absEO+2 + dw absEO+3 +indOR dw absOR ;indirect pointer to OR pattern in absolute memory + dw absOR+1 + dw absOR+2 + dw absOR+3 +;add/subtract indirect pointers +adi2 dw ada2 ;indirect pointer to operand 2 in absolute memory +sbi2 dw sba2 ;indirect pointer to complemented operand 2 (SBC) +adiy2 dw ada2-$ff ;with offset for indirect indexed +sbiy2 dw sba2-$ff +zp_bss_end + + org data_segment +test_case ds 1 ;current test number +ram_chksm ds 2 ;checksum for RAM integrity test +;add/subtract operand copy - abs tests write area +abst ;5 bytes store/modify test area +ada2 ds 1 ;operand 2 +sba2 ds 1 ;operand 2 complemented for subtract + ds 3 ;fill remaining bytes +data_bss + if load_data_direct = 1 +ex_andi and #0 ;execute immediate opcodes + rts +ex_eori eor #0 ;execute immediate opcodes + rts +ex_orai ora #0 ;execute immediate opcodes + rts +ex_adci adc #0 ;execute immediate opcodes + rts +ex_sbci sbc #0 ;execute immediate opcodes + rts + else +ex_andi ds 3 +ex_eori ds 3 +ex_orai ds 3 +ex_adci ds 3 +ex_sbci ds 3 + endif +abs1 db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +abs7f db $7f ;test pattern for compare +;loads +fLDx db fn,fn,0,fz ;expected flags for load +;shifts +rASL ;expected result ASL & ROL -carry +rROL db $86,$04,$82,0 ; " +rROLc db $87,$05,$83,1 ;expected result ROL +carry +rLSR ;expected result LSR & ROR -carry +rROR db $61,$41,$20,0 ; " +rRORc db $e1,$c1,$a0,$80 ;expected result ROR +carry +fASL ;expected flags for shifts +fROL db fnc,fc,fn,fz ;no carry in +fROLc db fnc,fc,fn,0 ;carry in +fLSR +fROR db fc,0,fc,fz ;no carry in +fRORc db fnc,fn,fnc,fn ;carry in +;increments (decrements) +rINC db $7f,$80,$ff,0,1 ;expected result for INC/DEC +fINC db 0,fn,fn,fz,0 ;expected flags for INC/DEC +;logical memory operand +absOR db 0,$1f,$71,$80 ;test pattern for OR +absAN db $0f,$ff,$7f,$80 ;test pattern for AND +absEO db $ff,$0f,$8f,$8f ;test pattern for EOR +;logical accu operand +absORa db 0,$f1,$1f,0 ;test pattern for OR +absANa db $f0,$ff,$ff,$ff ;test pattern for AND +absEOa db $ff,$f0,$f0,$0f ;test pattern for EOR +;logical results +absrlo db 0,$ff,$7f,$80 +absflo db fz,fn,0,fn +data_bss_end + + + code + org code_segment +start cld + ldx #$ff + txs + lda #0 ;*** test 0 = initialize + sta test_case +test_num = 0 + +;stop interrupts before initializing BSS + if I_flag = 1 + sei + endif + +;initialize I/O for report channel + if report = 1 + jsr report_init + endif + +;pretest small branch offset + ldx #5 + jmp psb_test +psb_bwok + ldy #5 + bne psb_forw + trap ;branch should be taken + dey ;forward landing zone + dey + dey + dey + dey +psb_forw + dey + dey + dey + dey + dey + beq psb_fwok + trap ;forward offset + + dex ;backward landing zone + dex + dex + dex + dex +psb_back + dex + dex + dex + dex + dex + beq psb_bwok + trap ;backward offset +psb_test + bne psb_back + trap ;branch should be taken +psb_fwok + +;initialize BSS segment + if load_data_direct != 1 + ldx #zp_end-zp_init-1 +ld_zp lda zp_init,x + sta zp_bss,x + dex + bpl ld_zp + ldx #data_end-data_init-1 +ld_data lda data_init,x + sta data_bss,x + dex + bpl ld_data + if ROM_vectors = 1 + ldx #5 +ld_vect lda vec_init,x + sta vec_bss,x + dex + bpl ld_vect + endif + endif + +;retain status of interrupt flag + if I_flag = 2 + php + pla + and #4 ;isolate flag + sta flag_I_on ;or mask + eor #lo(~4) ;reverse + sta flag_I_off ;and mask + endif + +;generate checksum for RAM integrity test + if ram_top > -1 + lda #0 + sta zpt ;set low byte of indirect pointer + sta ram_chksm+1 ;checksum high byte + if disable_selfmod = 0 + sta range_adr ;reset self modifying code + endif + clc + ldx #zp_bss-zero_page ;zeropage - write test area +gcs3 adc zero_page,x + bcc gcs2 + inc ram_chksm+1 ;carry to high byte + clc +gcs2 inx + bne gcs3 + ldx #hi(abs1) ;set high byte of indirect pointer + stx zpt+1 + ldy #lo(abs1) ;data after write & execute test area +gcs5 adc (zpt),y + bcc gcs4 + inc ram_chksm+1 ;carry to high byte + clc +gcs4 iny + bne gcs5 + inx ;advance RAM high address + stx zpt+1 + cpx #ram_top + bne gcs5 + sta ram_chksm ;checksum complete + endif + next_test + + if disable_selfmod = 0 +;testing relative addressing with BEQ + ldy #$fe ;testing maximum range, not -1/-2 (invalid/self adr) +range_loop + dey ;next relative address + tya + tax ;precharge count to end of loop + bpl range_fw ;calculate relative address + clc ;avoid branch self or to relative address of branch + adc #2 + nop ;offset landing zone - tolerate +/-5 offset to branch + nop + nop + nop + nop +range_fw + nop + nop + nop + nop + nop + eor #$7f ;complement except sign + sta range_adr ;load into test target + lda #0 ;should set zero flag in status register + jmp range_op + + dex ; offset landing zone - backward branch too far + dex + dex + dex + dex + ;relative address target field with branch under test in the middle + dex ;-128 - max backward + dex + dex + dex + dex + dex + dex + dex + dex ;-120 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-110 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-100 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-90 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-80 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-70 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-60 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-50 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-40 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-30 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-20 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-10 + dex + dex + dex + dex + dex + dex + dex ;-3 +range_op ;test target with zero flag=0, z=1 if previous dex +range_adr = *+1 ;modifiable relative address + beq *+64 ;+64 if called without modification + dex ;+0 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+10 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+20 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+30 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+40 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+50 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+60 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+70 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+80 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+90 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+100 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+110 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+120 + dex + dex + dex + dex + dex + dex + nop ;offset landing zone - forward branch too far + nop + nop + nop + nop + beq range_ok ;+127 - max forward + trap ; bad range + nop ;offset landing zone - tolerate +/-5 offset to branch + nop + nop + nop + nop +range_ok + nop + nop + nop + nop + nop + cpy #0 + beq range_end + jmp range_loop +range_end ;range test successful + endif + next_test + +;partial test BNE & CMP, CPX, CPY immediate + cpy #1 ;testing BNE true + bne test_bne + trap +test_bne + lda #0 + cmp #0 ;test compare immediate + trap_ne + trap_cc + trap_mi + cmp #1 + trap_eq + trap_cs + trap_pl + tax + cpx #0 ;test compare x immediate + trap_ne + trap_cc + trap_mi + cpx #1 + trap_eq + trap_cs + trap_pl + tay + cpy #0 ;test compare y immediate + trap_ne + trap_cc + trap_mi + cpy #1 + trap_eq + trap_cs + trap_pl + next_test +;testing stack operations PHA PHP PLA PLP + + ldx #$ff ;initialize stack + txs + lda #$55 + pha + lda #$aa + pha + cmp $1fe ;on stack ? + trap_ne + tsx + txa ;overwrite accu + cmp #$fd ;sp decremented? + trap_ne + pla + cmp #$aa ;successful retreived from stack? + trap_ne + pla + cmp #$55 + trap_ne + cmp $1ff ;remains on stack? + trap_ne + tsx + cpx #$ff ;sp incremented? + trap_ne + next_test + +;testing branch decisions BPL BMI BVC BVS BCC BCS BNE BEQ + set_stat $ff ;all on + bpl nbr1 ;branches should not be taken + bvc nbr2 + bcc nbr3 + bne nbr4 + bmi br1 ;branches should be taken + trap +br1 bvs br2 + trap +br2 bcs br3 + trap +br3 beq br4 + trap +nbr1 + trap ;previous bpl taken +nbr2 + trap ;previous bvc taken +nbr3 + trap ;previous bcc taken +nbr4 + trap ;previous bne taken +br4 php + tsx + cpx #$fe ;sp after php? + trap_ne + pla + cmp_flag $ff ;returned all flags on? + trap_ne + tsx + cpx #$ff ;sp after php? + trap_ne + set_stat 0 ;all off + bmi nbr11 ;branches should not be taken + bvs nbr12 + bcs nbr13 + beq nbr14 + bpl br11 ;branches should be taken + trap +br11 bvc br12 + trap +br12 bcc br13 + trap +br13 bne br14 + trap +nbr11 + trap ;previous bmi taken +nbr12 + trap ;previous bvs taken +nbr13 + trap ;previous bcs taken +nbr14 + trap ;previous beq taken +br14 php + pla + cmp_flag 0 ;flags off except break (pushed by sw) + reserved? + trap_ne + ;crosscheck flags + set_stat zero + bne brzs1 + beq brzs2 +brzs1 + trap ;branch zero/non zero +brzs2 bcs brzs3 + bcc brzs4 +brzs3 + trap ;branch carry/no carry +brzs4 bmi brzs5 + bpl brzs6 +brzs5 + trap ;branch minus/plus +brzs6 bvs brzs7 + bvc brzs8 +brzs7 + trap ;branch overflow/no overflow +brzs8 + set_stat carry + beq brcs1 + bne brcs2 +brcs1 + trap ;branch zero/non zero +brcs2 bcc brcs3 + bcs brcs4 +brcs3 + trap ;branch carry/no carry +brcs4 bmi brcs5 + bpl brcs6 +brcs5 + trap ;branch minus/plus +brcs6 bvs brcs7 + bvc brcs8 +brcs7 + trap ;branch overflow/no overflow + +brcs8 + set_stat minus + beq brmi1 + bne brmi2 +brmi1 + trap ;branch zero/non zero +brmi2 bcs brmi3 + bcc brmi4 +brmi3 + trap ;branch carry/no carry +brmi4 bpl brmi5 + bmi brmi6 +brmi5 + trap ;branch minus/plus +brmi6 bvs brmi7 + bvc brmi8 +brmi7 + trap ;branch overflow/no overflow +brmi8 + set_stat overfl + beq brvs1 + bne brvs2 +brvs1 + trap ;branch zero/non zero +brvs2 bcs brvs3 + bcc brvs4 +brvs3 + trap ;branch carry/no carry +brvs4 bmi brvs5 + bpl brvs6 +brvs5 + trap ;branch minus/plus +brvs6 bvc brvs7 + bvs brvs8 +brvs7 + trap ;branch overflow/no overflow +brvs8 + set_stat $ff-zero + beq brzc1 + bne brzc2 +brzc1 + trap ;branch zero/non zero +brzc2 bcc brzc3 + bcs brzc4 +brzc3 + trap ;branch carry/no carry +brzc4 bpl brzc5 + bmi brzc6 +brzc5 + trap ;branch minus/plus +brzc6 bvc brzc7 + bvs brzc8 +brzc7 + trap ;branch overflow/no overflow +brzc8 + set_stat $ff-carry + bne brcc1 + beq brcc2 +brcc1 + trap ;branch zero/non zero +brcc2 bcs brcc3 + bcc brcc4 +brcc3 + trap ;branch carry/no carry +brcc4 bpl brcc5 + bmi brcc6 +brcc5 + trap ;branch minus/plus +brcc6 bvc brcc7 + bvs brcc8 +brcc7 + trap ;branch overflow/no overflow +brcc8 + set_stat $ff-minus + bne brpl1 + beq brpl2 +brpl1 + trap ;branch zero/non zero +brpl2 bcc brpl3 + bcs brpl4 +brpl3 + trap ;branch carry/no carry +brpl4 bmi brpl5 + bpl brpl6 +brpl5 + trap ;branch minus/plus +brpl6 bvc brpl7 + bvs brpl8 +brpl7 + trap ;branch overflow/no overflow +brpl8 + set_stat $ff-overfl + bne brvc1 + beq brvc2 +brvc1 + trap ;branch zero/non zero +brvc2 bcc brvc3 + bcs brvc4 +brvc3 + trap ;branch carry/no carry +brvc4 bpl brvc5 + bmi brvc6 +brvc5 + trap ;branch minus/plus +brvc6 bvs brvc7 + bvc brvc8 +brvc7 + trap ;branch overflow/no overflow +brvc8 + next_test + +; test PHA does not alter flags or accumulator but PLA does + ldx #$55 ;x & y protected + ldy #$aa + set_a 1,$ff ;push + pha + tst_a 1,$ff + set_a 0,0 + pha + tst_a 0,0 + set_a $ff,$ff + pha + tst_a $ff,$ff + set_a 1,0 + pha + tst_a 1,0 + set_a 0,$ff + pha + tst_a 0,$ff + set_a $ff,0 + pha + tst_a $ff,0 + set_a 0,$ff ;pull + pla + tst_a $ff,$ff-zero + set_a $ff,0 + pla + tst_a 0,zero + set_a $fe,$ff + pla + tst_a 1,$ff-zero-minus + set_a 0,0 + pla + tst_a $ff,minus + set_a $ff,$ff + pla + tst_a 0,$ff-minus + set_a $fe,0 + pla + tst_a 1,0 + cpx #$55 ;x & y unchanged? + trap_ne + cpy #$aa + trap_ne + next_test + +; partial pretest EOR # + set_a $3c,0 + eor #$c3 + tst_a $ff,fn + set_a $c3,0 + eor #$c3 + tst_a 0,fz + next_test + +; PC modifying instructions except branches (NOP, JMP, JSR, RTS, BRK, RTI) +; testing NOP + ldx #$24 + ldy #$42 + set_a $18,0 + nop + tst_a $18,0 + cpx #$24 + trap_ne + cpy #$42 + trap_ne + ldx #$db + ldy #$bd + set_a $e7,$ff + nop + tst_a $e7,$ff + cpx #$db + trap_ne + cpy #$bd + trap_ne + next_test + +; jump absolute + set_stat $0 + lda #'F' + ldx #'A' + ldy #'R' ;N=0, V=0, Z=0, C=0 + jmp test_far + nop + nop + trap_ne ;runover protection + inx + inx +far_ret + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('F'^$aa) ;returned registers OK? + trap_ne + cpx #('A'+1) + trap_ne + cpy #('R'-3) + trap_ne + dex + iny + iny + iny + eor #$aa ;N=0, V=1, Z=0, C=1 + jmp test_near + nop + nop + trap_ne ;runover protection + inx + inx +test_near + trap_eq ;passed flags OK? + trap_mi + trap_cc + trap_vc + cmp #'F' ;passed registers OK? + trap_ne + cpx #'A' + trap_ne + cpy #'R' + trap_ne + next_test + +; jump indirect + set_stat 0 + lda #'I' + ldx #'N' + ldy #'D' ;N=0, V=0, Z=0, C=0 + jmp (ptr_tst_ind) + nop + trap_ne ;runover protection + dey + dey +ind_ret + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('I'^$aa) ;returned registers OK? + trap_ne + cpx #('N'+1) + trap_ne + cpy #('D'-6) + trap_ne + tsx ;SP check + cpx #$ff + trap_ne + next_test + +; jump subroutine & return from subroutine + set_stat 0 + lda #'J' + ldx #'S' + ldy #'R' ;N=0, V=0, Z=0, C=0 + jsr test_jsr +jsr_ret = *-1 ;last address of jsr = return address + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('J'^$aa) ;returned registers OK? + trap_ne + cpx #('S'+1) + trap_ne + cpy #('R'-6) + trap_ne + tsx ;sp? + cpx #$ff + trap_ne + next_test + +; break & return from interrupt + if ROM_vectors = 1 + load_flag 0 ;with interrupts enabled if allowed! + pha + lda #'B' + ldx #'R' + ldy #'K' + plp ;N=0, V=0, Z=0, C=0 + brk + else + lda #hi brk_ret0 ;emulated break + pha + lda #lo brk_ret0 + pha + load_flag fao ;set break & unused on stack + pha + load_flag intdis ;during interrupt + pha + lda #'B' + ldx #'R' + ldy #'K' + plp ;N=0, V=0, Z=0, C=0 + jmp irq_trap + endif + dey ;should not be executed +brk_ret0 ;address of break return + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + cmp #'B'^$aa ;returned registers OK? + ;the IRQ vector was never executed if A & X stay unmodified + trap_ne + cpx #'R'+1 + trap_ne + cpy #'K'-6 + trap_ne + pla ;returned flags OK (unchanged)? + cmp_flag 0 + trap_ne + tsx ;sp? + cpx #$ff + trap_ne + if ROM_vectors = 1 + load_flag $ff ;with interrupts disabled if allowed! + pha + lda #$ff-'B' + ldx #$ff-'R' + ldy #$ff-'K' + plp ;N=1, V=1, Z=1, C=1 + brk + else + lda #hi brk_ret1 ;emulated break + pha + lda #lo brk_ret1 + pha + load_flag $ff + pha ;set break & unused on stack + pha ;actual flags + lda #$ff-'B' + ldx #$ff-'R' + ldy #$ff-'K' + plp ;N=1, V=1, Z=1, C=1 + jmp irq_trap + endif + dey ;should not be executed +brk_ret1 ;address of break return + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + cmp #($ff-'B')^$aa ;returned registers OK? + ;the IRQ vector was never executed if A & X stay unmodified + trap_ne + cpx #$ff-'R'+1 + trap_ne + cpy #$ff-'K'-6 + trap_ne + pla ;returned flags OK (unchanged)? + cmp_flag $ff + trap_ne + tsx ;sp? + cpx #$ff + trap_ne + next_test + +; test set and clear flags CLC CLI CLD CLV SEC SEI SED + set_stat $ff + clc + tst_stat $ff-carry + sec + tst_stat $ff + if I_flag = 3 + cli + tst_stat $ff-intdis + sei + tst_stat $ff + endif + cld + tst_stat $ff-decmode + sed + tst_stat $ff + clv + tst_stat $ff-overfl + set_stat 0 + tst_stat 0 + sec + tst_stat carry + clc + tst_stat 0 + if I_flag = 3 + sei + tst_stat intdis + cli + tst_stat 0 + endif + sed + tst_stat decmode + cld + tst_stat 0 + set_stat overfl + tst_stat overfl + clv + tst_stat 0 + next_test +; testing index register increment/decrement and transfer +; INX INY DEX DEY TAX TXA TAY TYA + ldx #$fe + set_stat $ff + inx ;ff + tst_x $ff,$ff-zero + inx ;00 + tst_x 0,$ff-minus + inx ;01 + tst_x 1,$ff-minus-zero + dex ;00 + tst_x 0,$ff-minus + dex ;ff + tst_x $ff,$ff-zero + dex ;fe + set_stat 0 + inx ;ff + tst_x $ff,minus + inx ;00 + tst_x 0,zero + inx ;01 + tst_x 1,0 + dex ;00 + tst_x 0,zero + dex ;ff + tst_x $ff,minus + + ldy #$fe + set_stat $ff + iny ;ff + tst_y $ff,$ff-zero + iny ;00 + tst_y 0,$ff-minus + iny ;01 + tst_y 1,$ff-minus-zero + dey ;00 + tst_y 0,$ff-minus + dey ;ff + tst_y $ff,$ff-zero + dey ;fe + set_stat 0 + iny ;ff + tst_y $ff,0+minus + iny ;00 + tst_y 0,zero + iny ;01 + tst_y 1,0 + dey ;00 + tst_y 0,zero + dey ;ff + tst_y $ff,minus + + ldx #$ff + set_stat $ff + txa + tst_a $ff,$ff-zero + php + inx ;00 + plp + txa + tst_a 0,$ff-minus + php + inx ;01 + plp + txa + tst_a 1,$ff-minus-zero + set_stat 0 + txa + tst_a 1,0 + php + dex ;00 + plp + txa + tst_a 0,zero + php + dex ;ff + plp + txa + tst_a $ff,minus + + ldy #$ff + set_stat $ff + tya + tst_a $ff,$ff-zero + php + iny ;00 + plp + tya + tst_a 0,$ff-minus + php + iny ;01 + plp + tya + tst_a 1,$ff-minus-zero + set_stat 0 + tya + tst_a 1,0 + php + dey ;00 + plp + tya + tst_a 0,zero + php + dey ;ff + plp + tya + tst_a $ff,minus + + load_flag $ff + pha + ldx #$ff ;ff + txa + plp + tay + tst_y $ff,$ff-zero + php + inx ;00 + txa + plp + tay + tst_y 0,$ff-minus + php + inx ;01 + txa + plp + tay + tst_y 1,$ff-minus-zero + load_flag 0 + pha + lda #0 + txa + plp + tay + tst_y 1,0 + php + dex ;00 + txa + plp + tay + tst_y 0,zero + php + dex ;ff + txa + plp + tay + tst_y $ff,minus + + + load_flag $ff + pha + ldy #$ff ;ff + tya + plp + tax + tst_x $ff,$ff-zero + php + iny ;00 + tya + plp + tax + tst_x 0,$ff-minus + php + iny ;01 + tya + plp + tax + tst_x 1,$ff-minus-zero + load_flag 0 + pha + lda #0 ;preset status + tya + plp + tax + tst_x 1,0 + php + dey ;00 + tya + plp + tax + tst_x 0,zero + php + dey ;ff + tya + plp + tax + tst_x $ff,minus + next_test + +;TSX sets NZ - TXS does not +; This section also tests for proper stack wrap around. + ldx #1 ;01 + set_stat $ff + txs + php + lda $101 + cmp_flag $ff + trap_ne + set_stat 0 + txs + php + lda $101 + cmp_flag 0 + trap_ne + dex ;00 + set_stat $ff + txs + php + lda $100 + cmp_flag $ff + trap_ne + set_stat 0 + txs + php + lda $100 + cmp_flag 0 + trap_ne + dex ;ff + set_stat $ff + txs + php + lda $1ff + cmp_flag $ff + trap_ne + set_stat 0 + txs + php + lda $1ff + cmp_flag 0 + + ldx #1 + txs ;sp=01 + set_stat $ff + tsx ;clears Z, N + php ;sp=00 + cpx #1 + trap_ne + lda $101 + cmp_flag $ff-minus-zero + trap_ne + set_stat $ff + tsx ;clears N, sets Z + php ;sp=ff + cpx #0 + trap_ne + lda $100 + cmp_flag $ff-minus + trap_ne + set_stat $ff + tsx ;clears N, sets Z + php ;sp=fe + cpx #$ff + trap_ne + lda $1ff + cmp_flag $ff-zero + trap_ne + + ldx #1 + txs ;sp=01 + set_stat 0 + tsx ;clears Z, N + php ;sp=00 + cpx #1 + trap_ne + lda $101 + cmp_flag 0 + trap_ne + set_stat 0 + tsx ;clears N, sets Z + php ;sp=ff + cpx #0 + trap_ne + lda $100 + cmp_flag zero + trap_ne + set_stat 0 + tsx ;clears N, sets Z + php ;sp=fe + cpx #$ff + trap_ne + lda $1ff + cmp_flag minus + trap_ne + pla ;sp=ff + next_test + +; testing index register load & store LDY LDX STY STX all addressing modes +; LDX / STX - zp,y / abs,y + ldy #3 +tldx + set_stat 0 + ldx zp1,y + php ;test stores do not alter flags + txa + eor #$c3 + plp + sta abst,y + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dey + bpl tldx + + ldy #3 +tldx1 + set_stat $ff + ldx zp1,y + php ;test stores do not alter flags + txa + eor #$c3 + plp + sta abst,y + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dey + bpl tldx1 + + ldy #3 +tldx2 + set_stat 0 + ldx abs1,y + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt,y + php ;flags after load/store sequence + eor #$c3 + cmp zp1,y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dey + bpl tldx2 + + ldy #3 +tldx3 + set_stat $ff + ldx abs1,y + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt,y + php ;flags after load/store sequence + eor #$c3 + cmp zp1,y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dey + bpl tldx3 + + ldy #3 ;testing store result + ldx #0 +tstx lda zpt,y + eor #$c3 + cmp zp1,y + trap_ne ;store to zp data + stx zpt,y ;clear + lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstx + next_test + +; indexed wraparound test (only zp should wrap) + ldy #3+$fa +tldx4 ldx zp1-$fa&$ff,y ;wrap on indexed zp + txa + sta abst-$fa,y ;no STX abs,y! + dey + cpy #$fa + bcs tldx4 + ldy #3+$fa +tldx5 ldx abs1-$fa,y ;no wrap on indexed abs + stx zpt-$fa&$ff,y + dey + cpy #$fa + bcs tldx5 + ldy #3 ;testing wraparound result + ldx #0 +tstx1 lda zpt,y + cmp zp1,y + trap_ne ;store to zp data + stx zpt,y ;clear + lda abst,y + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstx1 + next_test + +; LDY / STY - zp,x / abs,x + ldx #3 +tldy + set_stat 0 + ldy zp1,x + php ;test stores do not alter flags + tya + eor #$c3 + plp + sta abst,x + php ;flags after load/store sequence + eor #$c3 + cmp abs1,x ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldy + + ldx #3 +tldy1 + set_stat $ff + ldy zp1,x + php ;test stores do not alter flags + tya + eor #$c3 + plp + sta abst,x + php ;flags after load/store sequence + eor #$c3 + cmp abs1,x ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldy1 + + ldx #3 +tldy2 + set_stat 0 + ldy abs1,x + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt,x + php ;flags after load/store sequence + eor #$c3 + cmp zp1,x ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldy2 + + ldx #3 +tldy3 + set_stat $ff + ldy abs1,x + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt,x + php ;flags after load/store sequence + eor #$c3 + cmp zp1,x ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldy3 + + ldx #3 ;testing store result + ldy #0 +tsty lda zpt,x + eor #$c3 + cmp zp1,x + trap_ne ;store to zp,x data + sty zpt,x ;clear + lda abst,x + eor #$c3 + cmp abs1,x + trap_ne ;store to abs,x data + txa + sta abst,x ;clear + dex + bpl tsty + next_test + +; indexed wraparound test (only zp should wrap) + ldx #3+$fa +tldy4 ldy zp1-$fa&$ff,x ;wrap on indexed zp + tya + sta abst-$fa,x ;no STX abs,x! + dex + cpx #$fa + bcs tldy4 + ldx #3+$fa +tldy5 ldy abs1-$fa,x ;no wrap on indexed abs + sty zpt-$fa&$ff,x + dex + cpx #$fa + bcs tldy5 + ldx #3 ;testing wraparound result + ldy #0 +tsty1 lda zpt,x + cmp zp1,x + trap_ne ;store to zp,x data + sty zpt,x ;clear + lda abst,x + cmp abs1,x + trap_ne ;store to abs,x data + txa + sta abst,x ;clear + dex + bpl tsty1 + next_test + +; LDX / STX - zp / abs / # + set_stat 0 + ldx zp1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$c3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldx zp1+1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+1 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$82 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldx zp1+2 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+2 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$41 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldx zp1+3 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+3 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #0 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldx zp1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$c3 ;test result + trap_ne ; + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldx zp1+1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+1 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$82 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldx zp1+2 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+2 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$41 ;test result + trap_ne ; + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldx zp1+3 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+3 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #0 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + set_stat 0 + ldx abs1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt + php ;flags after load/store sequence + eor #$c3 + cmp zp1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldx abs1+1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+1 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldx abs1+2 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+2 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldx abs1+3 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+3 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldx abs1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt + php ;flags after load/store sequence + eor #$c3 + tax + cpx zp1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldx abs1+1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+1 + php ;flags after load/store sequence + eor #$c3 + tax + cpx zp1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldx abs1+2 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+2 + php ;flags after load/store sequence + eor #$c3 + tax + cpx zp1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldx abs1+3 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+3 + php ;flags after load/store sequence + eor #$c3 + tax + cpx zp1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + set_stat 0 + ldx #$c3 + php + cpx abs1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldx #$82 + php + cpx abs1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldx #$41 + php + cpx abs1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldx #0 + php + cpx abs1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldx #$c3 + php + cpx abs1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldx #$82 + php + cpx abs1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldx #$41 + php + cpx abs1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldx #0 + php + cpx abs1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + ldx #0 + lda zpt + eor #$c3 + cmp zp1 + trap_ne ;store to zp data + stx zpt ;clear + lda abst + eor #$c3 + cmp abs1 + trap_ne ;store to abs data + stx abst ;clear + lda zpt+1 + eor #$c3 + cmp zp1+1 + trap_ne ;store to zp data + stx zpt+1 ;clear + lda abst+1 + eor #$c3 + cmp abs1+1 + trap_ne ;store to abs data + stx abst+1 ;clear + lda zpt+2 + eor #$c3 + cmp zp1+2 + trap_ne ;store to zp data + stx zpt+2 ;clear + lda abst+2 + eor #$c3 + cmp abs1+2 + trap_ne ;store to abs data + stx abst+2 ;clear + lda zpt+3 + eor #$c3 + cmp zp1+3 + trap_ne ;store to zp data + stx zpt+3 ;clear + lda abst+3 + eor #$c3 + cmp abs1+3 + trap_ne ;store to abs data + stx abst+3 ;clear + next_test + +; LDY / STY - zp / abs / # + set_stat 0 + ldy zp1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$c3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldy zp1+1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+1 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$82 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldy zp1+2 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+2 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$41 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldy zp1+3 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+3 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #0 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldy zp1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$c3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldy zp1+1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+1 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$82 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldy zp1+2 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+2 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$41 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldy zp1+3 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+3 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #0 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + set_stat 0 + ldy abs1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt + php ;flags after load/store sequence + eor #$c3 + tay + cpy zp1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldy abs1+1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+1 + php ;flags after load/store sequence + eor #$c3 + tay + cpy zp1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldy abs1+2 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+2 + php ;flags after load/store sequence + eor #$c3 + tay + cpy zp1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldy abs1+3 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+3 + php ;flags after load/store sequence + eor #$c3 + tay + cpy zp1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldy abs1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt + php ;flags after load/store sequence + eor #$c3 + tay + cmp zp1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldy abs1+1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+1 + php ;flags after load/store sequence + eor #$c3 + tay + cmp zp1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldy abs1+2 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+2 + php ;flags after load/store sequence + eor #$c3 + tay + cmp zp1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldy abs1+3 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+3 + php ;flags after load/store sequence + eor #$c3 + tay + cmp zp1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + + set_stat 0 + ldy #$c3 + php + cpy abs1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldy #$82 + php + cpy abs1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldy #$41 + php + cpy abs1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldy #0 + php + cpy abs1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldy #$c3 + php + cpy abs1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldy #$82 + php + cpy abs1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldy #$41 + php + cpy abs1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldy #0 + php + cpy abs1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + ldy #0 + lda zpt + eor #$c3 + cmp zp1 + trap_ne ;store to zp data + sty zpt ;clear + lda abst + eor #$c3 + cmp abs1 + trap_ne ;store to abs data + sty abst ;clear + lda zpt+1 + eor #$c3 + cmp zp1+1 + trap_ne ;store to zp+1 data + sty zpt+1 ;clear + lda abst+1 + eor #$c3 + cmp abs1+1 + trap_ne ;store to abs+1 data + sty abst+1 ;clear + lda zpt+2 + eor #$c3 + cmp zp1+2 + trap_ne ;store to zp+2 data + sty zpt+2 ;clear + lda abst+2 + eor #$c3 + cmp abs1+2 + trap_ne ;store to abs+2 data + sty abst+2 ;clear + lda zpt+3 + eor #$c3 + cmp zp1+3 + trap_ne ;store to zp+3 data + sty zpt+3 ;clear + lda abst+3 + eor #$c3 + cmp abs1+3 + trap_ne ;store to abs+3 data + sty abst+3 ;clear + next_test + +; testing load / store accumulator LDA / STA all addressing modes +; LDA / STA - zp,x / abs,x + ldx #3 +tldax + set_stat 0 + lda zp1,x + php ;test stores do not alter flags + eor #$c3 + plp + sta abst,x + php ;flags after load/store sequence + eor #$c3 + cmp abs1,x ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldax + + ldx #3 +tldax1 + set_stat $ff + lda zp1,x + php ;test stores do not alter flags + eor #$c3 + plp + sta abst,x + php ;flags after load/store sequence + eor #$c3 + cmp abs1,x ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldax1 + + ldx #3 +tldax2 + set_stat 0 + lda abs1,x + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt,x + php ;flags after load/store sequence + eor #$c3 + cmp zp1,x ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldax2 + + ldx #3 +tldax3 + set_stat $ff + lda abs1,x + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt,x + php ;flags after load/store sequence + eor #$c3 + cmp zp1,x ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldax3 + + ldx #3 ;testing store result + ldy #0 +tstax lda zpt,x + eor #$c3 + cmp zp1,x + trap_ne ;store to zp,x data + sty zpt,x ;clear + lda abst,x + eor #$c3 + cmp abs1,x + trap_ne ;store to abs,x data + txa + sta abst,x ;clear + dex + bpl tstax + next_test + +; LDA / STA - (zp),y / abs,y / (zp,x) + ldy #3 +tlday + set_stat 0 + lda (ind1),y + php ;test stores do not alter flags + eor #$c3 + plp + sta abst,y + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dey + bpl tlday + + ldy #3 +tlday1 + set_stat $ff + lda (ind1),y + php ;test stores do not alter flags + eor #$c3 + plp + sta abst,y + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dey + bpl tlday1 + + ldy #3 ;testing store result + ldx #0 +tstay lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay + + ldy #3 +tlday2 + set_stat 0 + lda abs1,y + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt),y + php ;flags after load/store sequence + eor #$c3 + cmp (ind1),y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dey + bpl tlday2 + + ldy #3 +tlday3 + set_stat $ff + lda abs1,y + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt),y + php ;flags after load/store sequence + eor #$c3 + cmp (ind1),y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dey + bpl tlday3 + + ldy #3 ;testing store result + ldx #0 +tstay1 lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay1 + + ldx #6 + ldy #3 +tldax4 + set_stat 0 + lda (ind1,x) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt,x) + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dex + dex + dey + bpl tldax4 + + ldx #6 + ldy #3 +tldax5 + set_stat $ff + lda (ind1,x) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt,x) + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dex + dex + dey + bpl tldax5 + + ldy #3 ;testing store result + ldx #0 +tstay2 lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay2 + next_test + +; indexed wraparound test (only zp should wrap) + ldx #3+$fa +tldax6 lda zp1-$fa&$ff,x ;wrap on indexed zp + sta abst-$fa,x ;no STX abs,x! + dex + cpx #$fa + bcs tldax6 + ldx #3+$fa +tldax7 lda abs1-$fa,x ;no wrap on indexed abs + sta zpt-$fa&$ff,x + dex + cpx #$fa + bcs tldax7 + + ldx #3 ;testing wraparound result + ldy #0 +tstax1 lda zpt,x + cmp zp1,x + trap_ne ;store to zp,x data + sty zpt,x ;clear + lda abst,x + cmp abs1,x + trap_ne ;store to abs,x data + txa + sta abst,x ;clear + dex + bpl tstax1 + + ldy #3+$f8 + ldx #6+$f8 +tlday4 lda (ind1-$f8&$ff,x) ;wrap on indexed zp indirect + sta abst-$f8,y + dex + dex + dey + cpy #$f8 + bcs tlday4 + ldy #3 ;testing wraparound result + ldx #0 +tstay4 lda abst,y + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay4 + + ldy #3+$f8 +tlday5 lda abs1-$f8,y ;no wrap on indexed abs + sta (inwt),y + dey + cpy #$f8 + bcs tlday5 + ldy #3 ;testing wraparound result + ldx #0 +tstay5 lda abst,y + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay5 + + ldy #3+$f8 + ldx #6+$f8 +tlday6 lda (inw1),y ;no wrap on zp indirect indexed + sta (indt-$f8&$ff,x) + dex + dex + dey + cpy #$f8 + bcs tlday6 + ldy #3 ;testing wraparound result + ldx #0 +tstay6 lda abst,y + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay6 + next_test + +; LDA / STA - zp / abs / # + set_stat 0 + lda zp1 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst + php ;flags after load/store sequence + eor #$c3 + cmp #$c3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + lda zp1+1 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+1 + php ;flags after load/store sequence + eor #$c3 + cmp #$82 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + lda zp1+2 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+2 + php ;flags after load/store sequence + eor #$c3 + cmp #$41 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + lda zp1+3 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+3 + php ;flags after load/store sequence + eor #$c3 + cmp #0 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + set_stat $ff + lda zp1 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst + php ;flags after load/store sequence + eor #$c3 + cmp #$c3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + lda zp1+1 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+1 + php ;flags after load/store sequence + eor #$c3 + cmp #$82 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + lda zp1+2 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+2 + php ;flags after load/store sequence + eor #$c3 + cmp #$41 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + lda zp1+3 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+3 + php ;flags after load/store sequence + eor #$c3 + cmp #0 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + set_stat 0 + lda abs1 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt + php ;flags after load/store sequence + eor #$c3 + cmp zp1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + lda abs1+1 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+1 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + lda abs1+2 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+2 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + lda abs1+3 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+3 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + set_stat $ff + lda abs1 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt + php ;flags after load/store sequence + eor #$c3 + cmp zp1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + lda abs1+1 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+1 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + lda abs1+2 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+2 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + lda abs1+3 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+3 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + set_stat 0 + lda #$c3 + php + cmp abs1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + lda #$82 + php + cmp abs1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + lda #$41 + php + cmp abs1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + lda #0 + php + cmp abs1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + lda #$c3 + php + cmp abs1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + lda #$82 + php + cmp abs1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + lda #$41 + php + cmp abs1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + lda #0 + php + cmp abs1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + ldx #0 + lda zpt + eor #$c3 + cmp zp1 + trap_ne ;store to zp data + stx zpt ;clear + lda abst + eor #$c3 + cmp abs1 + trap_ne ;store to abs data + stx abst ;clear + lda zpt+1 + eor #$c3 + cmp zp1+1 + trap_ne ;store to zp data + stx zpt+1 ;clear + lda abst+1 + eor #$c3 + cmp abs1+1 + trap_ne ;store to abs data + stx abst+1 ;clear + lda zpt+2 + eor #$c3 + cmp zp1+2 + trap_ne ;store to zp data + stx zpt+2 ;clear + lda abst+2 + eor #$c3 + cmp abs1+2 + trap_ne ;store to abs data + stx abst+2 ;clear + lda zpt+3 + eor #$c3 + cmp zp1+3 + trap_ne ;store to zp data + stx zpt+3 ;clear + lda abst+3 + eor #$c3 + cmp abs1+3 + trap_ne ;store to abs data + stx abst+3 ;clear + next_test + +; testing bit test & compares BIT CPX CPY CMP all addressing modes +; BIT - zp / abs + set_a $ff,0 + bit zp1+3 ;00 - should set Z / clear NV + tst_a $ff,fz + set_a 1,0 + bit zp1+2 ;41 - should set V (M6) / clear NZ + tst_a 1,fv + set_a 1,0 + bit zp1+1 ;82 - should set N (M7) & Z / clear V + tst_a 1,fnz + set_a 1,0 + bit zp1 ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,fnv + + set_a $ff,$ff + bit zp1+3 ;00 - should set Z / clear NV + tst_a $ff,~fnv + set_a 1,$ff + bit zp1+2 ;41 - should set V (M6) / clear NZ + tst_a 1,~fnz + set_a 1,$ff + bit zp1+1 ;82 - should set N (M7) & Z / clear V + tst_a 1,~fv + set_a 1,$ff + bit zp1 ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,~fz + + set_a $ff,0 + bit abs1+3 ;00 - should set Z / clear NV + tst_a $ff,fz + set_a 1,0 + bit abs1+2 ;41 - should set V (M6) / clear NZ + tst_a 1,fv + set_a 1,0 + bit abs1+1 ;82 - should set N (M7) & Z / clear V + tst_a 1,fnz + set_a 1,0 + bit abs1 ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,fnv + + set_a $ff,$ff + bit abs1+3 ;00 - should set Z / clear NV + tst_a $ff,~fnv + set_a 1,$ff + bit abs1+2 ;41 - should set V (M6) / clear NZ + tst_a 1,~fnz + set_a 1,$ff + bit abs1+1 ;82 - should set N (M7) & Z / clear V + tst_a 1,~fv + set_a 1,$ff + bit abs1 ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,~fz + next_test + +; CPX - zp / abs / # + set_x $80,0 + cpx zp7f + tst_stat fc + dex + cpx zp7f + tst_stat fzc + dex + cpx zp7f + tst_x $7e,fn + set_x $80,$ff + cpx zp7f + tst_stat ~fnz + dex + cpx zp7f + tst_stat ~fn + dex + cpx zp7f + tst_x $7e,~fzc + + set_x $80,0 + cpx abs7f + tst_stat fc + dex + cpx abs7f + tst_stat fzc + dex + cpx abs7f + tst_x $7e,fn + set_x $80,$ff + cpx abs7f + tst_stat ~fnz + dex + cpx abs7f + tst_stat ~fn + dex + cpx abs7f + tst_x $7e,~fzc + + set_x $80,0 + cpx #$7f + tst_stat fc + dex + cpx #$7f + tst_stat fzc + dex + cpx #$7f + tst_x $7e,fn + set_x $80,$ff + cpx #$7f + tst_stat ~fnz + dex + cpx #$7f + tst_stat ~fn + dex + cpx #$7f + tst_x $7e,~fzc + next_test + +; CPY - zp / abs / # + set_y $80,0 + cpy zp7f + tst_stat fc + dey + cpy zp7f + tst_stat fzc + dey + cpy zp7f + tst_y $7e,fn + set_y $80,$ff + cpy zp7f + tst_stat ~fnz + dey + cpy zp7f + tst_stat ~fn + dey + cpy zp7f + tst_y $7e,~fzc + + set_y $80,0 + cpy abs7f + tst_stat fc + dey + cpy abs7f + tst_stat fzc + dey + cpy abs7f + tst_y $7e,fn + set_y $80,$ff + cpy abs7f + tst_stat ~fnz + dey + cpy abs7f + tst_stat ~fn + dey + cpy abs7f + tst_y $7e,~fzc + + set_y $80,0 + cpy #$7f + tst_stat fc + dey + cpy #$7f + tst_stat fzc + dey + cpy #$7f + tst_y $7e,fn + set_y $80,$ff + cpy #$7f + tst_stat ~fnz + dey + cpy #$7f + tst_stat ~fn + dey + cpy #$7f + tst_y $7e,~fzc + next_test + +; CMP - zp / abs / # + set_a $80,0 + cmp zp7f + tst_a $80,fc + set_a $7f,0 + cmp zp7f + tst_a $7f,fzc + set_a $7e,0 + cmp zp7f + tst_a $7e,fn + set_a $80,$ff + cmp zp7f + tst_a $80,~fnz + set_a $7f,$ff + cmp zp7f + tst_a $7f,~fn + set_a $7e,$ff + cmp zp7f + tst_a $7e,~fzc + + set_a $80,0 + cmp abs7f + tst_a $80,fc + set_a $7f,0 + cmp abs7f + tst_a $7f,fzc + set_a $7e,0 + cmp abs7f + tst_a $7e,fn + set_a $80,$ff + cmp abs7f + tst_a $80,~fnz + set_a $7f,$ff + cmp abs7f + tst_a $7f,~fn + set_a $7e,$ff + cmp abs7f + tst_a $7e,~fzc + + set_a $80,0 + cmp #$7f + tst_a $80,fc + set_a $7f,0 + cmp #$7f + tst_a $7f,fzc + set_a $7e,0 + cmp #$7f + tst_a $7e,fn + set_a $80,$ff + cmp #$7f + tst_a $80,~fnz + set_a $7f,$ff + cmp #$7f + tst_a $7f,~fn + set_a $7e,$ff + cmp #$7f + tst_a $7e,~fzc + + ldx #4 ;with indexing by X + set_a $80,0 + cmp zp1,x + tst_a $80,fc + set_a $7f,0 + cmp zp1,x + tst_a $7f,fzc + set_a $7e,0 + cmp zp1,x + tst_a $7e,fn + set_a $80,$ff + cmp zp1,x + tst_a $80,~fnz + set_a $7f,$ff + cmp zp1,x + tst_a $7f,~fn + set_a $7e,$ff + cmp zp1,x + tst_a $7e,~fzc + + set_a $80,0 + cmp abs1,x + tst_a $80,fc + set_a $7f,0 + cmp abs1,x + tst_a $7f,fzc + set_a $7e,0 + cmp abs1,x + tst_a $7e,fn + set_a $80,$ff + cmp abs1,x + tst_a $80,~fnz + set_a $7f,$ff + cmp abs1,x + tst_a $7f,~fn + set_a $7e,$ff + cmp abs1,x + tst_a $7e,~fzc + + ldy #4 ;with indexing by Y + ldx #8 ;with indexed indirect + set_a $80,0 + cmp abs1,y + tst_a $80,fc + set_a $7f,0 + cmp abs1,y + tst_a $7f,fzc + set_a $7e,0 + cmp abs1,y + tst_a $7e,fn + set_a $80,$ff + cmp abs1,y + tst_a $80,~fnz + set_a $7f,$ff + cmp abs1,y + tst_a $7f,~fn + set_a $7e,$ff + cmp abs1,y + tst_a $7e,~fzc + + set_a $80,0 + cmp (ind1,x) + tst_a $80,fc + set_a $7f,0 + cmp (ind1,x) + tst_a $7f,fzc + set_a $7e,0 + cmp (ind1,x) + tst_a $7e,fn + set_a $80,$ff + cmp (ind1,x) + tst_a $80,~fnz + set_a $7f,$ff + cmp (ind1,x) + tst_a $7f,~fn + set_a $7e,$ff + cmp (ind1,x) + tst_a $7e,~fzc + + set_a $80,0 + cmp (ind1),y + tst_a $80,fc + set_a $7f,0 + cmp (ind1),y + tst_a $7f,fzc + set_a $7e,0 + cmp (ind1),y + tst_a $7e,fn + set_a $80,$ff + cmp (ind1),y + tst_a $80,~fnz + set_a $7f,$ff + cmp (ind1),y + tst_a $7f,~fn + set_a $7e,$ff + cmp (ind1),y + tst_a $7e,~fzc + next_test + +; testing shifts - ASL LSR ROL ROR all addressing modes +; shifts - accumulator + ldx #3 +tasl + set_ax zp1,0 + asl a + tst_ax rASL,fASL,0 + dex + bpl tasl + ldx #3 +tasl1 + set_ax zp1,$ff + asl a + tst_ax rASL,fASL,$ff-fnzc + dex + bpl tasl1 + + ldx #3 +tlsr + set_ax zp1,0 + lsr a + tst_ax rLSR,fLSR,0 + dex + bpl tlsr + ldx #3 +tlsr1 + set_ax zp1,$ff + lsr a + tst_ax rLSR,fLSR,$ff-fnzc + dex + bpl tlsr1 + + ldx #3 +trol + set_ax zp1,0 + rol a + tst_ax rROL,fROL,0 + dex + bpl trol + ldx #3 +trol1 + set_ax zp1,$ff-fc + rol a + tst_ax rROL,fROL,$ff-fnzc + dex + bpl trol1 + + ldx #3 +trolc + set_ax zp1,fc + rol a + tst_ax rROLc,fROLc,0 + dex + bpl trolc + ldx #3 +trolc1 + set_ax zp1,$ff + rol a + tst_ax rROLc,fROLc,$ff-fnzc + dex + bpl trolc1 + + ldx #3 +tror + set_ax zp1,0 + ror a + tst_ax rROR,fROR,0 + dex + bpl tror + ldx #3 +tror1 + set_ax zp1,$ff-fc + ror a + tst_ax rROR,fROR,$ff-fnzc + dex + bpl tror1 + + ldx #3 +trorc + set_ax zp1,fc + ror a + tst_ax rRORc,fRORc,0 + dex + bpl trorc + ldx #3 +trorc1 + set_ax zp1,$ff + ror a + tst_ax rRORc,fRORc,$ff-fnzc + dex + bpl trorc1 + next_test + +; shifts - zeropage + ldx #3 +tasl2 + set_z zp1,0 + asl zpt + tst_z rASL,fASL,0 + dex + bpl tasl2 + ldx #3 +tasl3 + set_z zp1,$ff + asl zpt + tst_z rASL,fASL,$ff-fnzc + dex + bpl tasl3 + + ldx #3 +tlsr2 + set_z zp1,0 + lsr zpt + tst_z rLSR,fLSR,0 + dex + bpl tlsr2 + ldx #3 +tlsr3 + set_z zp1,$ff + lsr zpt + tst_z rLSR,fLSR,$ff-fnzc + dex + bpl tlsr3 + + ldx #3 +trol2 + set_z zp1,0 + rol zpt + tst_z rROL,fROL,0 + dex + bpl trol2 + ldx #3 +trol3 + set_z zp1,$ff-fc + rol zpt + tst_z rROL,fROL,$ff-fnzc + dex + bpl trol3 + + ldx #3 +trolc2 + set_z zp1,fc + rol zpt + tst_z rROLc,fROLc,0 + dex + bpl trolc2 + ldx #3 +trolc3 + set_z zp1,$ff + rol zpt + tst_z rROLc,fROLc,$ff-fnzc + dex + bpl trolc3 + + ldx #3 +tror2 + set_z zp1,0 + ror zpt + tst_z rROR,fROR,0 + dex + bpl tror2 + ldx #3 +tror3 + set_z zp1,$ff-fc + ror zpt + tst_z rROR,fROR,$ff-fnzc + dex + bpl tror3 + + ldx #3 +trorc2 + set_z zp1,fc + ror zpt + tst_z rRORc,fRORc,0 + dex + bpl trorc2 + ldx #3 +trorc3 + set_z zp1,$ff + ror zpt + tst_z rRORc,fRORc,$ff-fnzc + dex + bpl trorc3 + next_test + +; shifts - absolute + ldx #3 +tasl4 + set_abs zp1,0 + asl abst + tst_abs rASL,fASL,0 + dex + bpl tasl4 + ldx #3 +tasl5 + set_abs zp1,$ff + asl abst + tst_abs rASL,fASL,$ff-fnzc + dex + bpl tasl5 + + ldx #3 +tlsr4 + set_abs zp1,0 + lsr abst + tst_abs rLSR,fLSR,0 + dex + bpl tlsr4 + ldx #3 +tlsr5 + set_abs zp1,$ff + lsr abst + tst_abs rLSR,fLSR,$ff-fnzc + dex + bpl tlsr5 + + ldx #3 +trol4 + set_abs zp1,0 + rol abst + tst_abs rROL,fROL,0 + dex + bpl trol4 + ldx #3 +trol5 + set_abs zp1,$ff-fc + rol abst + tst_abs rROL,fROL,$ff-fnzc + dex + bpl trol5 + + ldx #3 +trolc4 + set_abs zp1,fc + rol abst + tst_abs rROLc,fROLc,0 + dex + bpl trolc4 + ldx #3 +trolc5 + set_abs zp1,$ff + rol abst + tst_abs rROLc,fROLc,$ff-fnzc + dex + bpl trolc5 + + ldx #3 +tror4 + set_abs zp1,0 + ror abst + tst_abs rROR,fROR,0 + dex + bpl tror4 + ldx #3 +tror5 + set_abs zp1,$ff-fc + ror abst + tst_abs rROR,fROR,$ff-fnzc + dex + bpl tror5 + + ldx #3 +trorc4 + set_abs zp1,fc + ror abst + tst_abs rRORc,fRORc,0 + dex + bpl trorc4 + ldx #3 +trorc5 + set_abs zp1,$ff + ror abst + tst_abs rRORc,fRORc,$ff-fnzc + dex + bpl trorc5 + next_test + +; shifts - zp indexed + ldx #3 +tasl6 + set_zx zp1,0 + asl zpt,x + tst_zx rASL,fASL,0 + dex + bpl tasl6 + ldx #3 +tasl7 + set_zx zp1,$ff + asl zpt,x + tst_zx rASL,fASL,$ff-fnzc + dex + bpl tasl7 + + ldx #3 +tlsr6 + set_zx zp1,0 + lsr zpt,x + tst_zx rLSR,fLSR,0 + dex + bpl tlsr6 + ldx #3 +tlsr7 + set_zx zp1,$ff + lsr zpt,x + tst_zx rLSR,fLSR,$ff-fnzc + dex + bpl tlsr7 + + ldx #3 +trol6 + set_zx zp1,0 + rol zpt,x + tst_zx rROL,fROL,0 + dex + bpl trol6 + ldx #3 +trol7 + set_zx zp1,$ff-fc + rol zpt,x + tst_zx rROL,fROL,$ff-fnzc + dex + bpl trol7 + + ldx #3 +trolc6 + set_zx zp1,fc + rol zpt,x + tst_zx rROLc,fROLc,0 + dex + bpl trolc6 + ldx #3 +trolc7 + set_zx zp1,$ff + rol zpt,x + tst_zx rROLc,fROLc,$ff-fnzc + dex + bpl trolc7 + + ldx #3 +tror6 + set_zx zp1,0 + ror zpt,x + tst_zx rROR,fROR,0 + dex + bpl tror6 + ldx #3 +tror7 + set_zx zp1,$ff-fc + ror zpt,x + tst_zx rROR,fROR,$ff-fnzc + dex + bpl tror7 + + ldx #3 +trorc6 + set_zx zp1,fc + ror zpt,x + tst_zx rRORc,fRORc,0 + dex + bpl trorc6 + ldx #3 +trorc7 + set_zx zp1,$ff + ror zpt,x + tst_zx rRORc,fRORc,$ff-fnzc + dex + bpl trorc7 + next_test + +; shifts - abs indexed + ldx #3 +tasl8 + set_absx zp1,0 + asl abst,x + tst_absx rASL,fASL,0 + dex + bpl tasl8 + ldx #3 +tasl9 + set_absx zp1,$ff + asl abst,x + tst_absx rASL,fASL,$ff-fnzc + dex + bpl tasl9 + + ldx #3 +tlsr8 + set_absx zp1,0 + lsr abst,x + tst_absx rLSR,fLSR,0 + dex + bpl tlsr8 + ldx #3 +tlsr9 + set_absx zp1,$ff + lsr abst,x + tst_absx rLSR,fLSR,$ff-fnzc + dex + bpl tlsr9 + + ldx #3 +trol8 + set_absx zp1,0 + rol abst,x + tst_absx rROL,fROL,0 + dex + bpl trol8 + ldx #3 +trol9 + set_absx zp1,$ff-fc + rol abst,x + tst_absx rROL,fROL,$ff-fnzc + dex + bpl trol9 + + ldx #3 +trolc8 + set_absx zp1,fc + rol abst,x + tst_absx rROLc,fROLc,0 + dex + bpl trolc8 + ldx #3 +trolc9 + set_absx zp1,$ff + rol abst,x + tst_absx rROLc,fROLc,$ff-fnzc + dex + bpl trolc9 + + ldx #3 +tror8 + set_absx zp1,0 + ror abst,x + tst_absx rROR,fROR,0 + dex + bpl tror8 + ldx #3 +tror9 + set_absx zp1,$ff-fc + ror abst,x + tst_absx rROR,fROR,$ff-fnzc + dex + bpl tror9 + + ldx #3 +trorc8 + set_absx zp1,fc + ror abst,x + tst_absx rRORc,fRORc,0 + dex + bpl trorc8 + ldx #3 +trorc9 + set_absx zp1,$ff + ror abst,x + tst_absx rRORc,fRORc,$ff-fnzc + dex + bpl trorc9 + next_test + +; testing memory increment/decrement - INC DEC all addressing modes +; zeropage + ldx #0 + lda #$7e + sta zpt +tinc + set_stat 0 + inc zpt + tst_z rINC,fINC,0 + inx + cpx #2 + bne tinc1 + lda #$fe + sta zpt +tinc1 cpx #5 + bne tinc + dex + inc zpt +tdec + set_stat 0 + dec zpt + tst_z rINC,fINC,0 + dex + bmi tdec1 + cpx #1 + bne tdec + lda #$81 + sta zpt + bne tdec +tdec1 + ldx #0 + lda #$7e + sta zpt +tinc10 + set_stat $ff + inc zpt + tst_z rINC,fINC,$ff-fnz + inx + cpx #2 + bne tinc11 + lda #$fe + sta zpt +tinc11 cpx #5 + bne tinc10 + dex + inc zpt +tdec10 + set_stat $ff + dec zpt + tst_z rINC,fINC,$ff-fnz + dex + bmi tdec11 + cpx #1 + bne tdec10 + lda #$81 + sta zpt + bne tdec10 +tdec11 + next_test + +; absolute memory + ldx #0 + lda #$7e + sta abst +tinc2 + set_stat 0 + inc abst + tst_abs rINC,fINC,0 + inx + cpx #2 + bne tinc3 + lda #$fe + sta abst +tinc3 cpx #5 + bne tinc2 + dex + inc abst +tdec2 + set_stat 0 + dec abst + tst_abs rINC,fINC,0 + dex + bmi tdec3 + cpx #1 + bne tdec2 + lda #$81 + sta abst + bne tdec2 +tdec3 + ldx #0 + lda #$7e + sta abst +tinc12 + set_stat $ff + inc abst + tst_abs rINC,fINC,$ff-fnz + inx + cpx #2 + bne tinc13 + lda #$fe + sta abst +tinc13 cpx #5 + bne tinc12 + dex + inc abst +tdec12 + set_stat $ff + dec abst + tst_abs rINC,fINC,$ff-fnz + dex + bmi tdec13 + cpx #1 + bne tdec12 + lda #$81 + sta abst + bne tdec12 +tdec13 + next_test + +; zeropage indexed + ldx #0 + lda #$7e +tinc4 sta zpt,x + set_stat 0 + inc zpt,x + tst_zx rINC,fINC,0 + lda zpt,x + inx + cpx #2 + bne tinc5 + lda #$fe +tinc5 cpx #5 + bne tinc4 + dex + lda #2 +tdec4 sta zpt,x + set_stat 0 + dec zpt,x + tst_zx rINC,fINC,0 + lda zpt,x + dex + bmi tdec5 + cpx #1 + bne tdec4 + lda #$81 + bne tdec4 +tdec5 + ldx #0 + lda #$7e +tinc14 sta zpt,x + set_stat $ff + inc zpt,x + tst_zx rINC,fINC,$ff-fnz + lda zpt,x + inx + cpx #2 + bne tinc15 + lda #$fe +tinc15 cpx #5 + bne tinc14 + dex + lda #2 +tdec14 sta zpt,x + set_stat $ff + dec zpt,x + tst_zx rINC,fINC,$ff-fnz + lda zpt,x + dex + bmi tdec15 + cpx #1 + bne tdec14 + lda #$81 + bne tdec14 +tdec15 + next_test + +; memory indexed + ldx #0 + lda #$7e +tinc6 sta abst,x + set_stat 0 + inc abst,x + tst_absx rINC,fINC,0 + lda abst,x + inx + cpx #2 + bne tinc7 + lda #$fe +tinc7 cpx #5 + bne tinc6 + dex + lda #2 +tdec6 sta abst,x + set_stat 0 + dec abst,x + tst_absx rINC,fINC,0 + lda abst,x + dex + bmi tdec7 + cpx #1 + bne tdec6 + lda #$81 + bne tdec6 +tdec7 + ldx #0 + lda #$7e +tinc16 sta abst,x + set_stat $ff + inc abst,x + tst_absx rINC,fINC,$ff-fnz + lda abst,x + inx + cpx #2 + bne tinc17 + lda #$fe +tinc17 cpx #5 + bne tinc16 + dex + lda #2 +tdec16 sta abst,x + set_stat $ff + dec abst,x + tst_absx rINC,fINC,$ff-fnz + lda abst,x + dex + bmi tdec17 + cpx #1 + bne tdec16 + lda #$81 + bne tdec16 +tdec17 + next_test + +; testing logical instructions - AND EOR ORA all addressing modes +; AND + ldx #3 ;immediate +tand lda zpAN,x + sta ex_andi+1 ;set AND # operand + set_ax absANa,0 + jsr ex_andi ;execute AND # in RAM + tst_ax absrlo,absflo,0 + dex + bpl tand + ldx #3 +tand1 lda zpAN,x + sta ex_andi+1 ;set AND # operand + set_ax absANa,$ff + jsr ex_andi ;execute AND # in RAM + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand1 + + ldx #3 ;zp +tand2 lda zpAN,x + sta zpt + set_ax absANa,0 + and zpt + tst_ax absrlo,absflo,0 + dex + bpl tand2 + ldx #3 +tand3 lda zpAN,x + sta zpt + set_ax absANa,$ff + and zpt + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand3 + + ldx #3 ;abs +tand4 lda zpAN,x + sta abst + set_ax absANa,0 + and abst + tst_ax absrlo,absflo,0 + dex + bpl tand4 + ldx #3 +tand5 lda zpAN,x + sta abst + set_ax absANa,$ff + and abst + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand6 + + ldx #3 ;zp,x +tand6 + set_ax absANa,0 + and zpAN,x + tst_ax absrlo,absflo,0 + dex + bpl tand6 + ldx #3 +tand7 + set_ax absANa,$ff + and zpAN,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand7 + + ldx #3 ;abs,x +tand8 + set_ax absANa,0 + and absAN,x + tst_ax absrlo,absflo,0 + dex + bpl tand8 + ldx #3 +tand9 + set_ax absANa,$ff + and absAN,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand9 + + ldy #3 ;abs,y +tand10 + set_ay absANa,0 + and absAN,y + tst_ay absrlo,absflo,0 + dey + bpl tand10 + ldy #3 +tand11 + set_ay absANa,$ff + and absAN,y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl tand11 + + ldx #6 ;(zp,x) + ldy #3 +tand12 + set_ay absANa,0 + and (indAN,x) + tst_ay absrlo,absflo,0 + dex + dex + dey + bpl tand12 + ldx #6 + ldy #3 +tand13 + set_ay absANa,$ff + and (indAN,x) + tst_ay absrlo,absflo,$ff-fnz + dex + dex + dey + bpl tand13 + + ldy #3 ;(zp),y +tand14 + set_ay absANa,0 + and (indAN),y + tst_ay absrlo,absflo,0 + dey + bpl tand14 + ldy #3 +tand15 + set_ay absANa,$ff + and (indAN),y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl tand15 + next_test + +; EOR + ldx #3 ;immediate - self modifying code +teor lda zpEO,x + sta ex_eori+1 ;set EOR # operand + set_ax absEOa,0 + jsr ex_eori ;execute EOR # in RAM + tst_ax absrlo,absflo,0 + dex + bpl teor + ldx #3 +teor1 lda zpEO,x + sta ex_eori+1 ;set EOR # operand + set_ax absEOa,$ff + jsr ex_eori ;execute EOR # in RAM + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor1 + + ldx #3 ;zp +teor2 lda zpEO,x + sta zpt + set_ax absEOa,0 + eor zpt + tst_ax absrlo,absflo,0 + dex + bpl teor2 + ldx #3 +teor3 lda zpEO,x + sta zpt + set_ax absEOa,$ff + eor zpt + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor3 + + ldx #3 ;abs +teor4 lda zpEO,x + sta abst + set_ax absEOa,0 + eor abst + tst_ax absrlo,absflo,0 + dex + bpl teor4 + ldx #3 +teor5 lda zpEO,x + sta abst + set_ax absEOa,$ff + eor abst + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor6 + + ldx #3 ;zp,x +teor6 + set_ax absEOa,0 + eor zpEO,x + tst_ax absrlo,absflo,0 + dex + bpl teor6 + ldx #3 +teor7 + set_ax absEOa,$ff + eor zpEO,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor7 + + ldx #3 ;abs,x +teor8 + set_ax absEOa,0 + eor absEO,x + tst_ax absrlo,absflo,0 + dex + bpl teor8 + ldx #3 +teor9 + set_ax absEOa,$ff + eor absEO,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor9 + + ldy #3 ;abs,y +teor10 + set_ay absEOa,0 + eor absEO,y + tst_ay absrlo,absflo,0 + dey + bpl teor10 + ldy #3 +teor11 + set_ay absEOa,$ff + eor absEO,y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl teor11 + + ldx #6 ;(zp,x) + ldy #3 +teor12 + set_ay absEOa,0 + eor (indEO,x) + tst_ay absrlo,absflo,0 + dex + dex + dey + bpl teor12 + ldx #6 + ldy #3 +teor13 + set_ay absEOa,$ff + eor (indEO,x) + tst_ay absrlo,absflo,$ff-fnz + dex + dex + dey + bpl teor13 + + ldy #3 ;(zp),y +teor14 + set_ay absEOa,0 + eor (indEO),y + tst_ay absrlo,absflo,0 + dey + bpl teor14 + ldy #3 +teor15 + set_ay absEOa,$ff + eor (indEO),y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl teor15 + next_test + +; OR + ldx #3 ;immediate - self modifying code +tora lda zpOR,x + sta ex_orai+1 ;set ORA # operand + set_ax absORa,0 + jsr ex_orai ;execute ORA # in RAM + tst_ax absrlo,absflo,0 + dex + bpl tora + ldx #3 +tora1 lda zpOR,x + sta ex_orai+1 ;set ORA # operand + set_ax absORa,$ff + jsr ex_orai ;execute ORA # in RAM + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora1 + + ldx #3 ;zp +tora2 lda zpOR,x + sta zpt + set_ax absORa,0 + ora zpt + tst_ax absrlo,absflo,0 + dex + bpl tora2 + ldx #3 +tora3 lda zpOR,x + sta zpt + set_ax absORa,$ff + ora zpt + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora3 + + ldx #3 ;abs +tora4 lda zpOR,x + sta abst + set_ax absORa,0 + ora abst + tst_ax absrlo,absflo,0 + dex + bpl tora4 + ldx #3 +tora5 lda zpOR,x + sta abst + set_ax absORa,$ff + ora abst + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora6 + + ldx #3 ;zp,x +tora6 + set_ax absORa,0 + ora zpOR,x + tst_ax absrlo,absflo,0 + dex + bpl tora6 + ldx #3 +tora7 + set_ax absORa,$ff + ora zpOR,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora7 + + ldx #3 ;abs,x +tora8 + set_ax absORa,0 + ora absOR,x + tst_ax absrlo,absflo,0 + dex + bpl tora8 + ldx #3 +tora9 + set_ax absORa,$ff + ora absOR,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora9 + + ldy #3 ;abs,y +tora10 + set_ay absORa,0 + ora absOR,y + tst_ay absrlo,absflo,0 + dey + bpl tora10 + ldy #3 +tora11 + set_ay absORa,$ff + ora absOR,y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl tora11 + + ldx #6 ;(zp,x) + ldy #3 +tora12 + set_ay absORa,0 + ora (indOR,x) + tst_ay absrlo,absflo,0 + dex + dex + dey + bpl tora12 + ldx #6 + ldy #3 +tora13 + set_ay absORa,$ff + ora (indOR,x) + tst_ay absrlo,absflo,$ff-fnz + dex + dex + dey + bpl tora13 + + ldy #3 ;(zp),y +tora14 + set_ay absORa,0 + ora (indOR),y + tst_ay absrlo,absflo,0 + dey + bpl tora14 + ldy #3 +tora15 + set_ay absORa,$ff + ora (indOR),y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl tora15 + if I_flag = 3 + cli + endif + next_test + +; full binary add/subtract test +; iterates through all combinations of operands and carry input +; uses increments/decrements to predict result & result flags + cld + ldx #ad2 ;for indexed test + ldy #$ff ;max range + lda #0 ;start with adding zeroes & no carry + sta adfc ;carry in - for diag + sta ad1 ;operand 1 - accumulator + sta ad2 ;operand 2 - memory or immediate + sta ada2 ;non zp + sta adrl ;expected result bits 0-7 + sta adrh ;expected result bit 8 (carry out) + lda #$ff ;complemented operand 2 for subtract + sta sb2 + sta sba2 ;non zp + lda #2 ;expected Z-flag + sta adrf +tadd clc ;test with carry clear + jsr chkadd + inc adfc ;now with carry + inc adrl ;result +1 + php ;save N & Z from low result + php + pla ;accu holds expected flags + and #$82 ;mask N & Z + plp + bne tadd1 + inc adrh ;result bit 8 - carry +tadd1 ora adrh ;merge C to expected flags + sta adrf ;save expected flags except overflow + sec ;test with carry set + jsr chkadd + dec adfc ;same for operand +1 but no carry + inc ad1 + bne tadd ;iterate op1 + lda #0 ;preset result to op2 when op1 = 0 + sta adrh + inc ada2 + inc ad2 + php ;save NZ as operand 2 becomes the new result + pla + and #$82 ;mask N00000Z0 + sta adrf ;no need to check carry as we are adding to 0 + dec sb2 ;complement subtract operand 2 + dec sba2 + lda ad2 + sta adrl + bne tadd ;iterate op2 + if disable_decimal < 1 + next_test + +; decimal add/subtract test +; *** WARNING - tests documented behavior only! *** +; only valid BCD operands are tested, N V Z flags are ignored +; iterates through all valid combinations of operands and carry input +; uses increments/decrements to predict result & carry flag + sed + ldx #ad2 ;for indexed test + ldy #$ff ;max range + lda #$99 ;start with adding 99 to 99 with carry + sta ad1 ;operand 1 - accumulator + sta ad2 ;operand 2 - memory or immediate + sta ada2 ;non zp + sta adrl ;expected result bits 0-7 + lda #1 ;set carry in & out + sta adfc ;carry in - for diag + sta adrh ;expected result bit 8 (carry out) + lda #0 ;complemented operand 2 for subtract + sta sb2 + sta sba2 ;non zp +tdad sec ;test with carry set + jsr chkdad + dec adfc ;now with carry clear + lda adrl ;decimal adjust result + bne tdad1 ;skip clear carry & preset result 99 (9A-1) + dec adrh + lda #$99 + sta adrl + bne tdad3 +tdad1 and #$f ;lower nibble mask + bne tdad2 ;no decimal adjust needed + dec adrl ;decimal adjust (?0-6) + dec adrl + dec adrl + dec adrl + dec adrl + dec adrl +tdad2 dec adrl ;result -1 +tdad3 clc ;test with carry clear + jsr chkdad + inc adfc ;same for operand -1 but with carry + lda ad1 ;decimal adjust operand 1 + beq tdad5 ;iterate operand 2 + and #$f ;lower nibble mask + bne tdad4 ;skip decimal adjust + dec ad1 ;decimal adjust (?0-6) + dec ad1 + dec ad1 + dec ad1 + dec ad1 + dec ad1 +tdad4 dec ad1 ;operand 1 -1 + jmp tdad ;iterate op1 + +tdad5 lda #$99 ;precharge op1 max + sta ad1 + lda ad2 ;decimal adjust operand 2 + beq tdad7 ;end of iteration + and #$f ;lower nibble mask + bne tdad6 ;skip decimal adjust + dec ad2 ;decimal adjust (?0-6) + dec ad2 + dec ad2 + dec ad2 + dec ad2 + dec ad2 + inc sb2 ;complemented decimal adjust for subtract (?9+6) + inc sb2 + inc sb2 + inc sb2 + inc sb2 + inc sb2 +tdad6 dec ad2 ;operand 2 -1 + inc sb2 ;complemented operand for subtract + lda sb2 + sta sba2 ;copy as non zp operand + lda ad2 + sta ada2 ;copy as non zp operand + sta adrl ;new result since op1+carry=00+carry +op2=op2 + inc adrh ;result carry + bne tdad ;iterate op2 +tdad7 + next_test + +; decimal/binary switch test +; tests CLD, SED, PLP, RTI to properly switch between decimal & binary opcode +; tables + clc + cld + php + lda #$55 + adc #$55 + cmp #$aa + trap_ne ;expected binary result after cld + clc + sed + php + lda #$55 + adc #$55 + cmp #$10 + trap_ne ;expected decimal result after sed + cld + plp + lda #$55 + adc #$55 + cmp #$10 + trap_ne ;expected decimal result after plp D=1 + plp + lda #$55 + adc #$55 + cmp #$aa + trap_ne ;expected binary result after plp D=0 + clc + lda #hi bin_rti_ret ;emulated interrupt for rti + pha + lda #lo bin_rti_ret + pha + php + sed + lda #hi dec_rti_ret ;emulated interrupt for rti + pha + lda #lo dec_rti_ret + pha + php + cld + rti +dec_rti_ret + lda #$55 + adc #$55 + cmp #$10 + trap_ne ;expected decimal result after rti D=1 + rti +bin_rti_ret + lda #$55 + adc #$55 + cmp #$aa + trap_ne ;expected binary result after rti D=0 + endif + + lda test_case + cmp #test_num + trap_ne ;previous test is out of sequence + lda #$f0 ;mark opcode testing complete + sta test_case + +; final RAM integrity test +; verifies that none of the previous tests has altered RAM outside of the +; designated write areas. + check_ram +; *** DEBUG INFO *** +; to debug checksum errors uncomment check_ram in the next_test macro to +; narrow down the responsible opcode. +; may give false errors when monitor, OS or other background activity is +; allowed during previous tests. + + +; S U C C E S S ************************************************ +; ------------- + success ;if you get here everything went well +; ------------- +; S U C C E S S ************************************************ + jmp start ;run again + + if disable_decimal < 1 +; core subroutine of the decimal add/subtract test +; *** WARNING - tests documented behavior only! *** +; only valid BCD operands are tested, N V Z flags are ignored +; iterates through all valid combinations of operands and carry input +; uses increments/decrements to predict result & carry flag +chkdad +; decimal ADC / SBC zp + php ;save carry for subtract + lda ad1 + adc ad2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sb2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad flags + plp +; decimal ADC / SBC abs + php ;save carry for subtract + lda ad1 + adc ada2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sba2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC # + php ;save carry for subtract + lda ad2 + sta ex_adci+1 ;set ADC # operand + lda ad1 + jsr ex_adci ;execute ADC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda sb2 + sta ex_sbci+1 ;set SBC # operand + lda ad1 + jsr ex_sbci ;execute SBC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC zp,x + php ;save carry for subtract + lda ad1 + adc 0,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sb2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC abs,x + php ;save carry for subtract + lda ad1 + adc ada2-ad2,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sba2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC abs,y + php ;save carry for subtract + lda ad1 + adc ada2-$ff,y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sba2-$ff,y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC (zp,x) + php ;save carry for subtract + lda ad1 + adc (lo adi2-ad2,x) ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc (lo sbi2-ad2,x) ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC (abs),y + php ;save carry for subtract + lda ad1 + adc (adiy2),y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc (sbiy2),y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + rts + endif + +; core subroutine of the full binary add/subtract test +; iterates through all combinations of operands and carry input +; uses increments/decrements to predict result & result flags +chkadd lda adrf ;add V-flag if overflow + and #$83 ;keep N-----ZC / clear V + pha + lda ad1 ;test sign unequal between operands + eor ad2 + bmi ckad1 ;no overflow possible - operands have different sign + lda ad1 ;test sign equal between operands and result + eor adrl + bpl ckad1 ;no overflow occured - operand and result have same sign + pla + ora #$40 ;set V + pha +ckad1 pla + sta adrf ;save expected flags +; binary ADC / SBC zp + php ;save carry for subtract + lda ad1 + adc ad2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sb2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC abs + php ;save carry for subtract + lda ad1 + adc ada2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sba2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC # + php ;save carry for subtract + lda ad2 + sta ex_adci+1 ;set ADC # operand + lda ad1 + jsr ex_adci ;execute ADC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda sb2 + sta ex_sbci+1 ;set SBC # operand + lda ad1 + jsr ex_sbci ;execute SBC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC zp,x + php ;save carry for subtract + lda ad1 + adc 0,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sb2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC abs,x + php ;save carry for subtract + lda ad1 + adc ada2-ad2,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sba2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC abs,y + php ;save carry for subtract + lda ad1 + adc ada2-$ff,y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sba2-$ff,y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC (zp,x) + php ;save carry for subtract + lda ad1 + adc (lo adi2-ad2,x) ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc (lo sbi2-ad2,x) ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC (abs),y + php ;save carry for subtract + lda ad1 + adc (adiy2),y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc (sbiy2),y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + rts + +; target for the jump absolute test + dey + dey +test_far + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_cs ;flags loaded? + trap_vs + trap_mi + trap_eq + cmp #'F' ;registers loaded? + trap_ne + cpx #'A' + trap_ne + cpy #('R'-3) + trap_ne + pha ;save a,x + txa + pha + tsx + cpx #$fd ;check SP + trap_ne + pla ;restore x + tax + set_stat $ff + pla ;restore a + inx ;return registers with modifications + eor #$aa ;N=1, V=1, Z=0, C=1 + jmp far_ret + +; target for the jump indirect test + align +ptr_tst_ind dw test_ind +ptr_ind_ret dw ind_ret + trap ;runover protection + dey + dey +test_ind + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_cs ;flags loaded? + trap_vs + trap_mi + trap_eq + cmp #'I' ;registers loaded? + trap_ne + cpx #'N' + trap_ne + cpy #('D'-3) + trap_ne + pha ;save a,x + txa + pha + tsx + cpx #$fd ;check SP + trap_ne + pla ;restore x + tax + set_stat $ff + pla ;restore a + inx ;return registers with modifications + eor #$aa ;N=1, V=1, Z=0, C=1 + jmp (ptr_ind_ret) + trap ;runover protection + jmp start ;catastrophic error - cannot continue + +; target for the jump subroutine test + dey + dey +test_jsr + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_cs ;flags loaded? + trap_vs + trap_mi + trap_eq + cmp #'J' ;registers loaded? + trap_ne + cpx #'S' + trap_ne + cpy #('R'-3) + trap_ne + pha ;save a,x + txa + pha + tsx ;sp -4? (return addr,a,x) + cpx #$fb + trap_ne + lda $1ff ;propper return on stack + cmp #hi(jsr_ret) + trap_ne + lda $1fe + cmp #lo(jsr_ret) + trap_ne + set_stat $ff + pla ;pull x,a + tax + pla + inx ;return registers with modifications + eor #$aa ;N=1, V=1, Z=0, C=1 + rts + trap ;runover protection + jmp start ;catastrophic error - cannot continue + +;trap in case of unexpected IRQ, NMI, BRK, RESET - BRK test target +nmi_trap + trap ;check stack for conditions at NMI + jmp start ;catastrophic error - cannot continue +res_trap + trap ;unexpected RESET + jmp start ;catastrophic error - cannot continue + + dey + dey +irq_trap ;BRK test or unextpected BRK or IRQ + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + ;next traps could be caused by unexpected BRK or IRQ + ;check stack for BREAK and originating location + ;possible jump/branch into weeds (uninitialized space) + cmp #$ff-'B' ;BRK pass 2 registers loaded? + beq break2 + cmp #'B' ;BRK pass 1 registers loaded? + trap_ne + cpx #'R' + trap_ne + cpy #'K'-3 + trap_ne + sta irq_a ;save registers during break test + stx irq_x + tsx ;test break on stack + lda $102,x + cmp_flag 0 ;break test should have B=1 & unused=1 on stack + trap_ne ; - no break flag on stack + pla + cmp_flag intdis ;should have added interrupt disable + trap_ne + tsx + cpx #$fc ;sp -3? (return addr, flags) + trap_ne + lda $1ff ;propper return on stack + cmp #hi(brk_ret0) + trap_ne + lda $1fe + cmp #lo(brk_ret0) + trap_ne + load_flag $ff + pha + ldx irq_x + inx ;return registers with modifications + lda irq_a + eor #$aa + plp ;N=1, V=1, Z=1, C=1 but original flags should be restored + rti + trap ;runover protection + jmp start ;catastrophic error - cannot continue + +break2 ;BRK pass 2 + cpx #$ff-'R' + trap_ne + cpy #$ff-'K'-3 + trap_ne + sta irq_a ;save registers during break test + stx irq_x + tsx ;test break on stack + lda $102,x + cmp_flag $ff ;break test should have B=1 + trap_ne ; - no break flag on stack + pla + ora #decmode ;ignore decmode cleared if 65c02 + cmp_flag $ff ;actual passed flags + trap_ne + tsx + cpx #$fc ;sp -3? (return addr, flags) + trap_ne + lda $1ff ;propper return on stack + cmp #hi(brk_ret1) + trap_ne + lda $1fe + cmp #lo(brk_ret1) + trap_ne + load_flag intdis + pha + ldx irq_x + inx ;return registers with modifications + lda irq_a + eor #$aa + plp ;N=0, V=0, Z=0, C=0 but original flags should be restored + rti + trap ;runover protection + jmp start ;catastrophic error - cannot continue + + if report = 1 + include "report.i65" + endif + +;copy of data to initialize BSS segment + if load_data_direct != 1 +zp_init +zp1_ db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +zp7f_ db $7f ;test pattern for compare +;logical zeropage operands +zpOR_ db 0,$1f,$71,$80 ;test pattern for OR +zpAN_ db $0f,$ff,$7f,$80 ;test pattern for AND +zpEO_ db $ff,$0f,$8f,$8f ;test pattern for EOR +;indirect addressing pointers +ind1_ dw abs1 ;indirect pointer to pattern in absolute memory + dw abs1+1 + dw abs1+2 + dw abs1+3 + dw abs7f +inw1_ dw abs1-$f8 ;indirect pointer for wrap-test pattern +indt_ dw abst ;indirect pointer to store area in absolute memory + dw abst+1 + dw abst+2 + dw abst+3 +inwt_ dw abst-$f8 ;indirect pointer for wrap-test store +indAN_ dw absAN ;indirect pointer to AND pattern in absolute memory + dw absAN+1 + dw absAN+2 + dw absAN+3 +indEO_ dw absEO ;indirect pointer to EOR pattern in absolute memory + dw absEO+1 + dw absEO+2 + dw absEO+3 +indOR_ dw absOR ;indirect pointer to OR pattern in absolute memory + dw absOR+1 + dw absOR+2 + dw absOR+3 +;add/subtract indirect pointers +adi2_ dw ada2 ;indirect pointer to operand 2 in absolute memory +sbi2_ dw sba2 ;indirect pointer to complemented operand 2 (SBC) +adiy2_ dw ada2-$ff ;with offset for indirect indexed +sbiy2_ dw sba2-$ff +zp_end + if (zp_end - zp_init) != (zp_bss_end - zp_bss) + ;force assembler error if size is different + ERROR ERROR ERROR ;mismatch between bss and zeropage data + endif +data_init +ex_and_ and #0 ;execute immediate opcodes + rts +ex_eor_ eor #0 ;execute immediate opcodes + rts +ex_ora_ ora #0 ;execute immediate opcodes + rts +ex_adc_ adc #0 ;execute immediate opcodes + rts +ex_sbc_ sbc #0 ;execute immediate opcodes + rts +abs1_ db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +abs7f_ db $7f ;test pattern for compare +;loads +fLDx_ db fn,fn,0,fz ;expected flags for load +;shifts +rASL_ ;expected result ASL & ROL -carry +rROL_ db $86,$04,$82,0 ; " +rROLc_ db $87,$05,$83,1 ;expected result ROL +carry +rLSR_ ;expected result LSR & ROR -carry +rROR_ db $61,$41,$20,0 ; " +rRORc_ db $e1,$c1,$a0,$80 ;expected result ROR +carry +fASL_ ;expected flags for shifts +fROL_ db fnc,fc,fn,fz ;no carry in +fROLc_ db fnc,fc,fn,0 ;carry in +fLSR_ +fROR_ db fc,0,fc,fz ;no carry in +fRORc_ db fnc,fn,fnc,fn ;carry in +;increments (decrements) +rINC_ db $7f,$80,$ff,0,1 ;expected result for INC/DEC +fINC_ db 0,fn,fn,fz,0 ;expected flags for INC/DEC +;logical memory operand +absOR_ db 0,$1f,$71,$80 ;test pattern for OR +absAN_ db $0f,$ff,$7f,$80 ;test pattern for AND +absEO_ db $ff,$0f,$8f,$8f ;test pattern for EOR +;logical accu operand +absORa_ db 0,$f1,$1f,0 ;test pattern for OR +absANa_ db $f0,$ff,$ff,$ff ;test pattern for AND +absEOa_ db $ff,$f0,$f0,$0f ;test pattern for EOR +;logical results +absrlo_ db 0,$ff,$7f,$80 +absflo_ db fz,fn,0,fn +data_end + if (data_end - data_init) != (data_bss_end - data_bss) + ;force assembler error if size is different + ERROR ERROR ERROR ;mismatch between bss and data + endif + +vec_init + dw nmi_trap + dw res_trap + dw irq_trap +vec_bss equ $fffa + endif ;end of RAM init data + + if (load_data_direct = 1) & (ROM_vectors = 1) + org $fffa ;vectors + dw nmi_trap + dw res_trap + dw irq_trap + endif + + end start diff --git a/6502_functional_test.bin.gz b/6502_functional_test.bin.gz new file mode 100644 index 0000000..a3af7a3 Binary files /dev/null and b/6502_functional_test.bin.gz differ diff --git a/cmd/test-cpu.go b/cmd/test-cpu.go new file mode 100644 index 0000000..f67483e --- /dev/null +++ b/cmd/test-cpu.go @@ -0,0 +1,47 @@ +package main + +import ( + "encoding/hex" + "flag" + "mos6502go/cpu" + "mos6502go/utils" +) + +func main() { + cpu.InitDisasm() + var s cpu.State + s.Init() + + bytes, err := utils.ReadMemoryFromFile("6502_functional_test.bin.gz") + if err != nil { + panic(err) + } + + for i := 0; i < len(bytes); i++ { + s.Memory[i] = bytes[i] + } + + showInstructions := flag.Bool("show-instructions", false, "Show instructions code while running") + breakAddressString := flag.String("break", "", "Break on address") + flag.Parse() + + var breakAddress *uint16 + if *breakAddressString != "" { + breakAddressValue, err := hex.DecodeString(*breakAddressString) + if err != nil { + panic(err) + } + + var foo uint16 + if len(breakAddressValue) == 1 { + foo = uint16(breakAddressValue[0]) + } else if len(breakAddressValue) == 2 { + foo = uint16(breakAddressValue[0])*uint16(0x100) + uint16(breakAddressValue[1]) + } else { + panic("Invalid break address") + } + breakAddress = &foo + } + + cpu.Run(&s, *showInstructions, breakAddress) +} diff --git a/cmd/test-vid.go b/cmd/test-vid.go new file mode 100644 index 0000000..6b35dfa --- /dev/null +++ b/cmd/test-vid.go @@ -0,0 +1,109 @@ +package main + +import ( + "image" + "log" + + "github.com/hajimehoshi/ebiten" + "github.com/hajimehoshi/ebiten/ebitenutil" +) + +const ( + screenSizeFactor = 1 // Factor by which the whole screen is resized + textVideoMemory = 0x400 // Base location of page 1 text video memory + flashFrames = 8 // Number of frames when FLASH mode is toggled +) + +var ( + ebitenImage *ebiten.Image + memory [0x100000]byte + flashCounter int + flashOn bool +) + +func drawTextScreen(screen *ebiten.Image) error { + flashCounter-- + if flashCounter < 0 { + flashCounter = flashFrames + flashOn = !flashOn + } + + if ebiten.IsRunningSlowly() { + return nil + } + + for y := 0; y < 24; y++ { + base := 128*(y%8) + 40*(y/8) + for x := 0; x < 40; x++ { + offset := textVideoMemory + base + x + value := memory[offset] + inverted := false + + if (value & 0xc0) == 0 { + inverted = true + } else if (value & 0x80) == 0 { + value = value & 0x3f + inverted = flashOn + } + + if !inverted { + value = value & 0x7f + } + + if value < 0x20 { + value += 0x40 + } + + op := &ebiten.DrawImageOptions{} + op.GeoM.Scale(screenSizeFactor, screenSizeFactor) + op.GeoM.Translate(screenSizeFactor*7*float64(x), screenSizeFactor*8*float64(y)) + + fontRow := value % 16 + fontCol := value / 16 + var fontX = (int)(15 + fontCol*12) + var fontY = (int)(32 + fontRow*11) + r := image.Rect(fontX, fontY, fontX+7, fontY+8) + op.SourceRect = &r + + if !inverted { + op.ColorM.Scale(-1, -1, -1, 1) + op.ColorM.Translate(1, 1, 1, 0) + } + + op.ColorM.Scale(0.20, 0.75, 0.20, 1) + + if err := screen.DrawImage(ebitenImage, op); err != nil { + return err + } + } + } + + return nil +} + +func update(screen *ebiten.Image) error { + return drawTextScreen(screen) +} + +func addTestTextScreenData() { + // Clear screen + for i := 0; i < 0x400; i++ { + memory[textVideoMemory+i] = 160 + } + + for i := 0; i < 255; i++ { + memory[textVideoMemory+i] = byte(i) + } +} + +func main() { + addTestTextScreenData() + + var err error + ebitenImage, _, err = ebitenutil.NewImageFromFile("./pr-latin1.png", ebiten.FilterNearest) + if err != nil { + log.Fatal(err) + } + + ebiten.Run(update, 280*screenSizeFactor, 192*screenSizeFactor, 2, "Apple //") +} diff --git a/cpu/cpu.go b/cpu/cpu.go new file mode 100644 index 0000000..96f5805 --- /dev/null +++ b/cpu/cpu.go @@ -0,0 +1,817 @@ +package cpu + +import ( + "fmt" + "os" +) + +const ( + CpuFlagC byte = 1 << iota + CpuFlagZ + CpuFlagI + CpuFlagD + CpuFlagB + CpuFlagR + CpuFlagV + CpuFlagN +) + +const runningTests = true + +type State struct { + Memory [0x10000]uint8 + A uint8 + X uint8 + Y uint8 + PC uint16 + SP uint8 + P uint8 +} + +func (s *State) Init() { + s.A = 0 + s.X = 0 + s.Y = 0 + s.P = CpuFlagR | CpuFlagB | CpuFlagZ + s.PC = 0x800 + s.SP = 0xff +} + +func (s *State) setC(value bool) { + if value { + s.P |= CpuFlagC + } else { + s.P &= ^CpuFlagC + } +} + +func (s *State) setV(value bool) { + if value { + s.P |= CpuFlagV + } else { + s.P &= ^CpuFlagV + } +} + +func (s *State) setN(value uint8) { + if (value & 0x80) != 0 { + s.P |= CpuFlagN + } else { + s.P &= ^CpuFlagN + } +} + +func (s *State) setZ(value uint8) { + if value == 0 { + s.P |= CpuFlagZ + } else { + s.P &= ^CpuFlagZ + } +} + +func (s *State) isC() bool { + return (s.P & CpuFlagC) != 0 +} + +func (s *State) isZ() bool { + return (s.P & CpuFlagZ) != 0 +} + +func (s *State) isD() bool { + return (s.P & CpuFlagD) != 0 +} + +func (s *State) isV() bool { + return (s.P & CpuFlagV) != 0 +} + +func (s *State) isN() bool { + return (s.P & CpuFlagN) != 0 +} + +func push8(s *State, value uint8) { + s.Memory[0x100+uint16(s.SP)] = value + s.SP -= 1 + s.SP &= 0xff +} + +func push16(s *State, value uint16) { + s.Memory[0x100+uint16(s.SP)] = uint8(value >> 8) + s.Memory[0xff+uint16(s.SP)] = uint8(value & 0xff) + s.SP -= 2 + s.SP &= 0xff +} + +func pop8(s *State) uint8 { + s.SP += 1 + s.SP &= 0xff + return s.Memory[0x100+uint16(s.SP)] +} + +func pop16(s *State) uint16 { + s.SP += 2 + s.SP &= 0xff + lsb := uint16(s.Memory[0xff+uint16(s.SP)]) + msb := uint16(s.Memory[0x100+uint16(s.SP)]) + return lsb + msb<<8 +} + +func readMemory(s *State, address uint16) uint8 { + return s.Memory[address] +} + +func writeMemory(s *State, address uint16, value uint8) { + s.Memory[address] = value + + if runningTests && address == 0x200 { + testNumber := s.Memory[0x200] + if testNumber == 0xf0 { + fmt.Println("Opcode testing completed") + } else { + fmt.Printf("Test %d OK\n", s.Memory[0x200]) + } + } +} + +func branch(s *State, cycles *int, instructionName string, doBranch bool) { + value := s.Memory[s.PC+1] + + var relativeAddress uint16 + if (value & 0x80) == 0 { + relativeAddress = s.PC + uint16(value) + 2 + } else { + relativeAddress = s.PC + uint16(value) + 2 - 0x100 + } + + *cycles += 2 + if doBranch { + if runningTests && s.PC == relativeAddress { + fmt.Printf("Trap at $%04x\n", relativeAddress) + os.Exit(0) + } + + samePage := (s.PC & 0xff00) != (relativeAddress & 0xff00) + if samePage { + *cycles += 1 + } else { + *cycles += 2 + } + s.PC = relativeAddress + } else { + s.PC += 2 + } +} + +func getAddressFromAddressMode(s *State, addressMode byte) (result uint16, pageBoundaryCrossed bool) { + switch addressMode { + case AmZeroPage: + result = uint16(s.Memory[s.PC+1]) + case AmZeroPageX: + result = (uint16(s.Memory[s.PC+1]) + uint16(s.X)) & 0xff + case AmZeroPageY: + result = (uint16(s.Memory[s.PC+1]) + uint16(s.Y)) & 0xff + case AmAbsolute: + result = uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8 + case AmAbsoluteX: + value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8 + pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.X)) & 0xff00) + result = value + uint16(s.X) + case AmAbsoluteY: + value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8 + pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.Y)) & 0xff00) + result = value + uint16(s.Y) + case AmIndirectX: + zeroPageAddress := (s.Memory[s.PC+1] + s.X) & 0xff + result = uint16(s.Memory[zeroPageAddress]) + uint16(s.Memory[zeroPageAddress+1])<<8 + case AmIndirectY: + address := s.Memory[s.PC+1] + lsb := uint16(s.Memory[address]) + msb := uint16(s.Memory[address+1]) + value := lsb + msb<<8 + pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.Y)) & 0xff00) + result = value + uint16(s.Y) + default: + panic(fmt.Sprintf("Unknown address mode %d in getAddressFromAddressMode()", addressMode)) + } + + return result, pageBoundaryCrossed +} + +func readMemoryWithAddressMode(s *State, addressMode byte) (result uint8, pageBoundaryCrossed bool) { + switch addressMode { + case AmImmediate: + result = s.Memory[s.PC+1] + s.PC += 2 + case AmZeroPage: + var address uint16 + address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode) + result = s.Memory[address] + s.PC += 2 + case AmZeroPageX: + var address uint16 + address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode) + result = s.Memory[address] + s.PC += 2 + case AmZeroPageY: + var address uint16 + address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode) + result = s.Memory[address] + s.PC += 2 + case AmAbsolute: + var address uint16 + address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode) + result = s.Memory[address] + s.PC += 3 + case AmAbsoluteX: + var address uint16 + address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode) + result = s.Memory[address] + s.PC += 3 + case AmAbsoluteY: + var address uint16 + address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode) + result = s.Memory[address] + s.PC += 3 + case AmIndirectX: + var address uint16 + address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode) + result = s.Memory[address] + s.PC += 2 + case AmIndirectY: + var address uint16 + address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode) + result = s.Memory[address] + s.PC += 2 + default: + result = 0 + s.PC++ + } + + return result, pageBoundaryCrossed +} + +// STA, STX and STY +func store(s *State, cycles *int, regValue uint8, addressMode byte) { + address, _ := getAddressFromAddressMode(s, addressMode) + writeMemory(s, address, regValue) + + switch addressMode { + case AmZeroPage: + s.PC += 2 + *cycles += 3 + case AmZeroPageX: + s.PC += 2 + *cycles += 4 + case AmZeroPageY: + s.PC += 2 + *cycles += 4 + case AmAbsolute: + s.PC += 3 + *cycles += 4 + case AmAbsoluteX: + s.PC += 3 + *cycles += 5 + case AmAbsoluteY: + s.PC += 3 + *cycles += 5 + case AmIndirect: + s.PC += 2 + *cycles += 6 + case AmIndirectX: + s.PC += 2 + *cycles += 6 + case AmIndirectY: + s.PC += 2 + *cycles += 6 + default: + panic(fmt.Sprintf("Unknown address mode %d in store()", addressMode)) + } +} + +// These instructions take the same amount of cycles +func advanceCyclesForAcculumatorOperation(cycles *int, addressMode byte, pageBoundaryCrossed bool) { + extraCycle := 0 + if pageBoundaryCrossed { + extraCycle = 1 + } + + switch addressMode { + case AmImmediate: + *cycles += 2 + case AmZeroPage: + *cycles += 3 + case AmZeroPageX: + *cycles += 4 + case AmZeroPageY: + *cycles += 4 + case AmAbsolute: + *cycles += 4 + case AmAbsoluteX: + *cycles += 4 + extraCycle + case AmAbsoluteY: + *cycles += 4 + extraCycle + case AmIndirectX: + *cycles += 6 + case AmIndirectY: + *cycles += 5 + extraCycle + default: + panic(fmt.Sprintf("Unknown address mode %d in advanceCyclesForAcculumatorOperation()", addressMode)) + } +} + +func load(s *State, cycles *int, addressMode byte) uint8 { + value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode) + s.setN(value) + s.setZ(value) + advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed) + return value +} + +func cmp(s *State, cycles *int, regValue uint8, addressMode byte) { + value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode) + var result uint16 + result = uint16(regValue) - uint16(value) + s.setC(result < 0x100) + s.setN(uint8(result)) + s.setZ(uint8(result & 0xff)) + advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed) +} + +func ora(s *State, cycles *int, addressMode byte) { + value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode) + s.A |= value + s.setN(s.A) + s.setZ(s.A) + advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed) +} + +func and(s *State, cycles *int, addressMode byte) { + value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode) + s.A &= value + s.setN(s.A) + s.setZ(s.A) + advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed) +} + +func eor(s *State, cycles *int, addressMode byte) { + value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode) + s.A ^= value + s.setN(s.A) + s.setZ(s.A) + advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed) +} + +func adc(s *State, cycles *int, addressMode byte) { + value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode) + + var temp uint16 + temp = uint16(s.A) + uint16(value) + + var carry uint8 + if s.isC() { + carry = 1 + } + + if carry > 0 { + temp++ + } + + // This is not valid in decimal mode + s.setZ(uint8(temp & 0xff)) + + if s.isD() { + if ((s.A & 0xf) + (value & 0xf) + carry) > 9 { + temp += 6 + } + + s.setN(uint8(temp)) + s.setV((((s.A ^ value) & 0x80) == 0) && (((s.A ^ uint8(temp)) & 0x80) != 0)) + + if temp > 0x99 { + temp += 96 + } + s.setC(temp > 0x99) + } else { + s.setN(uint8(temp)) + s.setV((((s.A ^ value) & 0x80) == 0) && (((s.A ^ uint8(temp)) & 0x80) != 0)) + s.setC(temp > 0xff) + } + + s.A = uint8(temp & 0xff) + + s.setN(s.A) + s.setZ(s.A) + advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed) +} + +func sbc(s *State, cycles *int, addressMode byte) { + value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode) + + var temp uint16 + temp = uint16(s.A) - uint16(value) + + var carry uint8 + if s.isC() { + carry = 0 + } else { + carry = 1 + } + + if carry > 0 { + temp-- + } + + s.setN(uint8(temp)) + + // This is not valid in decimal mode + s.setZ(uint8(temp & 0xff)) + + s.setV((((s.A ^ uint8(temp)) & 0x80) != 0) && (((s.A ^ value) & 0x80) != 0)) + + if s.isD() { + if ((int8(s.A) & 0xf) - int8(carry)) < (int8(value) & 0xf) { + temp -= 6 + } + + if temp > 0x99 { + temp -= 96 + } + } + + s.setC(temp < 0x100) + s.A = uint8(temp & 0xff) + advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed) +} + +func bit(s *State, address uint16) { + value := s.Memory[address] + s.setN(value) + s.setV((value & 0x40) != 0) + s.setZ(value & s.A) +} + +// Read the address/value for an ASL, LSR, ROR, ROL +func preProcessShift(s *State, cycles *int, addressMode byte) (address uint16, value uint8) { + if addressMode == AmAccumulator { + value = s.A + } else { + address, _ = getAddressFromAddressMode(s, addressMode) + value = s.Memory[address] + } + + if addressMode == AmAccumulator { + value = s.A + } else { + address, _ = getAddressFromAddressMode(s, addressMode) + value = s.Memory[address] + } + + return +} + +// Store the result of a ASL, LSR, ROR, ROL and advance PC and cycles +func postProcessShift(s *State, cycles *int, addressMode byte, address uint16, value uint8) { + switch addressMode { + case AmAccumulator: + s.A = value + s.PC += 1 + *cycles += 2 + case AmZeroPage: + writeMemory(s, address, value) + s.PC += 2 + *cycles += 5 + case AmZeroPageX: + writeMemory(s, address, value) + s.PC += 2 + *cycles += 6 + case AmAbsolute: + writeMemory(s, address, value) + s.PC += 3 + *cycles += 6 + case AmAbsoluteX: + writeMemory(s, address, value) + s.PC += 3 + *cycles += 7 + default: + panic(fmt.Sprintf("Unknown address mode %d in postProcessShift()", addressMode)) + } +} + +func postProcessIncDec(s *State, cycles *int, addressMode byte) { + switch addressMode { + case AmZeroPage: + s.PC += 2 + *cycles += 5 + case AmZeroPageX: + s.PC += 2 + *cycles += 6 + case AmAbsolute: + s.PC += 3 + *cycles += 6 + case AmAbsoluteX: + s.PC += 3 + *cycles += 7 + default: + panic(fmt.Sprintf("Unknown address mode %d in INC", addressMode)) + } +} + +func Run(s *State, showInstructions bool, breakAddress *uint16) { + cycles := 0 + + for { + if runningTests && (s.PC == 0x3869) { + fmt.Println("Functional tests passed") + return + } + + if showInstructions { + PrintInstruction(s) + } + + opcode := s.Memory[s.PC] + addressMode := OpCodes[opcode].AddressingMode.Mode + + if breakAddress != nil && s.PC == *breakAddress { + fmt.Printf("Break at $%04x\n", *breakAddress) + os.Exit(0) + } + + switch opcode { + + case 0x4c: // JMP $0000 + value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8 + if runningTests && s.PC == value { + fmt.Printf("Trap at $%04x\n", value) + os.Exit(0) + } + s.PC = value + cycles += 3 + case 0x6c: // JMP ($0000) + value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8 + s.PC = uint16(s.Memory[value]) + uint16(s.Memory[value+1])<<8 + cycles += 5 + + case 0x20: // JSR $0000 + value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8 + push16(s, s.PC+2) + s.PC = value + cycles += 6 + + case 0x60: // RTS + value := pop16(s) + s.PC = value + 1 + cycles += 6 + + case 0xa9, 0xa5, 0xb5, 0xad, 0xbd, 0xb9, 0xa1, 0xb1: // LDA + s.A = load(s, &cycles, addressMode) + case 0xa2, 0xa6, 0xb6, 0xae, 0xbe: // LDX + s.X = load(s, &cycles, addressMode) + case 0xa0, 0xa4, 0xb4, 0xac, 0xbc: // LDY + s.Y = load(s, &cycles, addressMode) + + case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91: //STA + store(s, &cycles, s.A, addressMode) + case 0x86, 0x96, 0x8e: // STX + store(s, &cycles, s.X, addressMode) + case 0x84, 0x94, 0x8c: //STY + store(s, &cycles, s.Y, addressMode) + + case 0xc9, 0xc5, 0xd5, 0xcd, 0xdd, 0xd9, 0xc1, 0xd1: // CMP + cmp(s, &cycles, s.A, addressMode) + case 0xe0, 0xe4, 0xeC: // CPX + cmp(s, &cycles, s.X, addressMode) + case 0xc0, 0xc4, 0xcc: // CPY + cmp(s, &cycles, s.Y, addressMode) + case 0x09, 0x05, 0x15, 0x0d, 0x1d, 0x19, 0x01, 0x11: // ORA + ora(s, &cycles, addressMode) + case 0x29, 0x25, 0x35, 0x2d, 0x3d, 0x39, 0x21, 0x31: // AND + and(s, &cycles, addressMode) + case 0x49, 0x45, 0x55, 0x4d, 0x5d, 0x59, 0x41, 0x51: // EOR + eor(s, &cycles, addressMode) + case 0x69, 0x65, 0x75, 0x6d, 0x7d, 0x79, 0x61, 0x71: // ADC + adc(s, &cycles, addressMode) + case 0xe9, 0xe5, 0xf5, 0xed, 0xfd, 0xf9, 0xe1, 0xf1: // SBC + sbc(s, &cycles, addressMode) + + // Register transfers + case 0xaa: // TAX + s.X = s.A + s.setN(s.X) + s.setZ(s.X) + s.PC++ + cycles += 2 + case 0xa8: // TAY + s.Y = s.A + s.setN(s.Y) + s.setZ(s.Y) + s.PC++ + cycles += 2 + case 0xba: // TSX + s.X = s.SP + s.setN(s.X) + s.setZ(s.X) + s.PC++ + cycles += 2 + case 0x8a: // TXA + s.A = s.X + s.setN(s.A) + s.setZ(s.A) + s.PC++ + cycles += 2 + case 0x9a: // TXS + s.SP = s.X + s.PC++ + cycles += 2 + case 0x98: // TYA + s.A = s.Y + s.setN(s.A) + s.setZ(s.A) + s.PC++ + cycles += 2 + + case 0xE8: + s.X = (s.X + 1) & 0xff + s.setN(s.X) + s.setZ(s.X) + s.PC++ + cycles += 2 + case 0xC8: + s.Y = (s.Y + 1) & 0xff + s.setN(s.Y) + s.setZ(s.Y) + s.PC++ + cycles += 2 + case 0xca: + s.X = (s.X - 1) & 0xff + s.setN(s.X) + s.setZ(s.X) + s.PC++ + cycles += 2 + case 0x88: + s.Y = (s.Y - 1) & 0xff + s.setN(s.Y) + s.setZ(s.Y) + s.PC++ + cycles += 2 + + // Branch instructions + case 0x10: + branch(s, &cycles, "BPL", !s.isN()) + case 0x30: + branch(s, &cycles, "BMI", s.isN()) + case 0x50: + branch(s, &cycles, "BVC", !s.isV()) + case 0x70: + branch(s, &cycles, "BVS", s.isV()) + case 0x90: + branch(s, &cycles, "BCC", !s.isC()) + case 0xb0: + branch(s, &cycles, "BCS", s.isC()) + case 0xd0: + branch(s, &cycles, "BNE", !s.isZ()) + case 0xf0: + branch(s, &cycles, "BEQ", s.isZ()) + + // Flag setting + case 0x18: + s.setC(false) + s.PC++ + cycles += 2 + case 0x38: + s.setC(true) + s.PC++ + cycles += 2 + case 0x58: + s.P &= ^CpuFlagI + s.PC++ + cycles += 2 + case 0x78: + s.P |= CpuFlagI + s.PC++ + cycles += 2 + case 0xb8: + s.P &= ^CpuFlagV + s.PC++ + cycles += 2 + case 0xd8: + s.P &= ^CpuFlagD + s.PC++ + cycles += 2 + case 0xf8: + s.P |= CpuFlagD + s.PC++ + cycles += 2 + + case 0x48: // PHA + push8(s, s.A) + s.PC++ + cycles += 3 + case 0x68: // PLA + s.A = pop8(s) + s.setN(s.A) + s.setZ(s.A) + s.PC++ + cycles += 4 + case 0x08: // PHP + // From http://visual6502.org/wiki/index.php?title=6502_BRK_and_B_bit#the_B_flag_and_the_various_mechanisms + // software instructions BRK & PHP will push the B flag as being 1. + push8(s, s.P|CpuFlagB) + s.PC++ + cycles += 3 + case 0x28: // PLP + // CpuFlagR is always supposed to be 1 + s.P = pop8(s) | CpuFlagR + s.PC++ + cycles += 4 + case 0xea: + s.PC++ + cycles += 2 + + case 0x00: // BRK + push16(s, s.PC+2) + s.P |= CpuFlagB + push8(s, s.P) + s.P |= CpuFlagI + s.PC = uint16(s.Memory[0xffff])<<8 + uint16(s.Memory[0xfffe]) + cycles += 7 + + case 0x40: // RTI + s.P = pop8(s) | CpuFlagR + value := pop16(s) + s.PC = value + cycles += 6 + + case 0x24: // BIT $00 + address := s.Memory[s.PC+1] + bit(s, uint16(address)) + s.PC += 2 + cycles += 3 + case 0x2C: // BIT $0000 + address := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8 + bit(s, address) + s.PC += 3 + cycles += 4 + + case 0x0a, 0x06, 0x16, 0x0e, 0x1e: // ASL + address, value := preProcessShift(s, &cycles, addressMode) + s.setC((value & 0x80) != 0) + value = (value << 1) & 0xff + s.setZ(value) + s.setN(value) + postProcessShift(s, &cycles, addressMode, address, value) + case 0x4a, 0x46, 0x56, 0x4e, 0x5e: // LSR + address, value := preProcessShift(s, &cycles, addressMode) + s.setC((value & 0x01) != 0) + value >>= 1 + s.setZ(value) + s.setN(value) + postProcessShift(s, &cycles, addressMode, address, value) + case 0x2a, 0x26, 0x36, 0x2e, 0x3e: // ROL + address, value := preProcessShift(s, &cycles, addressMode) + value16 := uint16(value) + value16 <<= 1 + if (s.P & CpuFlagC) != 0 { + value16 |= 0x01 + } + s.setC((value16 & 0x100) != 0) + value = uint8(value16 & 0xff) + s.setZ(value) + s.setN(value) + postProcessShift(s, &cycles, addressMode, address, value) + case 0x6a, 0x66, 0x76, 0x6e, 0x7e: // ROR + address, value := preProcessShift(s, &cycles, addressMode) + value16 := uint16(value) + if (s.P & CpuFlagC) != 0 { + value16 |= 0x100 + } + s.setC((value16 & 0x01) != 0) + value = uint8(value16 >> 1) + s.setZ(value) + s.setN(value) + postProcessShift(s, &cycles, addressMode, address, value) + + case 0xe6, 0xf6, 0xee, 0xfe: // INC + address, _ := getAddressFromAddressMode(s, addressMode) + value := s.Memory[address] + value = (value + 1) & 0xff + s.setZ(value) + s.setN(value) + writeMemory(s, address, value) + postProcessIncDec(s, &cycles, addressMode) + + case 0xc6, 0xd6, 0xce, 0xde: // DEC + address, _ := getAddressFromAddressMode(s, addressMode) + value := s.Memory[address] + value = (value - 1) & 0xff + s.setZ(value) + s.setN(value) + writeMemory(s, address, value) + postProcessIncDec(s, &cycles, addressMode) + + default: + fmt.Printf("Unknown opcode $%02x\n", opcode) + return + } + } +} diff --git a/cpu/disasm.go b/cpu/disasm.go new file mode 100644 index 0000000..624ed5c --- /dev/null +++ b/cpu/disasm.go @@ -0,0 +1,96 @@ +package cpu + +import ( + "fmt" + "strings" +) + +func printFlag(p byte, flag uint8, code string) { + if (p & flag) == 0 { + fmt.Print(code) + } else { + fmt.Printf("%s", strings.ToUpper(code)) + } +} + +func printInstruction(s *State, instruction string) { + fmt.Printf("%04x %-24s A=%02x X=%02x Y=%02x S=%02x P=%02x ", + s.PC, + instruction, + s.A, + s.X, + s.Y, + s.SP, + s.P, + ) + + printFlag(s.P, CpuFlagN, "n") + printFlag(s.P, CpuFlagV, "v") + fmt.Print("-") // CpuFlagR flag that's always 1 + printFlag(s.P, CpuFlagB, "b") + printFlag(s.P, CpuFlagD, "d") + printFlag(s.P, CpuFlagI, "i") + printFlag(s.P, CpuFlagZ, "z") + printFlag(s.P, CpuFlagC, "c") + + fmt.Println("") +} + +func PrintInstruction(s *State) { + opcodeValue := s.Memory[s.PC] + opcode := OpCodes[opcodeValue] + mnemonic := opcode.Mnemonic + size := opcode.AddressingMode.OperandSize + stringFormat := opcode.AddressingMode.StringFormat + + var value uint16 + if size == 0 { + printInstruction(s, fmt.Sprintf("%02x %s", opcodeValue, mnemonic)) + return + } + + var opcodes string + var suffix string + + if opcode.AddressingMode.Mode == AmRelative { + value = uint16(s.Memory[s.PC+1]) + var relativeAddress uint16 + if (value & 0x80) == 0 { + relativeAddress = s.PC + 2 + uint16(value) + } else { + relativeAddress = s.PC + 2 + uint16(value) - 0x100 + } + + suffix = fmt.Sprintf(stringFormat, relativeAddress) + opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value) + } else if size == 1 { + value = uint16(s.Memory[s.PC+1]) + suffix = fmt.Sprintf(stringFormat, value) + opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value) + } else if size == 2 { + lower := s.Memory[s.PC+1] + higher := s.Memory[s.PC+2] + value = uint16(lower) + uint16(higher)*0x100 + suffix = fmt.Sprintf(stringFormat, value) + opcodes = fmt.Sprintf("%02x %02x %02x ", opcodeValue, lower, higher) + } + + printInstruction(s, fmt.Sprintf("%s %s %s", opcodes, mnemonic, suffix)) +} + +func DumpMemory(s *State, offset uint16) { + var i uint16 + for i = 0; i < 0x100; i++ { + if (i & 0xf) == 8 { + fmt.Print(" ") + } + if (i & 0xf) == 0 { + if i > 0 { + fmt.Print("\n") + } + fmt.Printf("%04x ", offset+i) + } + fmt.Printf(" %02x", s.Memory[offset+i]) + } + fmt.Print("\n") +} diff --git a/cpu/opcodes.go b/cpu/opcodes.go new file mode 100644 index 0000000..a8ea575 --- /dev/null +++ b/cpu/opcodes.go @@ -0,0 +1,315 @@ +package cpu + +const ( + AmNone byte = 1 + iota + AmAccumulator // + AmImplied // + AmRelative // + AmExpansion // + AmImmediate // #$00 + AmZeroPage // $00 + AmZeroPageX // $00,X + AmZeroPageY // $00,Y + AmAbsolute // $0000 + AmAbsoluteX // $0000,X + AmAbsoluteY // $0000,Y + AmIndirect // ($0000) + AmIndirectX // ($00,X) + AmIndirectY // ($00),Y +) + +type AddressingMode struct { + Mode byte + OperandSize byte + StringFormat string +} + +type OpCode struct { + Mnemonic string + AddressingMode AddressingMode +} + +var AddressingModes map[byte]AddressingMode +var OpCodes [0x100]OpCode + +func InitAddressingModes() { + AddressingModes = make(map[byte]AddressingMode) + AddressingModes[AmAccumulator] = AddressingMode{Mode: AmAccumulator, OperandSize: 0, StringFormat: ""} + AddressingModes[AmImplied] = AddressingMode{Mode: AmImplied, OperandSize: 0, StringFormat: ""} + AddressingModes[AmRelative] = AddressingMode{Mode: AmRelative, OperandSize: 1, StringFormat: "$%04x"} + AddressingModes[AmExpansion] = AddressingMode{Mode: AmExpansion, OperandSize: 0, StringFormat: ""} + AddressingModes[AmImmediate] = AddressingMode{Mode: AmImmediate, OperandSize: 1, StringFormat: "#$%02x"} + AddressingModes[AmZeroPage] = AddressingMode{Mode: AmZeroPage, OperandSize: 1, StringFormat: "$%02x"} + AddressingModes[AmZeroPageX] = AddressingMode{Mode: AmZeroPageX, OperandSize: 1, StringFormat: "$%02x,X"} + AddressingModes[AmZeroPageY] = AddressingMode{Mode: AmZeroPageY, OperandSize: 1, StringFormat: "$%02x,Y"} + AddressingModes[AmAbsolute] = AddressingMode{Mode: AmAbsolute, OperandSize: 2, StringFormat: "$%04x"} + AddressingModes[AmAbsoluteX] = AddressingMode{Mode: AmAbsoluteX, OperandSize: 2, StringFormat: "$%04x,X"} + AddressingModes[AmAbsoluteY] = AddressingMode{Mode: AmAbsoluteY, OperandSize: 2, StringFormat: "$%04x,Y"} + AddressingModes[AmIndirect] = AddressingMode{Mode: AmIndirect, OperandSize: 2, StringFormat: "($%04x)"} + AddressingModes[AmIndirectX] = AddressingMode{Mode: AmIndirectX, OperandSize: 1, StringFormat: "($%02x,X)"} + AddressingModes[AmIndirectY] = AddressingMode{Mode: AmIndirectY, OperandSize: 1, StringFormat: "($%02x),Y"} +} + +func InitOpCodes() { + OpCodes[0x00] = OpCode{Mnemonic: "BRK", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x01] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmIndirectX]} + OpCodes[0x02] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x03] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x04] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x05] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x06] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x07] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x08] = OpCode{Mnemonic: "PHP", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x09] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0x0A] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmAccumulator]} + OpCodes[0x0B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x0C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x0D] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x0E] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x0F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x10] = OpCode{Mnemonic: "BPL", AddressingMode: AddressingModes[AmRelative]} + OpCodes[0x11] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmIndirectY]} + OpCodes[0x12] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x13] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x14] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x15] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x16] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x17] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x18] = OpCode{Mnemonic: "CLC", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x19] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmAbsoluteY]} + OpCodes[0x1A] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x1B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x1C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x1D] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0x1E] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0x1F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x20] = OpCode{Mnemonic: "JSR", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x21] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmIndirectX]} + OpCodes[0x22] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x23] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x24] = OpCode{Mnemonic: "BIT", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x25] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x26] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x27] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x28] = OpCode{Mnemonic: "PLP", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x29] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0x2A] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmAccumulator]} + OpCodes[0x2B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x2C] = OpCode{Mnemonic: "BIT", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x2D] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x2E] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x2F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x30] = OpCode{Mnemonic: "BMI", AddressingMode: AddressingModes[AmRelative]} + OpCodes[0x31] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmIndirectY]} + OpCodes[0x32] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x33] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x34] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x35] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x36] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x37] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x38] = OpCode{Mnemonic: "SEC", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x39] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmAbsoluteY]} + OpCodes[0x3A] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x3B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x3C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x3D] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0x3E] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0x3F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x40] = OpCode{Mnemonic: "RTI", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x41] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmIndirectX]} + OpCodes[0x42] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x43] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x44] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x45] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x46] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x47] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x48] = OpCode{Mnemonic: "PHA", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x49] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0x4A] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmAccumulator]} + OpCodes[0x4B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x4C] = OpCode{Mnemonic: "JMP", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x4D] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x4E] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x4F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x50] = OpCode{Mnemonic: "BVC", AddressingMode: AddressingModes[AmRelative]} + OpCodes[0x51] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmIndirectY]} + OpCodes[0x52] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x53] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x54] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x55] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x56] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x57] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x58] = OpCode{Mnemonic: "CLI", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x59] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmAbsoluteY]} + OpCodes[0x5A] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x5B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x5C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x5D] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0x5E] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0x5F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x60] = OpCode{Mnemonic: "RTS", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x61] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmIndirectX]} + OpCodes[0x62] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x63] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x64] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x65] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x66] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x67] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x68] = OpCode{Mnemonic: "PLA", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x69] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0x6A] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmAccumulator]} + OpCodes[0x6B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x6C] = OpCode{Mnemonic: "JMP", AddressingMode: AddressingModes[AmIndirect]} + OpCodes[0x6D] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x6E] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x6F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x70] = OpCode{Mnemonic: "BVS", AddressingMode: AddressingModes[AmRelative]} + OpCodes[0x71] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmIndirectY]} + OpCodes[0x72] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x73] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x74] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x75] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x76] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x77] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x78] = OpCode{Mnemonic: "SEI", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x79] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmAbsoluteY]} + OpCodes[0x7A] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x7B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x7C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x7D] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0x7E] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0x7F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x80] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x81] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmIndirectX]} + OpCodes[0x82] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x83] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x84] = OpCode{Mnemonic: "STY", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x85] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x86] = OpCode{Mnemonic: "STX", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0x87] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x88] = OpCode{Mnemonic: "DEY", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x89] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x8A] = OpCode{Mnemonic: "TXA", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x8B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x8C] = OpCode{Mnemonic: "STY", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x8D] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x8E] = OpCode{Mnemonic: "STX", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0x8F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x90] = OpCode{Mnemonic: "BCC", AddressingMode: AddressingModes[AmRelative]} + OpCodes[0x91] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmIndirectY]} + OpCodes[0x92] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x93] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x94] = OpCode{Mnemonic: "STY", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x95] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0x96] = OpCode{Mnemonic: "STX", AddressingMode: AddressingModes[AmZeroPageY]} + OpCodes[0x97] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x98] = OpCode{Mnemonic: "TYA", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x99] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmAbsoluteY]} + OpCodes[0x9A] = OpCode{Mnemonic: "TXS", AddressingMode: AddressingModes[AmNone]} + OpCodes[0x9B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x9C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x9D] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0x9E] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0x9F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xA0] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0xA1] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmIndirectX]} + OpCodes[0xA2] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0xA3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xA4] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0xA5] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0xA6] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0xA7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xA8] = OpCode{Mnemonic: "TAY", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xA9] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0xAA] = OpCode{Mnemonic: "TAX", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xAB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xAC] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0xAD] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0xAE] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0xAF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xB0] = OpCode{Mnemonic: "BCS", AddressingMode: AddressingModes[AmRelative]} + OpCodes[0xB1] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmIndirectY]} + OpCodes[0xB2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xB3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xB4] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0xB5] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0xB6] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmZeroPageY]} + OpCodes[0xB7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xB8] = OpCode{Mnemonic: "CLV", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xB9] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmAbsoluteY]} + OpCodes[0xBA] = OpCode{Mnemonic: "TSX", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xBB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xBC] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0xBD] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0xBE] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmAbsoluteY]} + OpCodes[0xBF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xC0] = OpCode{Mnemonic: "CPY", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0xC1] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmIndirectX]} + OpCodes[0xC2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xC3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xC4] = OpCode{Mnemonic: "CPY", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0xC5] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0xC6] = OpCode{Mnemonic: "DEC", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0xC7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xC8] = OpCode{Mnemonic: "INY", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xC9] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0xCA] = OpCode{Mnemonic: "DEX", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xCB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xCC] = OpCode{Mnemonic: "CPY", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0xCD] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0xCE] = OpCode{Mnemonic: "DEC", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0xCF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xD0] = OpCode{Mnemonic: "BNE", AddressingMode: AddressingModes[AmRelative]} + OpCodes[0xD1] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmIndirectY]} + OpCodes[0xD2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xD3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xD4] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xD5] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0xD6] = OpCode{Mnemonic: "DEC", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0xD7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xD8] = OpCode{Mnemonic: "CLD", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xD9] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmAbsoluteY]} + OpCodes[0xDA] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xDB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xDC] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xDD] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0xDE] = OpCode{Mnemonic: "DEC", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0xDF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xE0] = OpCode{Mnemonic: "CPX", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0xE1] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmIndirectX]} + OpCodes[0xE2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xE3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xE4] = OpCode{Mnemonic: "CPX", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0xE5] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0xE6] = OpCode{Mnemonic: "INC", AddressingMode: AddressingModes[AmZeroPage]} + OpCodes[0xE7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xE8] = OpCode{Mnemonic: "INX", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xE9] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmImmediate]} + OpCodes[0xEA] = OpCode{Mnemonic: "NOP", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xEB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xEC] = OpCode{Mnemonic: "CPX", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0xED] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0xEE] = OpCode{Mnemonic: "INC", AddressingMode: AddressingModes[AmAbsolute]} + OpCodes[0xEF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xF0] = OpCode{Mnemonic: "BEQ", AddressingMode: AddressingModes[AmRelative]} + OpCodes[0xF1] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmIndirectY]} + OpCodes[0xF2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xF3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xF4] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xF5] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0xF6] = OpCode{Mnemonic: "INC", AddressingMode: AddressingModes[AmZeroPageX]} + OpCodes[0xF7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xF8] = OpCode{Mnemonic: "SED", AddressingMode: AddressingModes[AmNone]} + OpCodes[0xF9] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmAbsoluteY]} + OpCodes[0xFA] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xFB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xFC] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} + OpCodes[0xFD] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0xFE] = OpCode{Mnemonic: "INC", AddressingMode: AddressingModes[AmAbsoluteX]} + OpCodes[0xFF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]} +} + +func InitDisasm() { + InitAddressingModes() + InitOpCodes() +} diff --git a/cpu_test.go b/cpu_test.go new file mode 100644 index 0000000..30b7709 --- /dev/null +++ b/cpu_test.go @@ -0,0 +1,25 @@ +package test_cpu + +import ( + "mos6502go/cpu" + "mos6502go/utils" + "testing" +) + +func TestFunctionalTests(*testing.T) { + cpu.InitDisasm() + + var s cpu.State + s.Init() + + bytes, err := utils.ReadMemoryFromFile("6502_functional_test.bin.gz") + if err != nil { + panic(err) + } + + for i := 0; i < len(bytes); i++ { + s.Memory[i] = bytes[i] + } + + cpu.Run(&s, false, nil) +} diff --git a/pr-latin1.png b/pr-latin1.png new file mode 100644 index 0000000..93c16f6 Binary files /dev/null and b/pr-latin1.png differ diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..abe3ea3 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,24 @@ +package utils + +import ( + "compress/gzip" + "io/ioutil" + "os" +) + +func ReadMemoryFromFile(filename string) (data []byte, err error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + + reader, err := gzip.NewReader(f) + if err != nil { + return nil, err + } + defer reader.Close() + + data, err = ioutil.ReadAll(reader) + + return +}