From 1f4390a42bc6d1b02dc569fe02547549c773a1ed Mon Sep 17 00:00:00 2001 From: Kilian Kurt Hofmann Date: Wed, 26 Aug 2026 23:03:15 +0200 Subject: [PATCH] Initial Commit --- .clang-format | 6 + .gitignore | 3 + .vscode/launch.json | 11 + 6502_65C02_functional_tests/.gitignore | 4 + .../6502_decimal_test.a65 | 355 + .../6502_functional_test.a65 | 6109 +++++++++++++++++ .../6502_interrupt_test.a65 | 1026 +++ .../65C02_extended_opcodes_test.a65c | 2882 ++++++++ 6502_65C02_functional_tests/as65_142.zip | Bin 0 -> 110316 bytes 6502_65C02_functional_tests/license.txt | 674 ++ 6502_65C02_functional_tests/readme.txt | 29 + 6502_65C02_functional_tests/report.i65 | 224 + CMakelists.txt | 26 + inc/cpu.h | 34 + inc/io.h | 18 + inc/memoryMap.h | 16 + inc/opcodes.h | 173 + inc/ram.h | 14 + inc/rom.h | 15 + src/cpu.c | 1673 +++++ src/io.c | 14 + src/main.c | 83 + src/memoryMap.c | 44 + src/ram.c | 12 + src/rom.c | 17 + 25 files changed, 13462 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 6502_65C02_functional_tests/.gitignore create mode 100644 6502_65C02_functional_tests/6502_decimal_test.a65 create mode 100644 6502_65C02_functional_tests/6502_functional_test.a65 create mode 100644 6502_65C02_functional_tests/6502_interrupt_test.a65 create mode 100644 6502_65C02_functional_tests/65C02_extended_opcodes_test.a65c create mode 100644 6502_65C02_functional_tests/as65_142.zip create mode 100644 6502_65C02_functional_tests/license.txt create mode 100644 6502_65C02_functional_tests/readme.txt create mode 100644 6502_65C02_functional_tests/report.i65 create mode 100644 CMakelists.txt create mode 100644 inc/cpu.h create mode 100644 inc/io.h create mode 100644 inc/memoryMap.h create mode 100644 inc/opcodes.h create mode 100644 inc/ram.h create mode 100644 inc/rom.h create mode 100644 src/cpu.c create mode 100644 src/io.c create mode 100644 src/main.c create mode 100644 src/memoryMap.c create mode 100644 src/ram.c create mode 100644 src/rom.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..55f3d06 --- /dev/null +++ b/.clang-format @@ -0,0 +1,6 @@ +--- +BasedOnStyle: LLVM +ColumnLimit: 130 +IndentCaseLabels: true +AllowShortIfStatementsOnASingleLine: Always +AllowShortLoopsOnASingleLine: true \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..654b23c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store + +build \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..678b632 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "configurations": [ + { + "name": "(lldb) Anfügen", + "type": "cppdbg", + "request": "attach", + "program": "${workspaceFolder}/build/6502PC", + "MIMode": "lldb" + } + ] +} \ No newline at end of file diff --git a/6502_65C02_functional_tests/.gitignore b/6502_65C02_functional_tests/.gitignore new file mode 100644 index 0000000..9e8ac6d --- /dev/null +++ b/6502_65C02_functional_tests/.gitignore @@ -0,0 +1,4 @@ +as65_142 + +*.bin +*.lst \ No newline at end of file diff --git a/6502_65C02_functional_tests/6502_decimal_test.a65 b/6502_65C02_functional_tests/6502_decimal_test.a65 new file mode 100644 index 0000000..7a76ad1 --- /dev/null +++ b/6502_65C02_functional_tests/6502_decimal_test.a65 @@ -0,0 +1,355 @@ +; Verify decimal mode behavior +; Written by Bruce Clark. This code is public domain. +; see http://www.6502.org/tutorials/decimal_mode.html +; +; Returns: +; ERROR = 0 if the test passed +; ERROR = 1 if the test failed +; modify the code at the DONE label for desired program end +; +; This routine requires 17 bytes of RAM -- 1 byte each for: +; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF +; and 2 bytes for N2H +; +; Variables: +; N1 and N2 are the two numbers to be added or subtracted +; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 +; DA and DNVZC are the actual accumulator and flag results in decimal mode +; HA and HNVZC are the accumulator and flag results when N1 and N2 are +; added or subtracted using binary arithmetic +; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and +; flag results, calculated using binary arithmetic +; +; This program takes approximately 1 minute at 1 MHz (a few seconds more on +; a 65C02 than a 6502 or 65816) +; + +; Configuration: +cputype = 0 ; 0 = 6502, 1 = 65C02, 2 = 65C816 +vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only +chk_a = 1 ; check accumulator +chk_n = 0 ; check sign (negative) flag +chk_v = 0 ; check overflow flag +chk_z = 0 ; check zero flag +chk_c = 1 ; check carry flag + +end_of_test macro + db $db ;execute 65C02 stop instruction + endm + + bss + org 0 +; operands - register Y = carry in +N1 ds 1 +N2 ds 1 +; binary result +HA ds 1 +HNVZC ds 1 + ;04 +; decimal result +DA ds 1 +DNVZC ds 1 +; predicted results +AR ds 1 +NF ds 1 + ;08 +VF ds 1 +ZF ds 1 +CF ds 1 +ERROR ds 1 + ;0C +; workspace +N1L ds 1 +N1H ds 1 +N2L ds 1 +N2H ds 2 + + code + org $200 +TEST ldy #1 ; initialize Y (used to loop through carry flag values) + sty ERROR ; store 1 in ERROR until the test passes + lda #0 ; initialize N1 and N2 + sta N1 + sta N2 +LOOP1 lda N2 ; N2L = N2 & $0F + and #$0F ; [1] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT2 + endif + sta N2L + lda N2 ; N2H = N2 & $F0 + and #$F0 ; [2] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT2 + endif + sta N2H + ora #$0F ; N2H+1 = (N2 & $F0) + $0F + sta N2H+1 +LOOP2 lda N1 ; N1L = N1 & $0F + and #$0F ; [3] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT1 + endif + sta N1L + lda N1 ; N1H = N1 & $F0 + and #$F0 ; [4] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT1 + endif + sta N1H + jsr ADD + jsr A6502 + jsr COMPARE + bne DONE + jsr SUB + jsr S6502 + jsr COMPARE + bne DONE +NEXT1 inc N1 ; [5] see text + bne LOOP2 ; loop through all 256 values of N1 +NEXT2 inc N2 ; [6] see text + bne LOOP1 ; loop through all 256 values of N2 + dey + bpl LOOP1 ; loop through both values of the carry flag + lda #0 ; test passed, so store 0 in ERROR + sta ERROR +DONE + end_of_test + +; Calculate the actual decimal mode accumulator and flags, the accumulator +; and flag results when N1 is added to N2 using binary arithmetic, the +; predicted accumulator result, the predicted carry flag, and the predicted +; V flag +; +ADD sed ; decimal mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + adc N2 + sta DA ; actual accumulator result in decimal mode + php + pla + sta DNVZC ; actual flags result in decimal mode + cld ; binary mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + adc N2 + sta HA ; accumulator result of N1+N2 using binary arithmetic + + php + pla + sta HNVZC ; flags result of N1+N2 using binary arithmetic + cpy #1 + lda N1L + adc N2L + cmp #$0A + ldx #0 + bcc A1 + inx + adc #5 ; add 6 (carry is set) + and #$0F + sec +A1 ora N1H +; +; if N1L + N2L < $0A, then add N2 & $F0 +; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) +; + adc N2H,x + php + bcs A2 + cmp #$A0 + bcc A3 +A2 adc #$5F ; add $60 (carry is set) + sec +A3 sta AR ; predicted accumulator result + php + pla + sta CF ; predicted carry result + pla +; +; note that all 8 bits of the P register are stored in VF +; + sta VF ; predicted V flags + rts + +; Calculate the actual decimal mode accumulator and flags, and the +; accumulator and flag results when N2 is subtracted from N1 using binary +; arithmetic +; +SUB sed ; decimal mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + sbc N2 + sta DA ; actual accumulator result in decimal mode + php + pla + sta DNVZC ; actual flags result in decimal mode + cld ; binary mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + sbc N2 + sta HA ; accumulator result of N1-N2 using binary arithmetic + + php + pla + sta HNVZC ; flags result of N1-N2 using binary arithmetic + rts + + if cputype != 1 +; Calculate the predicted SBC accumulator result for the 6502 and 65816 +; +SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S11 + inx + sbc #5 ; subtract 6 (carry is clear) + and #$0F + clc +S11 ora N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + sbc N2H,x + bcs S12 + sbc #$5F ; subtract $60 (carry is clear) +S12 sta AR + rts + endif + + if cputype = 1 +; Calculate the predicted SBC accumulator result for the 6502 and 65C02 +; +SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S21 + inx + and #$0F + clc +S21 ora N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + sbc N2H,x + bcs S22 + sbc #$5F ; subtract $60 (carry is clear) +S22 cpx #0 + beq S23 + sbc #6 +S23 sta AR ; predicted accumulator result + rts + endif + +; Compare accumulator actual results to predicted results +; +; Return: +; Z flag = 1 (BEQ branch) if same +; Z flag = 0 (BNE branch) if different +; +COMPARE + if chk_a = 1 + lda DA + cmp AR + bne C1 + endif + if chk_n = 1 + lda DNVZC ; [7] see text + eor NF + and #$80 ; mask off N flag + bne C1 + endif + if chk_v = 1 + lda DNVZC ; [8] see text + eor VF + and #$40 ; mask off V flag + bne C1 ; [9] see text + endif + if chk_z = 1 + lda DNVZC + eor ZF ; mask off Z flag + and #2 + bne C1 ; [10] see text + endif + if chk_c = 1 + lda DNVZC + eor CF + and #1 ; mask off C flag + endif +C1 rts + +; These routines store the predicted values for ADC and SBC for the 6502, +; 65C02, and 65816 in AR, CF, NF, VF, and ZF + + if cputype = 0 + +A6502 lda VF ; 6502 +; +; since all 8 bits of the P register were stored in VF, bit 7 of VF contains +; the N flag for NF +; + sta NF + lda HNVZC + sta ZF + rts + +S6502 jsr SUB1 + lda HNVZC + sta NF + sta VF + sta ZF + sta CF + rts + + endif + if cputype = 1 + +A6502 lda AR ; 65C02 + php + pla + sta NF + sta ZF + rts + +S6502 jsr SUB2 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + if cputype = 2 + +A6502 lda AR ; 65C816 + php + pla + sta NF + sta ZF + rts + +S6502 jsr SUB1 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + + end TEST diff --git a/6502_65C02_functional_tests/6502_functional_test.a65 b/6502_65C02_functional_tests/6502_functional_test.a65 new file mode 100644 index 0000000..0c7ce35 --- /dev/null +++ b/6502_65C02_functional_tests/6502_functional_test.a65 @@ -0,0 +1,6109 @@ +; +; 6 5 0 2 F U N C T I O N A L T E S T +; +; Copyright (C) 2012-2020 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 05-jan-2020 +; contact info at http://2m5.de or email K@2m5.de +; +; assembled with AS65 written by Frank A. Kingswood +; The assembler as65_142.zip can be obtained from my GitHub repository +; 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 +; 05-jan-2020 fixed shifts not testing zero result and flag when last 1-bit +; is shifted out + +; 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, $52 (82) consecutive Bytes required +; add 2 if I_flag = 2 +zero_page = $0 + +;data_segment memory start address, $7B (123) 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, 13.1kB of consecutive space required +; add 2.5 kB if I_flag = 2 +code_segment = $400 + +;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 = 1 + +;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 = 1 + + 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 ;6 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 +zps db $80,1 ;additional shift pattern to test zero result & flag +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 ;6 bytes store/modify test area +ada2 ds 1 ;operand 2 +sba2 ds 1 ;operand 2 complemented for subtract + ds 4 ;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 +;zps db $80,1 ;additional shift patterns test zero result & flag +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 0,2,$86,$04,$82,0 +rROLc db 1,3,$87,$05,$83,1 ;expected result ROL +carry +rLSR ;expected result LSR & ROR -carry +rROR db $40,0,$61,$41,$20,0 +rRORc db $c0,$80,$e1,$c1,$a0,$80 ;expected result ROR +carry +fASL ;expected flags for shifts +fROL db fzc,0,fnc,fc,fn,fz ;no carry in +fROLc db fc,0,fnc,fc,fn,0 ;carry in +fLSR +fROR db 0,fzc,fc,0,fc,fz ;no carry in +fRORc db fn,fnc,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 #5 +tasl + set_ax zps,0 + asl a + tst_ax rASL,fASL,0 + dex + bpl tasl + ldx #5 +tasl1 + set_ax zps,$ff + asl a + tst_ax rASL,fASL,$ff-fnzc + dex + bpl tasl1 + + ldx #5 +tlsr + set_ax zps,0 + lsr a + tst_ax rLSR,fLSR,0 + dex + bpl tlsr + ldx #5 +tlsr1 + set_ax zps,$ff + lsr a + tst_ax rLSR,fLSR,$ff-fnzc + dex + bpl tlsr1 + + ldx #5 +trol + set_ax zps,0 + rol a + tst_ax rROL,fROL,0 + dex + bpl trol + ldx #5 +trol1 + set_ax zps,$ff-fc + rol a + tst_ax rROL,fROL,$ff-fnzc + dex + bpl trol1 + + ldx #5 +trolc + set_ax zps,fc + rol a + tst_ax rROLc,fROLc,0 + dex + bpl trolc + ldx #5 +trolc1 + set_ax zps,$ff + rol a + tst_ax rROLc,fROLc,$ff-fnzc + dex + bpl trolc1 + + ldx #5 +tror + set_ax zps,0 + ror a + tst_ax rROR,fROR,0 + dex + bpl tror + ldx #5 +tror1 + set_ax zps,$ff-fc + ror a + tst_ax rROR,fROR,$ff-fnzc + dex + bpl tror1 + + ldx #5 +trorc + set_ax zps,fc + ror a + tst_ax rRORc,fRORc,0 + dex + bpl trorc + ldx #5 +trorc1 + set_ax zps,$ff + ror a + tst_ax rRORc,fRORc,$ff-fnzc + dex + bpl trorc1 + next_test + +; shifts - zeropage + ldx #5 +tasl2 + set_z zps,0 + asl zpt + tst_z rASL,fASL,0 + dex + bpl tasl2 + ldx #5 +tasl3 + set_z zps,$ff + asl zpt + tst_z rASL,fASL,$ff-fnzc + dex + bpl tasl3 + + ldx #5 +tlsr2 + set_z zps,0 + lsr zpt + tst_z rLSR,fLSR,0 + dex + bpl tlsr2 + ldx #5 +tlsr3 + set_z zps,$ff + lsr zpt + tst_z rLSR,fLSR,$ff-fnzc + dex + bpl tlsr3 + + ldx #5 +trol2 + set_z zps,0 + rol zpt + tst_z rROL,fROL,0 + dex + bpl trol2 + ldx #5 +trol3 + set_z zps,$ff-fc + rol zpt + tst_z rROL,fROL,$ff-fnzc + dex + bpl trol3 + + ldx #5 +trolc2 + set_z zps,fc + rol zpt + tst_z rROLc,fROLc,0 + dex + bpl trolc2 + ldx #5 +trolc3 + set_z zps,$ff + rol zpt + tst_z rROLc,fROLc,$ff-fnzc + dex + bpl trolc3 + + ldx #5 +tror2 + set_z zps,0 + ror zpt + tst_z rROR,fROR,0 + dex + bpl tror2 + ldx #5 +tror3 + set_z zps,$ff-fc + ror zpt + tst_z rROR,fROR,$ff-fnzc + dex + bpl tror3 + + ldx #5 +trorc2 + set_z zps,fc + ror zpt + tst_z rRORc,fRORc,0 + dex + bpl trorc2 + ldx #5 +trorc3 + set_z zps,$ff + ror zpt + tst_z rRORc,fRORc,$ff-fnzc + dex + bpl trorc3 + next_test + +; shifts - absolute + ldx #5 +tasl4 + set_abs zps,0 + asl abst + tst_abs rASL,fASL,0 + dex + bpl tasl4 + ldx #5 +tasl5 + set_abs zps,$ff + asl abst + tst_abs rASL,fASL,$ff-fnzc + dex + bpl tasl5 + + ldx #5 +tlsr4 + set_abs zps,0 + lsr abst + tst_abs rLSR,fLSR,0 + dex + bpl tlsr4 + ldx #5 +tlsr5 + set_abs zps,$ff + lsr abst + tst_abs rLSR,fLSR,$ff-fnzc + dex + bpl tlsr5 + + ldx #5 +trol4 + set_abs zps,0 + rol abst + tst_abs rROL,fROL,0 + dex + bpl trol4 + ldx #5 +trol5 + set_abs zps,$ff-fc + rol abst + tst_abs rROL,fROL,$ff-fnzc + dex + bpl trol5 + + ldx #5 +trolc4 + set_abs zps,fc + rol abst + tst_abs rROLc,fROLc,0 + dex + bpl trolc4 + ldx #5 +trolc5 + set_abs zps,$ff + rol abst + tst_abs rROLc,fROLc,$ff-fnzc + dex + bpl trolc5 + + ldx #5 +tror4 + set_abs zps,0 + ror abst + tst_abs rROR,fROR,0 + dex + bpl tror4 + ldx #5 +tror5 + set_abs zps,$ff-fc + ror abst + tst_abs rROR,fROR,$ff-fnzc + dex + bpl tror5 + + ldx #5 +trorc4 + set_abs zps,fc + ror abst + tst_abs rRORc,fRORc,0 + dex + bpl trorc4 + ldx #5 +trorc5 + set_abs zps,$ff + ror abst + tst_abs rRORc,fRORc,$ff-fnzc + dex + bpl trorc5 + next_test + +; shifts - zp indexed + ldx #5 +tasl6 + set_zx zps,0 + asl zpt,x + tst_zx rASL,fASL,0 + dex + bpl tasl6 + ldx #5 +tasl7 + set_zx zps,$ff + asl zpt,x + tst_zx rASL,fASL,$ff-fnzc + dex + bpl tasl7 + + ldx #5 +tlsr6 + set_zx zps,0 + lsr zpt,x + tst_zx rLSR,fLSR,0 + dex + bpl tlsr6 + ldx #5 +tlsr7 + set_zx zps,$ff + lsr zpt,x + tst_zx rLSR,fLSR,$ff-fnzc + dex + bpl tlsr7 + + ldx #5 +trol6 + set_zx zps,0 + rol zpt,x + tst_zx rROL,fROL,0 + dex + bpl trol6 + ldx #5 +trol7 + set_zx zps,$ff-fc + rol zpt,x + tst_zx rROL,fROL,$ff-fnzc + dex + bpl trol7 + + ldx #5 +trolc6 + set_zx zps,fc + rol zpt,x + tst_zx rROLc,fROLc,0 + dex + bpl trolc6 + ldx #5 +trolc7 + set_zx zps,$ff + rol zpt,x + tst_zx rROLc,fROLc,$ff-fnzc + dex + bpl trolc7 + + ldx #5 +tror6 + set_zx zps,0 + ror zpt,x + tst_zx rROR,fROR,0 + dex + bpl tror6 + ldx #5 +tror7 + set_zx zps,$ff-fc + ror zpt,x + tst_zx rROR,fROR,$ff-fnzc + dex + bpl tror7 + + ldx #5 +trorc6 + set_zx zps,fc + ror zpt,x + tst_zx rRORc,fRORc,0 + dex + bpl trorc6 + ldx #5 +trorc7 + set_zx zps,$ff + ror zpt,x + tst_zx rRORc,fRORc,$ff-fnzc + dex + bpl trorc7 + next_test + +; shifts - abs indexed + ldx #5 +tasl8 + set_absx zps,0 + asl abst,x + tst_absx rASL,fASL,0 + dex + bpl tasl8 + ldx #5 +tasl9 + set_absx zps,$ff + asl abst,x + tst_absx rASL,fASL,$ff-fnzc + dex + bpl tasl9 + + ldx #5 +tlsr8 + set_absx zps,0 + lsr abst,x + tst_absx rLSR,fLSR,0 + dex + bpl tlsr8 + ldx #5 +tlsr9 + set_absx zps,$ff + lsr abst,x + tst_absx rLSR,fLSR,$ff-fnzc + dex + bpl tlsr9 + + ldx #5 +trol8 + set_absx zps,0 + rol abst,x + tst_absx rROL,fROL,0 + dex + bpl trol8 + ldx #5 +trol9 + set_absx zps,$ff-fc + rol abst,x + tst_absx rROL,fROL,$ff-fnzc + dex + bpl trol9 + + ldx #5 +trolc8 + set_absx zps,fc + rol abst,x + tst_absx rROLc,fROLc,0 + dex + bpl trolc8 + ldx #5 +trolc9 + set_absx zps,$ff + rol abst,x + tst_absx rROLc,fROLc,$ff-fnzc + dex + bpl trolc9 + + ldx #5 +tror8 + set_absx zps,0 + ror abst,x + tst_absx rROR,fROR,0 + dex + bpl tror8 + ldx #5 +tror9 + set_absx zps,$ff-fc + ror abst,x + tst_absx rROR,fROR,$ff-fnzc + dex + bpl tror9 + + ldx #5 +trorc8 + set_absx zps,fc + ror abst,x + tst_absx rRORc,fRORc,0 + dex + bpl trorc8 + ldx #5 +trorc9 + set_absx zps,$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 +zps_ db $80,1 ;additional shift pattern to test zero result & flag +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 +;zps db $80,1 ;additional shift patterns test zero result & flag +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 0,2,$86,$04,$82,0 +rROLc_ db 1,3,$87,$05,$83,1 ;expected result ROL +carry +rLSR_ ;expected result LSR & ROR -carry +rROR_ db $40,0,$61,$41,$20,0 +rRORc_ db $c0,$80,$e1,$c1,$a0,$80 ;expected result ROR +carry +fASL_ ;expected flags for shifts +fROL_ db fzc,0,fnc,fc,fn,fz ;no carry in +fROLc_ db fc,0,fnc,fc,fn,0 ;carry in +fLSR_ +fROR_ db 0,fzc,fc,0,fc,fz ;no carry in +fRORc_ db fn,fnc,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 + \ No newline at end of file diff --git a/6502_65C02_functional_tests/6502_interrupt_test.a65 b/6502_65C02_functional_tests/6502_interrupt_test.a65 new file mode 100644 index 0000000..e88d22c --- /dev/null +++ b/6502_65C02_functional_tests/6502_interrupt_test.a65 @@ -0,0 +1,1026 @@ +; +; 6 5 0 2 I N T E R R U P T T E S T +; +; Copyright (C) 2013 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 IRQ and NMI of a 6502 emulator. It requires +; an internal or external feedback register to the IRQ & NMI inputs +; +; version 15-aug-2014 +; 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 and +; enter a go command. +; Loop on program counter determines error or successful completion of test. +; Check listing for relevant traps (jump/branch *). +; +; 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: +; 19-jul-2013 1st version distributed for testing +; 16-aug-2013 added error report to standard output option +; 15-aug-2014 added filter to feedback (bit 7 will cause diag stop in emu) + + +; C O N F I G U R A T I O N +; +;ROM_vectors MUST be writable & the I_flag MUST be alterable + +;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 + +;NMI & IRQ are tested with a feedback register +;emulators diag register - set i_drive = 0 for a latch (74HC573) +I_port = $bffc ;feedback port address +I_ddr = 0 ;feedback DDR address, 0 = no DDR +I_drive = 1 ;0 = totem pole, 1 = open collector +IRQ_bit = 0 ;bit number of feedback to IRQ +NMI_bit = 1 ;bit number of feedback to NMI, -1 if not available +I_filter = $7f ;filtering bit 7 = diag stop + +;typical IO chip port B - set i_drive = 0 to avoid pullup resistors +;I_port = $bfb2 ;feedback port address +;I_ddr = $bfb3 ;feedback DDR address, 0 = no DDR +;I_drive = 1 ;0 = totem pole, 1 = open collector +;IRQ_bit = 0 ;bit number of feedback to IRQ +;NMI_bit = 1 ;bit number of feedback to NMI, -1 if not available +;I_filter = $ff ;no bits filtered + +;decimal mode flag during IRQ, NMI & BRK +D_clear = 0 ;0 = not cleared (NMOS), 1 = cleared (CMOS) + +;configure memory - try to stay away from memory used by the system +;zero_page memory start address, 6 consecutive Bytes required +zero_page = $0 + +;data_segment memory start address, 4 consecutive Bytes required +data_segment = $200 + +;code_segment memory start address +code_segment = $400 + +;report errors through I/O channel (0=use standard self trap loops, 1=include +;report.i65 as I/O channel) +report = 1 + + 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 +; 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 +; 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 +m8 equ $ff ;8 bit mask +m8i equ $ff&~intdis ;8 bit mask - interrupt disable + +;macros to set status +push_stat macro ;setting flags in the processor status register + lda #\1 + pha ;use stack to load status + endm + +set_stat macro ;setting flags in the processor status register + lda #\1 + pha ;use stack to load status + plp + endm + + if load_data_direct = 1 + data + else + bss ;uninitialized segment, copy of data at end of code! + endif + org zero_page +;BRK, IRQ, NMI test interrupt save +zpt +irq_a ds 1 ;a register +irq_x ds 1 ;x register +irq_f ds 1 ;flags +nmi_a ds 1 ;a register +nmi_x ds 1 ;x register +nmi_f ds 1 ;flags +zp_bss + +;fixed stack locations +lst_f equ $1fe ;last flags before interrupt +lst_a equ $1ff ;last accumulator before interrupt + + org data_segment +;concurrent NMI, IRQ & BRK test result +nmi_count ds 1 ;lowest number handled first, $ff = never +irq_count ds 1 ;separation-1 = instructions between interrupts +brk_count ds 1 +;expected interrupt mask +I_src ds 1 ;bit: 0=BRK, 1=IRQ, 2=NMI +data_bss + + code + org code_segment +start cld + lda #0 ;clear expected interrupts for 2nd run + sta I_src + ldx #$ff + txs + +;initialize I/O for report channel + if report = 1 + jsr report_init + endif + +; load system vectors + if load_data_direct != 1 + ldx #5 +ld_vect lda vec_init,x + sta vec_bss,x + dex + bpl ld_vect + endif + +; IRQ & NMI test - requires a feedback register + if I_drive > 1 + ERROR ;invalid interrupt drive! + endif + if NMI_bit < 0 + if I_drive = 0 ;totem pole (push/pull, 0 -> I_port to force interrupt) +I_set macro ibit ;ibit = interrupt bit + lda I_port ;turn on interrupt by bit + and #I_filter-(1<<\1) + plp ;set flags + pha ;save to verify + php + sta I_port ;interrupt next instruction plus outbound delay + endm +I_clr macro ibit ;ibit = interrupt bit + lda I_port ;turn off interrupt by bit + and #I_filter + ora #(1< I_DDR or I_port to force interrupt + if I_ddr != 0 ;with DDR +I_set macro ibit ;ibit = interrupt bit + lda I_ddr ;turn on interrupt by bit + and #I_filter + ora #(1<<\1) + plp ;set flags + pha ;save to verify + php + sta I_ddr ;interrupt next instruction plus outbound delay + endm +I_clr macro ibit ;ibit = interrupt bit + lda I_ddr ;turn off interrupt by bit + and #I_filter-(1< I_port to force interrupt) +I_set macro ibit ;ibit = interrupt bit + lda I_port ;turn on interrupt by bit + if ibit > 7 ;set both NMI & IRQ + and #I_filter-(1< I_DDR or I_port to force interrupt + if I_ddr != 0 ;with DDR +I_set macro ibit ;ibit = interrupt bit + lda I_ddr ;turn on interrupt by bit + and #I_filter + if ibit > 7 ;set both NMI & IRQ + ora #(1< 7 ;set both NMI & IRQ + ora #(1<. + + +; This program is designed to test all additional 65C02 opcodes, addressing +; modes and functionality not available in the NMOS version of the 6502. +; The 6502_functional_test is a prerequisite to this test. +; NMI, IRQ, STP & WAI are covered in the 6502_interrupt_test. +; +; 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 -x -h0 +; | | | | | no page headers in listing +; | | | | 65C02 extensions +; | | | 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 65C02 only! +; Decimal ops will only be tested with valid BCD operands and the V flag will +; be ignored as it is absolutely useless in decimal mode. +; +; 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: +; 19-jul-2013 1st version distributed for testing +; 23-jul-2013 fixed BRA out of range due to larger trap macros +; added RAM integrity check +; 16-aug-2013 added error report to standard output option +; 23-aug-2015 change revoked +; 24-aug-2015 all self modifying immediate opcodes now execute in data RAM +; 28-aug-2015 fixed decimal adc/sbc immediate only testing carry +; 09-feb-2017 fixed RMB/SMB tested when they shouldn't be tested +; 04-dec-2017 fixed BRK not tested for actually going through the IRQ vector +; added option to skip the remainder of a failing test +; in report.i65 +; added skip override to undefined opcode as NOP test + + +; 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. +I_flag = 3 + +;configure memory - try to stay away from memory used by the system +;zero_page memory start address, $4e (78) consecutive Bytes required +; add 2 if I_flag = 2 +zero_page = $a + +;data_segment memory start address, $63 (99) consecutive Bytes required +; + 12 Bytes at data_segment + $f9 (JMP indirect page cross test) +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, 10kB of consecutive space required +; add 1 kB if I_flag = 2 +code_segment = $400 + +;added WDC only opcodes WAI & STP (0=test as NOPs, >0=no test) +wdc_op = 1 + +;added Rockwell & WDC opcodes BBR, BBS, RMB & SMB +;(0=test as NOPs, 1=full test, >1=no test) +rkwl_wdc_op = 1 + +;skip testing all undefined opcodes override +;0=test as NOP, >0=skip +skip_nop = 0 + +;report errors through I/O channel (0=use standard self trap loops, 1=include +;report.i65 as I/O channel, add 3 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 + + 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 +m8 equ $ff ;8 bit mask +m8i equ $ff&~intdis ;8 bit mask - interrupt disable + +;macros to allow masking of status bits. +;masking of interrupt enable/disable on load and compare +;masking of always on bits after PHP or BRK (unused & break) on compare + 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 + +;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_as macro ;testing result in accu & flags, save accu + pha + php ;save flags + cmp #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + pla + 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 + ldx #11 ;reset modifiable RAM +ccs1\? sta jxi_tab,x ;JMP indirect page cross area + dex + bpl ccs1\? + 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 (-V in decimal 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 +pg_x ds 2 ;high JMP indirect address for page cross bug +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_adci adc #0 ;execute immediate opcodes + rts +ex_sbci sbc #0 ;execute immediate opcodes + rts + else +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 +;define area for page crossing JMP (abs) & JMP (abs,x) test +jxi_tab equ data_segment + $100 - 7 ;JMP (jxi_tab,x) x=6 +ji_tab equ data_segment + $100 - 3 ;JMP (ji_tab+2) +jxp_tab equ data_segment + $100 ;JMP (jxp_tab-255) x=255 + + + 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 + +;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 + ldx #11 ;reset modifiable RAM +gcs1 sta jxi_tab,x ;JMP indirect page cross area + dex + bpl gcs1 + 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 + +;testing stack operations PHX PHY PLX PLY + lda #$99 ;protect a + ldx #$ff ;initialize stack + txs + ldx #$55 + phx + ldx #$aa + phx + cpx $1fe ;on stack ? + trap_ne + tsx + cpx #$fd ;sp decremented? + trap_ne + ply + cpy #$aa ;successful retreived from stack? + trap_ne + ply + cpy #$55 + trap_ne + cpy $1ff ;remains on stack? + trap_ne + tsx + cpx #$ff ;sp incremented? + trap_ne + + ldy #$a5 + phy + ldy #$5a + phy + cpy $1fe ;on stack ? + trap_ne + tsx + cpx #$fd ;sp decremented? + trap_ne + plx + cpx #$5a ;successful retreived from stack? + trap_ne + plx + cpx #$a5 + trap_ne + cpx $1ff ;remains on stack? + trap_ne + tsx + cpx #$ff ;sp incremented? + trap_ne + cmp #$99 ;unchanged? + trap_ne + next_test + +; test PHX does not alter flags or X but PLX does + ldy #$aa ;protect y + set_x 1,$ff ;push + phx + tst_x 1,$ff + set_x 0,0 + phx + tst_x 0,0 + set_x $ff,$ff + phx + tst_x $ff,$ff + set_x 1,0 + phx + tst_x 1,0 + set_x 0,$ff + phx + tst_x 0,$ff + set_x $ff,0 + phx + tst_x $ff,0 + set_x 0,$ff ;pull + plx + tst_x $ff,$ff-zero + set_x $ff,0 + plx + tst_x 0,zero + set_x $fe,$ff + plx + tst_x 1,$ff-zero-minus + set_x 0,0 + plx + tst_x $ff,minus + set_x $ff,$ff + plx + tst_x 0,$ff-minus + set_x $fe,0 + plx + tst_x 1,0 + cpy #$aa ;Y unchanged + trap_ne + next_test + +; test PHY does not alter flags or Y but PLY does + ldx #$55 ;x & a protected + set_y 1,$ff ;push + phy + tst_y 1,$ff + set_y 0,0 + phy + tst_y 0,0 + set_y $ff,$ff + phy + tst_y $ff,$ff + set_y 1,0 + phy + tst_y 1,0 + set_y 0,$ff + phy + tst_y 0,$ff + set_y $ff,0 + phy + tst_y $ff,0 + set_y 0,$ff ;pull + ply + tst_y $ff,$ff-zero + set_y $ff,0 + ply + tst_y 0,zero + set_y $fe,$ff + ply + tst_y 1,$ff-zero-minus + set_y 0,0 + ply + tst_y $ff,minus + set_y $ff,$ff + ply + tst_y 0,$ff-minus + set_y $fe,0 + ply + tst_y 1,0 + cpx #$55 ;x unchanged? + trap_ne + next_test + +; PC modifying instructions (BRA, BBR, BBS, 1, 2, 3 byte NOPs, JMP(abs,x)) +; testing unconditional branch BRA + + ldx #$81 ;protect unused registers + ldy #$7e + set_a 0,$ff + bra br1 ;branch should always be taken + trap +br1 + tst_a 0,$ff + set_a $ff,0 + bra br2 ;branch should always be taken + trap +br2 + tst_a $ff,0 + cpx #$81 + trap_ne + cpy #$7e + trap_ne + next_test + + ldy #0 ;branch range test + bra bra0 + +bra1 cpy #1 + trap_ne ;long range backward + iny + bra bra2 + +bra3 cpy #3 + trap_ne ;long range backward + iny + bra bra4 + +bra5 cpy #5 + trap_ne ;long range backward + iny + ldy #0 + bra brf0 + + iny + iny + iny + iny +brf0 bra brf1 + + iny + iny + iny +brf1 iny + bra brf2 + + iny + iny +brf2 iny + iny + bra brf3 + + iny +brf3 iny + iny + iny + bra brf4 + +brf4 iny + iny + iny + iny + cpy #10 + trap_ne ;short range forward + bra brb0 + +brb4 dey + dey + dey + dey + bra brb5 + +brb3 dey + dey + dey + bra brb4 + +brb2 dey + dey + bra brb3 + +brb1 dey + bra brb2 + +brb0 bra brb1 + +brb5 cpy #0 + trap_ne ;short range backward + bra bra6 + +bra4 cpy #4 + trap_ne ;long range forward + iny + bra bra5 + +bra2 cpy #2 + trap_ne ;long range forward + iny + bra bra3 + +bra0 cpy #0 + trap_ne ;long range forward + iny + bra bra1 + +bra6 + next_test + + if rkwl_wdc_op = 1 +; testing BBR & BBS + +bbt macro ;\1 = bitnum + lda #(1<<\1) ;testing 1 bit on + sta zpt + set_a $33,0 ;with flags off + bbr \1,zpt,fail1\? + bbs \1,zpt,ok1\? + trap ;bbs branch not taken +fail1\? + trap ;bbr branch taken +ok1\? + tst_a $33,0 + set_a $cc,$ff ;with flags on + bbr \1,zpt,fail2\? + bbs \1,zpt,ok2\? + trap ;bbs branch not taken +fail2\? + trap ;bbr branch taken +ok2\? + tst_a $cc,$ff + lda zpt + cmp #(1<<\1) + trap_ne ;zp altered + lda #$ff-(1<<\1) ;testing 1 bit off + sta zpt + set_a $33,0 ;with flags off + bbs \1,zpt,fail3\? + bbr \1,zpt,ok3\? + trap ;bbr branch not taken +fail3\? + trap ;bbs branch taken +ok3\? + tst_a $33,0 + set_a $cc,$ff ;with flags on + bbs \1,zpt,fail4\? + bbr \1,zpt,ok4\? + trap ;bbr branch not taken +fail4\? + trap ;bbs branch taken +ok4\? + tst_a $cc,$ff + lda zpt + cmp #$ff-(1<<\1) + trap_ne ;zp altered + endm + + ldx #$11 ;test bbr/bbs integrity + ldy #$22 + bbt 0 + bbt 1 + bbt 2 + bbt 3 + bbt 4 + bbt 5 + bbt 6 + bbt 7 + cpx #$11 + trap_ne ;x overwritten + cpy #$22 + trap_ne ;y overwritten + next_test + +bbrc macro ;\1 = bitnum + bbr \1,zpt,skip\? + eor #(1<<\1) +skip\? + endm +bbsc macro ;\1 = bitnum + bbs \1,zpt,skip\? + eor #(1<<\1) +skip\? + endm + + lda #0 ;combined bit test + sta zpt +bbcl lda #0 + bbrc 0 + bbrc 1 + bbrc 2 + bbrc 3 + bbrc 4 + bbrc 5 + bbrc 6 + bbrc 7 + eor zpt + trap_ne ;failed bbr bitnum in accu + lda #$ff + bbsc 0 + bbsc 1 + bbsc 2 + bbsc 3 + bbsc 4 + bbsc 5 + bbsc 6 + bbsc 7 + eor zpt + trap_ne ;failed bbs bitnum in accu + inc zpt + bne bbcl + next_test + endif + +; testing NOP + +nop_test macro ;\1 = opcode, \2 = # of bytes + ldy #$42 + ldx #4-\2 + db \1 ;test nop length + if \2 = 1 + dex + dex + endif + if \2 = 2 + iny + dex + endif + if \2 = 3 + iny + iny + endif + dex + trap_ne ;wrong number of bytes + set_a $ff-\1,0 + db \1 ;test nop integrity - flags off + nop + nop + tst_a $ff-\1,0 + set_a $aa-\1,$ff + db \1 ;test nop integrity - flags on + nop + nop + tst_a $aa-\1,$ff + cpy #$42 + trap_ne ;y changed + cpx #0 + trap_ne ;x changed + endm + + if skip_nop = 0 + nop_test $02,2 + nop_test $22,2 + nop_test $42,2 + nop_test $62,2 + nop_test $82,2 + nop_test $c2,2 + nop_test $e2,2 + nop_test $44,2 + nop_test $54,2 + nop_test $d4,2 + nop_test $f4,2 + nop_test $5c,3 + nop_test $dc,3 + nop_test $fc,3 + nop_test $03,1 + nop_test $13,1 + nop_test $23,1 + nop_test $33,1 + nop_test $43,1 + nop_test $53,1 + nop_test $63,1 + nop_test $73,1 + nop_test $83,1 + nop_test $93,1 + nop_test $a3,1 + nop_test $b3,1 + nop_test $c3,1 + nop_test $d3,1 + nop_test $e3,1 + nop_test $f3,1 + nop_test $0b,1 + nop_test $1b,1 + nop_test $2b,1 + nop_test $3b,1 + nop_test $4b,1 + nop_test $5b,1 + nop_test $6b,1 + nop_test $7b,1 + nop_test $8b,1 + nop_test $9b,1 + nop_test $ab,1 + nop_test $bb,1 + nop_test $eb,1 + nop_test $fb,1 + if rkwl_wdc_op = 0 ;NOPs not available on Rockwell & WDC 65C02 + nop_test $07,1 + nop_test $17,1 + nop_test $27,1 + nop_test $37,1 + nop_test $47,1 + nop_test $57,1 + nop_test $67,1 + nop_test $77,1 + nop_test $87,1 + nop_test $97,1 + nop_test $a7,1 + nop_test $b7,1 + nop_test $c7,1 + nop_test $d7,1 + nop_test $e7,1 + nop_test $f7,1 + nop_test $0f,1 + nop_test $1f,1 + nop_test $2f,1 + nop_test $3f,1 + nop_test $4f,1 + nop_test $5f,1 + nop_test $6f,1 + nop_test $7f,1 + nop_test $8f,1 + nop_test $9f,1 + nop_test $af,1 + nop_test $bf,1 + nop_test $cf,1 + nop_test $df,1 + nop_test $ef,1 + nop_test $ff,1 + endif + if wdc_op = 0 ;NOPs not available on WDC 65C02 (WAI, STP) + nop_test $cb,1 + nop_test $db,1 + endif + next_test + endif + +; jump indirect (test page cross bug is fixed) + ldx #3 ;prepare table +ji1 lda ji_adr,x + sta ji_tab,x + dex + bpl ji1 + lda #hi(ji_px) ;high address if page cross bug + sta pg_x + set_stat 0 + lda #'I' + ldx #'N' + ldy #'D' ;N=0, V=0, Z=0, C=0 + jmp (ji_tab) + nop + trap_ne ;runover protection + + dey + dey +ji_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 indexed indirect + ldx #11 ;prepare table +jxi1 lda jxi_adr,x + sta jxi_tab,x + dex + bpl jxi1 + lda #hi(jxi_px) ;high address if page cross bug + sta pg_x + set_stat 0 + lda #'X' + ldx #4 + ldy #'I' ;N=0, V=0, Z=0, C=0 + jmp (jxi_tab,x) + nop + trap_ne ;runover protection + + dey + dey +jxi_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 #('X'^$aa) ;returned registers OK? + trap_ne + cpx #6 + trap_ne + cpy #('I'-6) + trap_ne + tsx ;SP check + cpx #$ff + trap_ne + + lda #lo(jxp_ok) ;test with index causing a page cross + sta jxp_tab + lda #hi(jxp_ok) + sta jxp_tab+1 + lda #lo(jxp_px) + sta pg_x + lda #hi(jxp_px) + sta pg_x+1 + ldx #$ff + jmp (jxp_tab-$ff,x) + +jxp_px + trap ;page cross by index to wrong page + +jxp_ok + next_test + + if ROM_vectors = 1 +; test BRK clears decimal mode + 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 + 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 +;pass 2 + 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 + 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 + endif + +; testing accumulator increment/decrement INC A & DEC A + ldx #$ac ;protect x & y + ldy #$dc + set_a $fe,$ff + inc a ;ff + tst_as $ff,$ff-zero + inc a ;00 + tst_as 0,$ff-minus + inc a ;01 + tst_as 1,$ff-minus-zero + dec a ;00 + tst_as 0,$ff-minus + dec a ;ff + tst_as $ff,$ff-zero + dec a ;fe + set_a $fe,0 + inc a ;ff + tst_as $ff,minus + inc a ;00 + tst_as 0,zero + inc a ;01 + tst_as 1,0 + dec a ;00 + tst_as 0,zero + dec a ;ff + tst_as $ff,minus + cpx #$ac + trap_ne ;x altered during test + cpy #$dc + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing load / store accumulator LDA / STA (zp) + ldx #$99 ;protect x & y + ldy #$66 + set_stat 0 + lda (ind1) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt) + 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 (ind1+2) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+2) + 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 (ind1+4) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+4) + 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 (ind1+6) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+6) + 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 + cpx #$99 + trap_ne ;x altered during test + cpy #$66 + trap_ne ;y altered during test + + ldy #3 ;testing store result + ldx #0 +tstai1 lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to indirect data + txa + sta abst,y ;clear + dey + bpl tstai1 + + ldx #$99 ;protect x & y + ldy #$66 + set_stat $ff + lda (ind1) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt) + 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 (ind1+2) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+2) + 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 (ind1+4) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+4) + 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 (ind1+6) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+6) + 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 + cpx #$99 + trap_ne ;x altered during test + cpy #$66 + trap_ne ;y altered during test + + ldy #3 ;testing store result + ldx #0 +tstai2 lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to indirect data + txa + sta abst,y ;clear + dey + bpl tstai2 + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing STZ - zp / abs / zp,x / abs,x + ldy #123 ;protect y + ldx #4 ;precharge test area + lda #7 +tstz1 sta zpt,x + asl a + dex + bpl tstz1 + ldx #4 + set_a $55,$ff + stz zpt + stz zpt+1 + stz zpt+2 + stz zpt+3 + stz zpt+4 + tst_a $55,$ff +tstz2 lda zpt,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz2 + ldx #4 ;precharge test area + lda #7 +tstz3 sta zpt,x + asl a + dex + bpl tstz3 + ldx #4 + set_a $aa,0 + stz zpt + stz zpt+1 + stz zpt+2 + stz zpt+3 + stz zpt+4 + tst_a $aa,0 +tstz4 lda zpt,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz4 + + ldx #4 ;precharge test area + lda #7 +tstz5 sta abst,x + asl a + dex + bpl tstz5 + ldx #4 + set_a $55,$ff + stz abst + stz abst+1 + stz abst+2 + stz abst+3 + stz abst+4 + tst_a $55,$ff +tstz6 lda abst,x ;verify zeros stored + trap_ne ;non zero after STZ abs + dex + bpl tstz6 + ldx #4 ;precharge test area + lda #7 +tstz7 sta abst,x + asl a + dex + bpl tstz7 + ldx #4 + set_a $aa,0 + stz abst + stz abst+1 + stz abst+2 + stz abst+3 + stz abst+4 + tst_a $aa,0 +tstz8 lda abst,x ;verify zeros stored + trap_ne ;non zero after STZ abs + dex + bpl tstz8 + + ldx #4 ;precharge test area + lda #7 +tstz11 sta zpt,x + asl a + dex + bpl tstz11 + ldx #4 +tstz15 + set_a $55,$ff + stz zpt,x + tst_a $55,$ff + dex + bpl tstz15 + ldx #4 +tstz12 lda zpt,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz12 + ldx #4 ;precharge test area + lda #7 +tstz13 sta zpt,x + asl a + dex + bpl tstz13 + ldx #4 +tstz16 + set_a $aa,0 + stz zpt,x + tst_a $aa,0 + dex + bpl tstz16 + ldx #4 +tstz14 lda zpt,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz14 + + ldx #4 ;precharge test area + lda #7 +tstz21 sta abst,x + asl a + dex + bpl tstz21 + ldx #4 +tstz25 + set_a $55,$ff + stz abst,x + tst_a $55,$ff + dex + bpl tstz25 + ldx #4 +tstz22 lda abst,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz22 + ldx #4 ;precharge test area + lda #7 +tstz23 sta abst,x + asl a + dex + bpl tstz23 + ldx #4 +tstz26 + set_a $aa,0 + stz abst,x + tst_a $aa,0 + dex + bpl tstz26 + ldx #4 +tstz24 lda abst,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz24 + + cpy #123 + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing BIT - zp,x / abs,x / # + ldy #$42 + ldx #3 + set_a $ff,0 + bit zp1,x ;00 - should set Z / clear NV + tst_a $ff,fz + dex + set_a 1,0 + bit zp1,x ;41 - should set V (M6) / clear NZ + tst_a 1,fv + dex + set_a 1,0 + bit zp1,x ;82 - should set N (M7) & Z / clear V + tst_a 1,fnz + dex + set_a 1,0 + bit zp1,x ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,fnv + + set_a 1,$ff + bit zp1,x ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,~fz + inx + set_a 1,$ff + bit zp1,x ;82 - should set N (M7) & Z / clear V + tst_a 1,~fv + inx + set_a 1,$ff + bit zp1,x ;41 - should set V (M6) / clear NZ + tst_a 1,~fnz + inx + set_a $ff,$ff + bit zp1,x ;00 - should set Z / clear NV + tst_a $ff,~fnv + + set_a $ff,0 + bit abs1,x ;00 - should set Z / clear NV + tst_a $ff,fz + dex + set_a 1,0 + bit abs1,x ;41 - should set V (M6) / clear NZ + tst_a 1,fv + dex + set_a 1,0 + bit abs1,x ;82 - should set N (M7) & Z / clear V + tst_a 1,fnz + dex + set_a 1,0 + bit abs1,x ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,fnv + + set_a 1,$ff + bit abs1,x ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,~fz + inx + set_a 1,$ff + bit abs1,x ;82 - should set N (M7) & Z / clear V + tst_a 1,~fv + inx + set_a 1,$ff + bit abs1,x ;41 - should set V (M6) / clear NZ + tst_a 1,~fnz + inx + set_a $ff,$ff + bit abs1,x ;00 - should set Z / clear NV + tst_a $ff,~fnv + + set_a $ff,0 + bit #$00 ;00 - should set Z + tst_a $ff,fz + dex + set_a 1,0 + bit #$41 ;41 - should clear Z + tst_a 1,0 +; *** DEBUG INFO *** +; if it fails the previous test and your BIT # has set the V flag +; see http://forum.6502.org/viewtopic.php?f=2&t=2241&p=27243#p27239 +; why it shouldn't alter N or V flags on a BIT # + dex + set_a 1,0 + bit #$82 ;82 - should set Z + tst_a 1,fz + dex + set_a 1,0 + bit #$c3 ;c3 - should clear Z + tst_a 1,0 + + set_a 1,$ff + bit #$c3 ;c3 - clear Z + tst_a 1,~fz + inx + set_a 1,$ff + bit #$82 ;82 - should set Z + tst_a 1,$ff + inx + set_a 1,$ff + bit #$41 ;41 - should clear Z + tst_a 1,~fz + inx + set_a $ff,$ff + bit #$00 ;00 - should set Z + tst_a $ff,$ff + + cpx #3 + trap_ne ;x altered during test + cpy #$42 + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing TRB, TSB - zp / abs + +trbt macro ;\1 = memory, \2 = flags + sty \1 + load_flag \2 + pha + lda zpt+1 + plp + trb \1 + php + cmp zpt+1 + trap_ne ;accu was changed + pla + pha + ora #fz ;mask Z + cmp_flag \2|fz + trap_ne ;flags changed except Z + pla + and #fz + cmp zpt+2 + trap_ne ;Z flag invalid + lda zpt+3 + cmp zpt + trap_ne ;altered bits in memory wrong + endm + +tsbt macro ;\1 = memory, \2 = flags + sty \1 + load_flag \2 + pha + lda zpt+1 + plp + tsb \1 + php + cmp zpt+1 + trap_ne ;accu was changed + pla + pha + ora #fz ;mask Z + cmp_flag \2|fz + trap_ne ;flags changed except Z + pla + and #fz + cmp zpt+2 + trap_ne ;Z flag invalid + lda zpt+4 + cmp zpt + trap_ne ;altered bits in memory wrong + endm + + ldx #$c0 + ldy #0 ;op1 - memory save + ; zpt ;op1 - memory modifiable + stz zpt+1 ;op2 - accu + ; zpt+2 ;and flags + ; zpt+3 ;memory after reset + ; zpt+4 ;memory after set + +tbt1 tya + and zpt+1 ;set Z by anding the 2 operands + php + pla + and #fz ;mask Z + sta zpt+2 + tya ;reset op1 bits by op2 + eor #$ff + ora zpt+1 + eor #$ff + sta zpt+3 + tya ;set op1 bits by op2 + ora zpt+1 + sta zpt+4 + + trbt zpt,$ff + trbt abst,$ff + trbt zpt,0 + trbt abst,0 + tsbt zpt,$ff + tsbt abst,$ff + tsbt zpt,0 + tsbt abst,0 + + iny ;iterate op1 + bne tbt3 + inc zpt+1 ;iterate op2 + beq tbt2 +tbt3 jmp tbt1 +tbt2 + cpx #$c0 + trap_ne ;x altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + + if rkwl_wdc_op = 1 +; testing RMB, SMB - zp +rmbt macro ;\1 = bitnum + lda #$ff + sta zpt + set_a $a5,0 + rmb \1,zpt + tst_a $a5,0 + lda zpt + cmp #$ff-(1<<\1) + trap_ne ;wrong bits set or cleared + lda #1<<\1 + sta zpt + set_a $5a,$ff + rmb \1,zpt + tst_a $5a,$ff + lda zpt + trap_ne ;wrong bits set or cleared + endm +smbt macro ;\1 = bitnum + lda #$ff-(1<<\1) + sta zpt + set_a $a5,0 + smb \1,zpt + tst_a $a5,0 + lda zpt + cmp #$ff + trap_ne ;wrong bits set or cleared + lda #0 + sta zpt + set_a $5a,$ff + smb \1,zpt + tst_a $5a,$ff + lda zpt + cmp #1<<\1 + trap_ne ;wrong bits set or cleared + endm + + ldx #$ba ;protect x & y + ldy #$d0 + rmbt 0 + rmbt 1 + rmbt 2 + rmbt 3 + rmbt 4 + rmbt 5 + rmbt 6 + rmbt 7 + smbt 0 + smbt 1 + smbt 2 + smbt 3 + smbt 4 + smbt 5 + smbt 6 + smbt 7 + cpx #$ba + trap_ne ;x altered during test + cpy #$d0 + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + endif + +; testing CMP - (zp) + ldx #$de ;protect x & y + ldy #$ad + set_a $80,0 + cmp (ind1+8) + tst_a $80,fc + set_a $7f,0 + cmp (ind1+8) + tst_a $7f,fzc + set_a $7e,0 + cmp (ind1+8) + tst_a $7e,fn + set_a $80,$ff + cmp (ind1+8) + tst_a $80,~fnz + set_a $7f,$ff + cmp (ind1+8) + tst_a $7f,~fn + set_a $7e,$ff + cmp (ind1+8) + tst_a $7e,~fzc + cpx #$de + trap_ne ;x altered during test + cpy #$ad + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing logical instructions - AND EOR ORA (zp) + ldx #$42 ;protect x & y + + ldy #0 ;AND + lda indAN ;set indirect address + sta zpt + lda indAN+1 + sta zpt+1 +tand1 + set_ay absANa,0 + and (zpt) + tst_ay absrlo,absflo,0 + inc zpt + iny + cpy #4 + bne tand1 + dey + dec zpt +tand2 + set_ay absANa,$ff + and (zpt) + tst_ay absrlo,absflo,$ff-fnz + dec zpt + dey + bpl tand2 + + ldy #0 ;EOR + lda indEO ;set indirect address + sta zpt + lda indEO+1 + sta zpt+1 +teor1 + set_ay absEOa,0 + eor (zpt) + tst_ay absrlo,absflo,0 + inc zpt + iny + cpy #4 + bne teor1 + dey + dec zpt +teor2 + set_ay absEOa,$ff + eor (zpt) + tst_ay absrlo,absflo,$ff-fnz + dec zpt + dey + bpl teor2 + + ldy #0 ;ORA + lda indOR ;set indirect address + sta zpt + lda indOR+1 + sta zpt+1 +tora1 + set_ay absORa,0 + ora (zpt) + tst_ay absrlo,absflo,0 + inc zpt + iny + cpy #4 + bne tora1 + dey + dec zpt +tora2 + set_ay absORa,$ff + ora (zpt) + tst_ay absrlo,absflo,$ff-fnz + dec zpt + dey + bpl tora2 + + cpx #$42 + trap_ne ;x altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + + if I_flag = 3 + cli + endif + +; full binary add/subtract test - (zp) only +; 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 + + cpx #ad2 + trap_ne ;x altered during test + cpy #$ff + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; decimal add/subtract test +; *** WARNING - tests documented behavior only! *** +; only valid BCD operands are tested, the V flag is ignored +; although V is declared as beeing valid on the 65C02 it has absolutely +; no use in BCD math. No sign = no overflow! +; 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 #$81 ;set N & C (99 + 99 + C = 99 + C) + sta adrf + 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 php ;save valid flags + pla + and #$82 ;N-----Z- + ora adrh ;N-----ZC + sta adrf + 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 + php ;save flags + pla + and #$82 ;N-----Z- + ora #1 ;N-----ZC + sta adrf + inc adrh ;result carry + jmp tdad ;iterate op2 + +tdad7 cpx #ad2 + trap_ne ;x altered during test + cpy #$ff + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + cld + + 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 + +; core subroutine of the decimal add/subtract test +; *** WARNING - tests documented behavior only! *** +; only valid BCD operands are tested, V flag is 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 #$83 ;mask N-----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 #$83 ;mask N-----ZC + cmp adrf + 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 #$83 ;mask N-----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 #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + 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 #$83 ;mask N-----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 #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + 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 #$83 ;mask N-----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 #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + 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 #$83 ;mask N-----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 #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + 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 #$83 ;mask N-----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 #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + 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 #$83 ;mask N-----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 #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + 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 #$83 ;mask N-----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 #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp +; decimal ADC / SBC (zp) + php ;save carry for subtract + lda ad1 + adc (adi2) ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc (sbi2) ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + rts + +; 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 (adi2) ;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 (sbi2) ;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 indirect test +ji_adr dw test_ji + dw ji_ret + + dey + dey +test_ji + 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 (ji_tab+2) + nop + nop + trap ;runover protection + jmp start ;catastrophic error - cannot continue + +; target for the jump indirect test +jxi_adr dw trap_ind + dw trap_ind + dw test_jxi ;+4 + dw jxi_ret ;+6 + dw trap_ind + dw trap_ind + + dey + dey +test_jxi + 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 #'X' ;registers loaded? + trap_ne + cpx #4 + trap_ne + cpy #('I'-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 + inx + eor #$aa ;N=1, V=1, Z=0, C=1 + jmp (jxi_tab,x) + nop + nop + trap ;runover protection + jmp start ;catastrophic error - cannot continue + +; JMP (abs,x) with bad x + nop + nop +trap_ind + nop + nop + trap ;near miss indexed indirect jump + jmp start ;catastrophic error - cannot continue + +;trap in case of unexpected IRQ, NMI, BRK, RESET +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 ;possible 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 ;possibly no break flag on stack + pla + cmp_flag $ff-decmode ;actual passed flags should have decmode cleared + 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_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 + +; code at end of image due to the need to add blank space as required + if ($ff & (ji_ret - * - 2)) < ($ff & (jxi_ret - * - 2)) +; JMP (abs) when $xxff and $xx00 are from same page + ds lo(ji_ret - * - 2) + nop + nop +ji_px nop ;low address byte matched with ji_ret + nop + trap ;jmp indirect page cross bug + +; JMP (abs,x) when $xxff and $xx00 are from same page + ds lo(jxi_ret - * - 2) + nop + nop +jxi_px nop ;low address byte matched with jxi_ret + nop + trap ;jmp indexed indirect page cross bug + else +; JMP (abs,x) when $xxff and $xx00 are from same page + ds lo(jxi_ret - * - 2) + nop + nop +jxi_px nop ;low address byte matched with jxi_ret + nop + trap ;jmp indexed indirect page cross bug + +; JMP (abs) when $xxff and $xx00 are from same page + ds lo(ji_ret - * - 2) + nop + nop +ji_px nop ;low address byte matched with ji_ret + nop + trap ;jmp indirect page cross bug + endif + + 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_65C02_functional_tests/as65_142.zip b/6502_65C02_functional_tests/as65_142.zip new file mode 100644 index 0000000000000000000000000000000000000000..5bf94922a08bc4ea8739d91ddb98b6fce3cbf434 GIT binary patch literal 110316 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

QF}4d?G4h18 zuc<>{8&-RhMy6ZT*aX=+;!HyFHc*uAN)|wX<+fYGZF@)a5sCPFGU~tJmvoS~9P+PB z<3H{>k+rCkc#rn5Uty&G`Rsh*;sYiz64)rwGM_=I(Li^dwM&^zC zmqTg7X*l0zn2LpJ@XVNQE0(M(m&L-L=THXQ#Nx5nbk)%W`bM3yvCfEXO;Op-Qn6vY zbvGghmd|`(E1Jb>4aLl9K5IDqN!riyk>U)vk2F=WR}_BoafsJr;*;S4HFwCXZd5U`lKa{*6G_y*b=r*2;6Hk zXuEIwkSjKsZPq%3v&9psDfZMHuYkOpI<;t}rQ;Otjqlmc0liT}q;`Dg$e(a}S+q|> zpHLdEu%>i{!X(@oHYMINI_S>CTm367y>MsY2&=%#&Hx0l`b3r8bpI?-!gtYthM&H{ zLw946Pp-cv4YeMMlP3PHTl0amzFlnim6sm~!f3scPp?Ult!Ut zl00?;jMDTB17EQrs$!Eko7*~jZy13y=O1$3a89r~Qz{98G!>qf_kmrl=c>d<_E^s? zRhFgfZ(wP2+VP0mVceZGOQWiT+fH?*X5^+;=Qe%zx9OnCEqodjKCUA$$bLxi{OJ8{+ zrkOlYw4)h@_djwP;hVeUJj0W=c2e#P4|g8z0KAf|D&gF(!okkFx^3R!;=Q=ABE9dP zh!!+)(CYpH+~Wv4Pg8QJ(7BpntR%hPzdwc{fqw2Ua3fZZrnrvreY)6itJk`Vx?9x= z-~C+3P$;_You~*7_Y-KIk}Bj_KzCtcUqkX|p^L--UafYPCVx|+*v&L0xY?8#hschM zGl4D|3K#9)&}?(0==dGqsmIkax+`ttE+Ci|3AToBqg49Yg7J*kpF|^zU8hP{x!wjT z$gE+N#W(laPP8lU9*&Z3EoTAgoULQ>h9XzB1wm8hj z)^#~Bo~?~gbffxp`ar1_4`F#Bff7Vs7~-bxxZCX=I`&%JLHlB1n3z@vInJ*Jc6 zFdFNeGLaed5m~n>>UD@2>qvYT8wi(H(|NiL$7G$eZl4jedt@KZAXX1znLzZ?eHa}j zn=yIMhHoHd`)k->!q!GY=4+Hp(T<9}xTmXXsJQ#Fwq_$vfOw-fCGKTgOzC(KLpToW z2lCctfx~WHWz3i znpGndxna%$YY_KS(KSK1enXdDg1za+gB!pF-cSq&yV1bUi45ZBZRo6sI4u+pLu|!A zbSwhz=cuO~*$Fd1^;yVj2b%e~O=%jPBT^SCF&}@?c@W=Y3$|~`{?HenZ5F)dEIE?S zqq6_vSA_FckaL_k!+0TsIdOI};>6IAy%-;LxMAa?ei7NK6NwlTsVK1S z@~{{}jjX!jI*tWkuoQxDV^}z=#vosv0}&nKLzHf+32Kix*G28Y+JJU>u>GCf-pGxB zc9AoqS~EY!hHxB|=Cn(>C!|c5==g*V(kd6BKFjvR=egEL*WnE0_OA(3OIQZ@t^1es zshsEX4Dg@FdW?sM&MK~3^9`RS{|pDR2dTsQZ;8vBVsVM~A|0MSrCU0|N*P7>D<>YM zMJyvuN4i!g2tvK}AMBEqD{&$cRed@#bJQsKKQk~F;<@hI?7#ZXe5Lvh*nz6ebHCED z+29DT7H}{x#mp9J@RFT&%<0PnS|^O6b;5L=ni_MU7bka1r#6g1HpcpH`-++bk!Vs) zPrritr8Wai{^YLe_mwqXc9#iN{BICL>cYJb$Sb2xLHEXbXOJDFRaIKFpJu~=o{!a7 zZ`TpLQ*|PhYte&YM7!JWU^6kdL0w#PDFVOu583an1pMp^KOeCktDA=#)?8;stE!tr z7csCa6+}lY)?$t;dCteJnMY_i$@~Hz>kVxFH17yz@P{v1lNWh2kI1>M=ZWyQhw&X{B~l|@h6B-cTxRhtW)L@x~;{yZK%{@ zo;W4bwLX#S!&u8f3>kCmbengm_`pKMz;Z;>eYRyN#4v{f?bm(4ka$6(Ab~sXk`5<# zt)Bq7YrxWi-t^Beyk1S_$8<#!Qfz~Ulmb0(Y~Yhmt^sD~TK9%L=VLkVW65&l?1z}$ z*>}Xu*Tsf&5>O4&*)?fwhYPC=p!kPt@T%%B+-=>oYlAY4e3^Z*;yw+V(sBvcLTf!p z3gZz>j#<@fn3*g`M)5iCTdyI4fMxB}g-Mwo(*;3bmUmGjKMU~z7wIjrp$V5-P^#XR zs5yJM=5wr7U{>NaN?W;0yM1$18LqFXmhMUH=Gs-rdxhZT&b#2pKKF#2vPoS7JQy(* zW9_CD8Tz8ZLDAs_SJL6uP%Hngi~Ad#{Y2L2hkAKV2=3gb6DNpyLuXT`Uh~1(4{E;l zvbll?_iUVg%RS;;9D>mfFoLl4I$upPNBM{{Ei447V&GiAiU}6il@P=IN7PW~d|_(} zfyQb&r`P;bT@v2I3@P5zu<4WfC@y4OBXL9O1%}0h$?m^jpjA3OifH%Z^MThs{sqhV zO&9I3;&;OmX|Q}n>Fzbk_kAFKz5*`^mGU~04ojBvyQgdEh@p9Tbpaj)o$9|sv^5m( z243>0Wkqa|BvnK1(4VPlM63dDl{3DBp!~ zsY_7rgov&|U!#GBIpjkQ?mGk4Uwr2~K}0PsMB0Nt|2P@rE&-QShB{2SwvvlGO zU!(l5q3T>Z&Zy=`cd+#yAfj*5hwd1{0}UPe)r-CHj&<5JzVF;iN2A4ti1*lqPv^S9 z604_;YV3j`(H^4no%TrHXBy~etm@CM73xFR3W*ylAvztn=JF6HZlrx=zuPE#t*tp4 zWn{mz$~Jm`fgeJF_PsczQR(HSKv#i8tQr%Q4fSub*#Upn&I~=vUB|sM_2}<@<_tR^ z`1>>d!gRc_L*4rJ(nQ8+ z3SBzr`UZh8bTN))a-KZ~f3(BqwS?jaX^oFLjQfC(M9pxY>G7;B;U zq_pBaSK&gio4#!k?Ro4{`QC(gBQ&2U{$tz^EMJSqtemD;94$Y<>-Fg_1Wg>$(fw5q zrMa)i39O6=tj*Jqfc0(nG+Gv6nO3cGnj+}_1AqAA-TV~0b8RdpJ^V3FcC;mTijL`Q zWreO>=D52B+y)rQ;zwXh{ly=DYd6LBSfRFtg|Psheu8%Xh1&k^5#DB@!}?;m0w zSKSXd+-%s6U5x3|%`+_Dp}fU4TDt#v2o5Fe%M1I>@7Li_1@7RYW6!CO`!ILeu= zpUKV#>Dz0W3cI zPyv3dlYB3n3;I5vjdkJbUhZS((B9%T^R2(I`(@}>n$8gO@aiaAXBaP#pyfMT zBkA4lx!);5QA_Rz&f&#H0sF54@D~Al%K(O=UP1BJbG~-K`w`UfLxGx>-V|$sKUU!Q zxKlO0b%1uj`y$wK=5Y1_KB;YgGi6Kn>abgO!bHbg)Y>F_SrwE^P{iHsD2+A>%9Nx3 zI@hiIs*Z3)Y?@PS-68Ch8#C9Z;+5ejpvJc=U5XFUOTVQdntv3~o%1bV}n?X1cSXi;0VyQ4IBiKRb~=?p|IJ4%opD7uQu? zqagIVZxw_T8QS)ZUxpU3Q^Qnv6&C&#V;fa_Xs|s(czgW*Y|rh%$xnU9tUcAt_$|Z| z>aIN7Q-5Rn9n(udLE(2WTl4`#gL#efRxi8Ghac5Ln!WvvB6_5cYL21clisTN3ZKa* z#Z8T)UU$ywwl!Ca4ecQ`+OR7l#b579I*h}>re?=SY#6ev0=GGrsO=lZ{_!s+f0dgR zhxnA9$}~FNr&^Ktlc(=u%jRo!ryHma~zRd~yM!pB<*3+IDZoqzoi+S|gb1iENYdpi!(T7x)^ zWChl7f~tMLy>p`zV`tHezE0#D*JRTF`sU^yz&H^b2C8VdrKZ*o^s zc-~giLv%C?LSStb1s5;e3+8}&c6)`A7nb>9idz9!niX$fRH{b&qJuxt-NDfk@#niQ(L?D z3o2b*wBh&-m`Mpr1>AToo=*d$`v&Dy!Xzc$b?jZQmn?l-$87U*Tcg#|SB@k49Tpvb z0Hj^n??5rlq%6?wX3xE=bV%rPs0%!$WTDJu@!6U!XeEss;-9V`I7AuGz-Ko4OgK&# zXrPKY>Ugk_S<{!|9^Gg z@9n9snd+MJ^yzbYYNk&2oTu~ypY+jML+^o{?hPS2{>`iJvqq+67_1HcQ=s~S%#)36L#CN-C5nU)4tybhJWxmRjG`pIP1#o`l? zay-5ZWeMn5qA%Hd_ir+P6{q&a z$EjD*8sk13L6`K~4IeD64LrYxUFebq>cqwoW!EZ@vf59q^)Xnj8^y$FYay>bcjgc( z7NYA;rc6G`tNqf9W)}QswtPOc*5P7Jt~gTn4}+oaQQ^9rwu4zeCQg-Z3+(59TVw!= z6W#2W2DZj~cdn}hI+B}E^!jD^8pVx^_*PTsIoXk;iHG$p&^+1p4t+d>b)Q-aec8e{ zxWtY5iSQ=Y6S;wwfC=t^rc7+?ay*ktUq9~k=p;P;P{U*WLnv&fw}i>TvZm47yURrY zwt(bpAN=^|LCC>R2BE@??9ZwAgjX5?-i9vajt^%lcRekRlAN#u3JzQlv4bg5Vbv!a z>2tKM;A*7}Q;x}w*`d0enH;Z-Ushk|)08*Q&FyTx*f9GO9$>ad=Pu8>vt|U6a(?$d ztPw>2jZax~62nA9VDiT*n|;lFQ@g{gb}@V4{BC=l52@_3cw6FemuIyghp|C(n7#eQ zI|^g*(t(l1?v$patP7|8mwtH+n549(kxt}b=^h^~zh=dr(@JHz5)-BB+Zj!&OI4f`29 z!k*6INsI@a>Ry1aC@@)>J+cBdgn9j$j=6PMI47{S|= zRHw{V<$?F(ZBEb+(wyT5I@re_;|r3LZfwb?JsqS46DmkAY=6UI-l&8s2!FquS}yf? zUT|_$Do}&pXFmTNwH(Rm|NRyE;O>>o@=njdJLBq-&tYMa!Kn|vS~?CoLkezuxlg%F zq0up6?A${+baJ_rCK~j@`7L7UKX&Bvu`5R{Wp{O z!NT!rl#crmYH#)@h?ZPsXl>qPX&>lPVx?@WbLV_H&3j72Q8?H0`_Edx!$8DAE^I&G zdffXc|KkcKzD7)@T)d2^!ib{(#h!f4vTf&~o}I9sS%}Mi+{F{q^Zna#e2mvokj)fV zWd!G}PB7v&#e-vfsZ)6csu(zfZG}tz>ZO|d1IKXsSAI!9QvJdlzSWlSYOO6tfqglC z++qLzxZPXw)yN6sHZrV+Ewxk6Z6l-F9c&EuZ73`<>lAr{^jsC2)pafg>Sa_}zC#uXrZFDc4A*Pbn*`lKt z``V{6DVD|aWqI8Io^8YMmxpLH6(;9xhq_bhF<7j>7>3^GhN*uDifvJu%Cw%sW_|8X zecl@dAYfg~qCeBJt_nz#By`gsW+m=yJoI`h($-{6 zA~rF}osR!XMKmuW4dkcng?43L*@N*Tgs^>jHCOSQ-nFJs8?rAXJuR=hQ$zp^#!;s}X2c$%#uSI(|soI@_@?7fpq@Q(3Fe#?8M$segv-00D z^G{#?{5_ODQ{OtY)uu!0cLV$hRYCCW5}FmvZPJ^Ee4b_I^S*=&;f(*Vqs~_$QWW>J z7R+|}9^X@){mk94+JVVYWfo)KJ(`^sKh0P>__i+91~S8en%~TeJaqCTY0W!td&|T6I5J) zkhIfHMSy)^{qFtr!(XXZgO9XZQNO*{*zFV7ypeeyB^(}&iu|s138x=e+VF)(5Cs}a z%zj?TJ;9z)<8@ZBU3z-R-^bz}?$XuKnZRi|2c>-aZ0K@`^e{`{^=&k|0eb^?+sCvt zO6)>FA!5;vNol_&D33ol=b8ZdQ2d~UW7K>5`HFpiwomBlP)A-(^wUL*PY)Q7g~mvq zy3f_Vj~~Y->h9p-((5>?i6Oz{%dG1^UXGW(L3eI_dWhCQPkh}R=3u3sP9GI_A>He! zBVkfpZCmaq=n{I-FVoO75hN3!=6fv8QNnUiIp2u+WUBevhC9T+g0W(!EuWa$B1pr# zO#+D`4yb;tIsK#|=r~cvWY}zAdj}KgvO_g?dLuArk4@t7Y-}Q^zK!vh#dy|e=nHI* zt`JKOPvXyn)8$)jZt{x~bM4MW2tF{!sdM1v?+&@?5GoFB7zj%>?^4HQW(x zBRiE3qPmEie0wJ(*xA2%=v|wV#gViNTRrXJ9U?|C%B+JvPa> zhs4-TAERRMx-_~$A}i+EGETux+-XL~(C}<>*$pi-f|ZACS^6DBK1N{-zxl{|sr@>3 zEFs)E>tE@O|4?r+`EsvA)5}xIp8!L zU6CJ;J|p`-b21$GhDh;um`Qkn*6#Nf33-h9P!aXQKTBQ5;`ew3v6U?auz1ebsR3`&&rC;mbzr-MH^sZ=^6ZtnlU~hS)^&UEAHWwt>N>Soq1EF zu-xSgQ^9uUwcrjywIeRq`J8J9;o=@F6FWQdZDM6M3xN^nWSzA9`J2RrU$k7VZ4%Y7 zKA#<%J7`b@)>ezN4|2ypc|`VA-$Gk~c^> zoHV|URUW@ce#Jm}VcI4SZ5ZcJY)uoIq&hzKps-KSU~IMI+Bun7(eA*uazrZB)apjs zUN|N5$)#ZKb{WqJDqC{ba_($5*1DV%M zUx`(QS5&`;ERaT23s=uDMl>-lH*aY%_3f@}#qazPW>fXW4zYjxe#yGuq_7j+wbqt! z#Wbe2imu$2aU2+la-5@KYJ`jU}=Td4`P;$$>l7f<&LnmFV197r3HGEef~ory*`eU{NER7BL(BLr zn~ps982Ns=WG_$Ovc9z8<-Esd79MGTWq5OM@4gyc1NdzCP^dUTcm&Mz%--C>(vo}8 z$c6h;6<@6wjph&m1%mT~HU04{4*nSf>5heu{s#N8_&K@UyG;XggQqV=04l0P3AbE< zT5hmt#t=Ug^D&5?;m;dMJRZOv;ta$9qv-LsM1M3On?Mrage`?e;xWP9i$)DP12= zn)D|%@9n)Ws%i1@f@jo6J?n<$!A1`-7G)1!NMHrIVlIO=3=)Wr*EnWr9v%)@A3+M5`+@Y8o#qeqcHiU zdK-ChRiU6%MED~r`h4PYi*YrWmGvf8|L&UjZ6b}|e13O2@w!-OFEz&a=eJ z5DTP%Qh`*oRP;sM7igxKbyzR>VF4(DZaIJqb_WmxvINK6um^xHNS3gUaAgd|_jFr* zGa&|(5pZko{Wazn6~uS+!Os`#=d*#(9v~Y1IWEcA4X;3MfwANsRdEN_%1^6gmp_8) zqFYqO8W_z)D*+IVF6Q7mwYWo)*Z!_=l-8#;xip?eP!-qTg^PXZV*Vc7#Y~tx z3e)llJ;R^4zo05c)H6Ndx!CkyZ5Dg?h*Oa=h6ZRs`#`lxKZCY7yc@lp_tH|3>)Tl< z?LAH%KaS;X<~WWUu)+Z80h+5;&|o=7Wxzwg?HSKuKOs5}_FD{?@xeXt1I6v@X3T>N z!x6%MArP}5wE|VDbYr{@CE1g5!t&c&#u#&$qdzAOyN-!%z(VfBfgRTj3GK4U_p8)} zgx%B+W68o9fA?JyBV&%aI$v`u#MAUHBPqu3aU#nBU+X9job~a2E=cYpSE5z=>vHDs zN9-}3^eRO9^w$;np~OOpm_NX8al^ypV0Lma;=6XVpT!PoDPKxXXv)K>mb;0w7;wJ9 zSE&lmRQ+%Kg-zp#8tpnddOCtx+h3FgK(oo)9tM)n(OiZ(iwtv9yI??GT{smTFH#pJ zXS~1&vnQvm6}F^R$c2tE5}cZ}q5tmJ>fPU^8qvb5EkAqIi5sm)ahse_bxhNFc}+96 za+!M@!tLUQum&rL3nIq_n*~~#W#1oRBh~MwyD()BmHn0o-0;g}eu}MwIPpeml;|+G zV}Ec8;W<_H2)j@MqnQF5&QV`~IxJb*VuW_}U-z9fT;tyRo7g!e>a8xC9G_{B!K8rp zT5CoWNm+KDewWqhqnvyNLAHnGeDsrCI3r6v!b}H3jg{mJTskilv?rK3C(%7|f^wz6ULN=DtdjdyUQG@z>5(;9q+8H3P*;pM+9B zVnONbSof5;kMwFd+;CQ%ue#1JZPzD|-AGHlkUil?G!Mvgl;79K=0Zf&eqfW!DbgH4 zPb_{})m-DAK${h~mh&D+uDyF2MG3#Q9a(nLACJGOdtR^n}fhKgmt#d`w(`EH<$hfmm z_j_LVdkYc7GW6&*aBF!?3u(iU;A%X4wPk&md4jZX5B;RwmKxtSW%xc=ZV>ju|1FjJ zilo#r)>L39=l#T3ydYBXX4P4g7+C3NX{Y$Z0^9~rEWP`JyP^-VLYKX8bt1myJbC~p zM(TAPX%H*l;heME<*Hb4mMSrzontrh)U5dB>%6_D{OMlqNw_K%Onjn?zEWbu1?ohH zEqEQHeRKuxG2PH$=x+2VUhG|&6@N-#GH-S4$e&tXKYc%ZTfF_=RN$w=oQeBUFQzdq z#uP}`^Z6ouq9^QFBiqGcIdRR)=r<#WRb%Bi)+4$%#h>m?XA83L-Iem!ufSL3J>yFq zZ zAHsz2XIeog@^e|~VO(K@jDe2h2}7nS1=n}_w);;y<5vN{k`)Yy5rj{kD+FIr9)<@z zfIS3mCH5Z$)dbMNtfW_Ng>z*?3G)e8gkq9F$4@tOLC0;LYM@RG7`mF@gRw546Z47) zIBLFQ1QgPKdE-irt)eMPLZ|@UJXPR1mK!Ea50G6CD8XBcc%eelbYnp1 zj9y@Di_Wdeh9lZ&47jtxLfgja+_X_ZYi0TGyO<1T1~u3rqmh->gu(t~ z?>(kDI&V^Y&|GV>8q-D2=mG>KLKezBBn#v9`@UU|V)(lH4KfZ-jBTCl=XV@qRXDk= zXBk`2#^v?Ez58j4>aZ6jE06oU>mGw8hsyiFl6!>1Ah6wJvz9XiNs$K~(G)4xvb$$Q!49Hs!yOBFgM7yMP3mw?C&Vm2DnPd(K zDS%TsTc^)1BPY9q(H7_Q!4ov{T<;ha^Q1?9RSYqO(mGIhy(*(a$cAK%W1oC{O`eEf zo>zj{_-=(UHGF$=A_B|>B5hq-fTH2|V#F2xdGaR8 z^%m)$7Ux2FlRMjy%pKolgYY+!9_mq)QMJAwn+_|fC@P_c?2!>;aV!>zM zA4hfO?9M|shrwqiwAx#@nx6%a$Ud+vk7!X#!$o2r=b@deV`qHdkLdF8OPUt(1@yGulE;=J6w*uQjK|6#s>67}fci*u2CM*;s8ZZn&`We9kSuy~nkcLL^8#y; zg@UAy^J+^FF)p1%BhGfDgDboOPxmbqve6(7uHZS z()z}5nqI*ZsIaG*nZMop0_J6-$hmZr7qgGjG%_*`-n&}L8$(}dm?>41Br{QWEAG67 z8qH3`x!Z3W-)?Z9zqge*vzgB?ORf`19_2m52M-fIHl{$>ahB)vKx*tdnIBSH3O7hxECm@@Aj~(Vt63&kf6GiWb|f0A$OP_Ld?ZtC z*3#HM^+L6ZJF$-no7dUYtg(Tfy{Iozr$MAkS>1FquCA`!!%lfA{xfy` zB@lXc`XjpdL<3J$xRc$du+bOM@Ey#`VKz z_bfHjy7ovy;ZNKOi%jpZx{W*1K50hs@q?dX@z*u%G@*boGpai9j{1B)fr(nbYjN_h87p1R6LK|4We8PBp z&oE9&XL>)#qH1lKt)D@=eHqBKWDBwk^GiaHkGeF^?Z-Od1x^7Mj5+L(O8-`6;P#h4 z;T%oT{U!X%?5)_9?mFU;5W#Vqc9bo9d(17)=yv!b7h=|yrU3{l_EHxKQLlg@ zCx9v>6F38fV4|K+X+;V}epp~Kr#oLSHZ_qDPCgS1&aVFPs*CDjDC^(oS5z=k=nVrT zjY;D(+rS+`sC-`@@yRv;WT*tqfjEjcYT`8f)*#G?CG2;PQO{$AaW`#v-_j>VYa&Ay z^=aXe!CImC_SU^Uqp1p2x|$~t$%$IkdR9)lpbPY7{?~!5aXF2Qgg==XO;sxen&K;v zQ6sXf+dm^nX;85ju zA?0Sx<~^`=v5;o7YV%WSLhzBPwMcmo<1>ERL)VSGVfa+9BD-DVpr4g=t@%`YwYFYvoK@j5y@>se!Qm!I+q zfh0;Nfc+C4HMPrvBZ%QEGo<>S3b z(9_3r5lRGWc3ZTnmSae|h3lDF;_2}iDck7HfV9JL^+fpjNwgp1L;)OteYe*)1IRSm zF{zlFJP`Sjg?{1$OEQzC!k-Xk5l+(}yj@E=!eO8Pk%m2QJ`(6OMgW}M?VMKSLt|WR zcLLLq74_e&|JvDq8FCp%j;>>Q=ZqWTOZQp<+W;wf<|ZTD<6qe0$jl_cEt zL{WC5inXnmicVcL;0-HD($;y+ZgxJ)cxir1Z9!^6%lWt!Dx}!*fXx!xD!kJ>{}PRQ zh7MCsR;|Yq5B3xiHJ#5EZdK=GVQpq+4GA!=R}gy>j-C)TEBZ=@<`_SY@SRDR6StU9 zoAE#u+Hi`)$c9Kpnv;lnhMBo}BLN%NdL$FyH3y#wo9vrZDJk{#P zZLfOS(LjJT+K_@~ErD6vm@(qeQ<#A%9d8@)TDhX??RS^d@IKf61x-AZg{}$xB>d8# z5z_J6tlGS0#^I3?d=`&B(7+e*vLiu_!inMUnCn19cWz7Q>hK5xD8g1y#m%eB8UNfOS1+7 z5{RXe-s{VW^CXh46P9^=Ze?Un^0MIy0_tmaKg;+ z2L_G8O&i{p#@>dr$_s+}K0C@P)JJNQY!wl*_tpPohnQ(}#;fD;Vz3=X#d`$V#ELxK zND7djNRLkHHD$=wEb?bE9wXd2Dy~i7R{Z7m94cfV5A2OJ!)3$1XWdNoPvrhl9VZZ` zoE$X>#9I>pAq7RKPcc>zT=DSCZ^-@p(QQ-W)tyV8-uaT)OR;vYk=#sqIkxq!lXWtY zSM2e+11;_s3AmF~BN=%@ueNk1yXhQ6d@4$c8D$P5nrRD@m_)5ddWF}?9@J`S zYqo}HPh`MtCPgS6-K{k?O%3h3=j<>s`7Fc91X*~()|3Y3!cQC4QtWKC`gL1f__iF- z91V;z^sH;>4Ha8AuiWD@nfy4kONZX7!E~!VfF}x#)8>c+2=lM`%GHH(EtW zOEvYxl^#!E?u$ORw`Jp)t)eEBPK4)ViV!Gx$nB!MN&D{2_#IMKLpu5)}W zK#f5{oQ#tkE(T-}y5TFbC}P4MLd+p*z*j(4ROt*keEmAI5w5&3B=($l6hk`Q!pstw zz=7Xmp_|eW-%3FOF<0M8lnrD*`${kNXQB{3@+q7(ODvj{Snb)OOlxy~IA_OZW)9tQk4z#A>Z=CYMVO|m>^NCS7kSeK z1nP_gEOA@esF~uZO!zFH8-4v$OxCwHa50zOPpfEM1ZgVeKpxp zj466^fr=VZ0Y6TOQt-%+Hy{@g|NJW+cWI@?rHCTEGQ)KvpDOV;}T!a*vm@Fi}G4EFV8RHh49# z#HXZov8~ZTl$_LsuCbFZs_jcEbY*qExM4{dRibfD)fJN$XZc6WEz13vhipDz=a6_ zcmN@akPHAosG1-4*OiBz^-E_v0WUuGTM()e@l9!B+0ZWzo?+3)c;Ge=HHT@!qWe?+#JesB_>VA_ACH^>Gl^5 z+`r9FPYe7*3g>L?^7nvo{$;iQueMm39|sau7Eos9Mh5_Vlmh=0X4I)e1~p(;Cs$uP z4*^?O=YRKYhH`zL&jOp literal 0 HcmV?d00001 diff --git a/6502_65C02_functional_tests/license.txt b/6502_65C02_functional_tests/license.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/6502_65C02_functional_tests/license.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/6502_65C02_functional_tests/readme.txt b/6502_65C02_functional_tests/readme.txt new file mode 100644 index 0000000..12aacac --- /dev/null +++ b/6502_65C02_functional_tests/readme.txt @@ -0,0 +1,29 @@ +This is a set of functional tests for the 6502/65C02 type processors. + +The 6502_functional_test.a65 is an assembler sourcecode to test all valid +opcodes and addressing modes of the original NMOS 6502 cpu. + +The 65C02_extended_opcodes_test.a65c tests all additional opcodes of the +65C02 processor including undefined opcodes. + +The 6502_interrupt_test.a65 is a simple test to check the interrupt system +of both processors. A feedback register is required to inject IRQ and NMI +requests. + +The 6502_decimal_test.a65 is Bruce Clark's code to accurately test decimal mode +of the various 6502 cores (6502, 65c02 & 65816 in 8-bit mode) with added +configuration options (invalid bcd or not, which flags to ignore). + +Detailed information about how to configure, assemble and run the tests is +included in each source file. + +The assembler used is no longer available on the author's website. as65_142.zip +is now included in this repository. + +And no, I will not switch to another assembler. However, GitHub user amb5l has +a CA65 compatible version in his repository. + +Good luck debugging your emulator, simulator, fpga core, discrete +logic implementation or whatever you have! + + diff --git a/6502_65C02_functional_tests/report.i65 b/6502_65C02_functional_tests/report.i65 new file mode 100644 index 0000000..cefc1c4 --- /dev/null +++ b/6502_65C02_functional_tests/report.i65 @@ -0,0 +1,224 @@ +;**** report 6502 funtional test errors to standard I/O **** +; +;this include file is part of the 6502 functional tests +;it is used when you configure report = 1 in the tests +; +;to adopt the standard output vectors of your test environment +;you must modify the rchar and rget subroutines in this include +; +;I/O hardware may have to be initialized in report_init + +;print message macro - \1 = message location +rprt macro + ldx #0 + lda \1 +loop\? + jsr rchar + inx + lda \1,x + bne loop\? + endm + +;initialize I/O as required (example: configure & enable ACIA) +report_init + ;nothing to initialize + rprt rmsg_start + rts + +;show stack (with saved registers), zeropage and absolute memory workspace +;after an error was trapped in the test program +report_error +;save registers + php + pha + txa + pha + tya + pha + cld +;show stack with index to registers at error + rprt rmsg_stack + tsx + inx + lda #1 ;address high + jsr rhex + txa ;address low + jsr rhex +rstack jsr rspace + lda $100,x ;stack data + jsr rhex + inx + bne rstack + jsr rcrlf ;new line + jmp rend ; Skip BS +;show zero page workspace + lda #0 + jsr rhex + lda #zpt + tax + jsr rhex +rzp jsr rspace + lda 0,x + jsr rhex + inx + cpx #zp_bss + bne rzp + jsr rcrlf +;show absolute workspace + lda #hi(data_segment) + jsr rhex + lda #lo(data_segment) + jsr rhex + ldx #0 +rabs jsr rspace + lda data_segment,x + jsr rhex + inx + cpx #(data_bss-data_segment) + bne rabs +;ask to continue +rend + rprt rmsg_cont +rerr1 jsr rget + cmp #'S' + beq rskip + cmp #'C' + bne rerr1 +;restore registers + pla + tay + pla + tax + pla + plp + rts +;skip the current test +rskip lda #$f0 ;already end of tests? + cmp test_case + beq rerr1 ;skip is not available + ldx #$ff ;clear stack + txs + inc test_case ;next test + lda #lo(start) ;find begin of test + sta zpt + lda #hi(start) + sta zpt+1 +rskipl1 ldy #4 ;search pattern +rskipl2 lda (zpt),y ;next byte + cmp rmark,y + bne rskipnx ;no match + dey + bmi rskipf ;found pattern + cpy #1 ;skip immediate value + bne rskipl2 + dey + beq rskipl2 + +rskipnx inc zpt ;next RAM location + bne rskipl1 + inc zpt+1 + bne rskipl1 + +rskipf ldy #1 ;pattern found - check test number + lda (zpt),y ;test number + cmp #$f0 ;end of last test? + beq rskipe ;ask to rerun all + cmp test_case ;is next test? + bne rskipnx ;continue searching +rskipe jmp (zpt) ;start next test or rerun at end of tests + +rmark lda #0 ;begin of test search pattern + sta test_case + +;show test has ended, ask to repeat +report_success + if rep_int = 1 + rprt rmsg_priority + lda data_segment ;show interrupt sequence + jsr rhex + jsr rspace + lda data_segment+1 + jsr rhex + jsr rspace + lda data_segment+2 + jsr rhex + endif + rprt rmsg_success +rsuc1 jsr rget + cmp #'R' + bne rsuc1 + rts + +;input subroutine +;get a character from standard input +;adjust according to the needs in your test environment +rget ;get character in A +;rget1 +; lda $bff1 ;wait RDRF +; and #8 +; beq rget1 +;not a real ACIA - so RDRF is not checked +; lda $bff0 ;read acia rx reg +; lda $f004 ;Kowalski simulator default + lda $dc01 ;KILIAN +;the load can be replaced by a call to a kernal routine +; jsr $ffcf ;example: CHRIN for a C64 + cmp #'a' ;lower case + bcc rget1 + and #$5f ;convert to upper case +rget1 rts + +;output subroutines +rcrlf lda #10 + jsr rchar + lda #13 + bne rchar + +rspace lda #' ' + bne rchar + +rhex pha ;report hex byte in A + lsr a ;high nibble first + lsr a + lsr a + lsr a + jsr rnib + pla ;now low nibble + and #$f + +rnib clc ;report nibble in A + adc #'0' ;make printable 0-9 + cmp #'9'+1 + bcc rchar + adc #6 ;make printable A-F + +;send a character to standard output +;adjust according to the needs in your test environment +;register X needs to be preserved! +rchar ;report character in A +; pha ;wait TDRF +;rchar1 lda $bff1 +; and #$10 +; beq rchar1 +; pla +;not a real ACIA - so TDRF is not checked +; sta $bff0 ;write acia tx reg +; sta $f001 ;Kowalski simulator default + sta $dc00 ;KILIAN +;the store can be replaced by a call to a kernal routine +; jsr $ffd2 ;example: CHROUT for a C64 + rts + +rmsg_start + db 10,13,"Started testing",10,13,0 +rmsg_stack + db 10,13,"regs Y X A PS PCLPCH",10,13,0 +rmsg_cont + db 10,13,"press C to continue or S to skip current test",10,13,0 +rmsg_success + db 10,13,"All tests completed, press R to repeat",10,13,0 + if rep_int = 1 +rmsg_priority + db 10,13,"interrupt sequence (NMI IRQ BRK) ",0 + endif + \ No newline at end of file diff --git a/CMakelists.txt b/CMakelists.txt new file mode 100644 index 0000000..b539f88 --- /dev/null +++ b/CMakelists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.11) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_PROJECT_NAME 6502PC) + +project(${CMAKE_PROJECT_NAME}) + +enable_language(C) + +add_executable(${CMAKE_PROJECT_NAME}) + +target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic) + +target_sources(${CMAKE_PROJECT_NAME} PRIVATE + "src/main.c" + "src/ram.c" + src/cpu.c + src/rom.c + src/io.c + src/memoryMap.c +) + +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE + "inc" +) \ No newline at end of file diff --git a/inc/cpu.h b/inc/cpu.h new file mode 100644 index 0000000..06f877b --- /dev/null +++ b/inc/cpu.h @@ -0,0 +1,34 @@ +#ifndef CPU_H +#define CPU_H + +#include +#include + +typedef struct { + uint16_t ProgrammeCounter; + uint8_t StackPointer; + + uint8_t Accumulator; + uint8_t X; + uint8_t Y; + + union { + uint8_t FlagsByte; + struct { + uint8_t CarryFlag : 1; + uint8_t ZeroFlag : 1; + uint8_t InterruptDisable : 1; + uint8_t DecimalMode : 1; + uint8_t BreakCommand : 1; + uint8_t UNUSED : 1; + uint8_t OverflowFlag : 1; + uint8_t NegativeFlag : 1; + } Flags; + }; +} CPU; + +void initCPU(CPU *cpu); +void resetCPU(CPU *cpu, uint8_t *rom); +useconds_t runCycle(CPU *cpu, uint8_t *ram, uint8_t *rom); + +#endif // CPU_H \ No newline at end of file diff --git a/inc/io.h b/inc/io.h new file mode 100644 index 0000000..95cca3f --- /dev/null +++ b/inc/io.h @@ -0,0 +1,18 @@ +#ifndef IO_H +#define IO_H + +#include "ram.h" + +#include + +#define IO_SIZE (1 * 1024) // 1KiB + +#define IO_BASE (RAM_SIZE) // 55KiB RAM + +#define SCREEN_ADDRESS 0 +#define KEYBOARD_ADDRESS 1 + +int putChar(uint8_t c); +int getChar(void); + +#endif // IO_H \ No newline at end of file diff --git a/inc/memoryMap.h b/inc/memoryMap.h new file mode 100644 index 0000000..ad324bc --- /dev/null +++ b/inc/memoryMap.h @@ -0,0 +1,16 @@ +#ifndef MEMORY_MAP_H +#define MEMORY_MAP_H + +#include "io.h" +#include "ram.h" +#include "rom.h" + +#include + +uint8_t readMemory(uint16_t address, uint8_t *ram, uint8_t *rom); +void writeMemory(uint16_t address, uint8_t value, uint8_t *ram, uint8_t *rom); + +uint8_t readStack(uint8_t address, uint8_t *ram); +void writeStack(uint8_t address, uint8_t value, uint8_t *ram); + +#endif // MEMORY_MAP_H \ No newline at end of file diff --git a/inc/opcodes.h b/inc/opcodes.h new file mode 100644 index 0000000..f159d40 --- /dev/null +++ b/inc/opcodes.h @@ -0,0 +1,173 @@ +#ifndef OPCODES_H +#define OPCODES_H + +/* 0 */ +#define BRK 0x00 // Break; +#define ORA_XZi 0x01 // OR memory with accumulator, X-indexed zero page indirect +#define ORA_Z 0x05 // OR memory with accumulator, zero page +#define ASL_Z 0x06 // Arithmetic shift left, zero page +#define PHP 0x08 // Push processor status on stack +#define ORA_I 0x09 // OR memory with accumulator, immediate +#define ASL 0x0a // Arithmetic shift left, accumulator +#define ORA_A 0x0d // OR memory with accumulator, absolute +#define ASL_A 0x0e // Arithmetic shift left, absolute +/* 9 */ +#define BPL 0x10 // Branch on result plus +#define ORA_ZiY 0x11 // OR memory with accumulator, zero page indirect Y-indexed +#define ORA_XZ 0x15 // OR memory with accumulator, X-indexed zero page +#define ASL_XZ 0x16 // Arithmetic shift left, X-indexed zero page +#define CLC 0x18 // Clear carry flag +#define ORA_YA 0x19 // OR memory with accumulator, Y-indexed absolute +#define ORA_XA 0x1d // OR memory with accumulator, X-indexed absolute +#define ASL_XA 0x1e // Arithmetic shift left, X-indexed absolute +/* 17 */ +#define JSR 0x20 // Jump to subroutine +#define AND_XZi 0x21 // AND memory with accumulator, X-indexed zero page indirect +#define BIT_Z 0x24 // Test bist in memory with accumulator, zero page +#define AND_Z 0x25 // AND memory with accumulator, zero page +#define ROL_Z 0x26 // Rotate left, zero page +#define PLP 0x28 // Pull processor status from stack +#define AND_I 0x29 // AND memory with accumulator, immediate +#define ROL 0x2a // Rotate left, accumulator +#define BIT_A 0x2c // Test bist in memory with accumulator, absolute +#define AND_A 0x2d // AND memory with accumulator, absolute +#define ROL_A 0x2e // Rotate left, absolute +/* 28 */ +#define BMI 0x30 // Branch on result minus +#define AND_ZiY 0x31 // AND memory with accumulator, zero page indirect Y-indexed +#define AND_XZ 0x35 // AND memory with accumulator, X-indexed zero page +#define ROL_XZ 0x36 // Rotate left, X-indexed zero page +#define SEC 0x38 // Set carry flag +#define AND_YA 0x39 // AND memory with accumulator, Y-indexed absolute +#define AND_XA 0x3d // AND memory with accumulator, X-indexed absolute +#define ROL_XA 0x3e // Rotate left, X-indexed absolute +/* 36 */ +#define RTI 0x40 // Return from interrupt +#define EOR_XZi 0x41 // XOR with accumulator, X-indexed zero page indirect +#define EOR_Z 0x45 // XOR with accumulator, zero page +#define LSR_Z 0x46 // Logical shift right, zero page +#define PHA 0x48 // Push accumulator on stack +#define EOR_I 0x49 // XOR with accumulator, immediate +#define LSR 0x4a // Logical shift right, accumulator +#define JMP_A 0x4c // Jump indirect, absolute +#define EOR_A 0x4d // XOR with accumulator, absolute +#define LSR_A 0x4e // Logical shift right, absolute +/* 46 */ +#define BVC 0x50 // Branch on overflow clear +#define EOR_ZiY 0x51 // XOR with accumulator, zero page indirect Y-indexed +#define EOR_XZ 0x55 // XOR with accumulator, X-indexed zero page +#define LSR_XZ 0x56 // Logical shift right, X-indexed zero page +#define CLI 0x58 // Clear interrupt disable +#define EOR_YA 0x59 // XOR with accumulator, Y-indexed absolute +#define EOR_XA 0x5d // XOR with accumulator, X-indexed absolute +#define LSR_XA 0x5e // Logical shift right, X-indexed absolute +/* 54 */ +#define RTS 0x60 // Return from subroutine +#define ADC_XZi 0x61 // Add memory to accumulator with carry, X-indexed zero page indirect +#define ADC_Z 0x65 // Add memory to accumulator with carry, zero page +#define ROR_Z 0x66 // Rotate right, zero page +#define PLA 0x68 // Pull accumulator from stack +#define ADC_I 0x69 // Add memory to accumulator with carry, immediate +#define ROR 0x6a // Rotate right, accumulator +#define JMP_Ai 0x6c // Jump indirect, absolute indirect +#define ADC_A 0x6d // Add memory to accumulator with carry, absolute +#define ROR_A 0x6e // Rotate right, absolute +/* 64 */ +#define BVS 0x70 // Branch on overflow set +#define ADC_ZiY 0x71 // Add memory to accumulator with carry, zero page indirect Y-indexed +#define ADC_XZ 0x75 // Add memory to accumulator with carry, X-indexed zero page +#define ROR_XZ 0x76 // Rotate right, X-indexed zero page +#define SEI 0x78 // Set Interrupt Disable +#define ADC_YA 0x79 // Add memory to accumulator with carry, Y-indexed absolute +#define ADC_XA 0x7d // Add memory to accumulator with carry, X-indexed absolute +#define ROR_XA 0x7e // Rotate right, X-indexed absolute +/* 72 */ +#define STA_XZi 0x81 // Store accumulator in memory, X-indexed zero page indirect +#define STY_Z 0x84 // Store Y in memory, zero page +#define STA_Z 0x85 // Store accumulator in memory, zero page +#define STX_Z 0x86 // Store X in memory, zero page +#define DEY 0x88 // Decrement Y by one +#define TXA 0x8a // Transfer X to accumulator +#define STY_A 0x8c // Store Y in memory, absolute +#define STA_A 0x8d // Store accumulator in memory, absolute +#define STX_A 0x8e // Store X in memory, absolute +/* 81 */ +#define BCC 0x90 // Branch on carry clear +#define STA_ZiY 0x91 // Store accumulator in memory, zero page indirect Y-indexed +#define STY_XZ 0x94 // Store Y in memory, X-indexed zero page +#define STA_XZ 0x95 // Store accumulator in memory, X-indexed zero page +#define STX_YZ 0x96 // Store X in memory, Y-indexed zero page +#define TYA 0x98 // Transfer Y to accumulator +#define STA_YA 0x99 // Store accumulator to memory, Y-indexed absolute +#define TXS 0x9a // Transfer X to stack pointer +#define STA_XA 0x9d // Store accumulator to memory, X-indexed absolute +/* 90 */ +#define LDY_I 0xa0 // Load Y from memory, immediate +#define LDA_XZi 0xa1 // Load accumulator from memory, X-indexed zero page indirect +#define LDX_I 0xa2 // Load X from memory, immediate +#define LDY_Z 0xa4 // Load Y from memory, zero page +#define LDA_Z 0xa5 // Load accumulator from memory, zero page +#define LDX_Z 0xa6 // Load X from memory, zero page +#define TAY 0xa8 // Transfer accumulator to Y +#define LDA_I 0xa9 // Load accumulator from memory, immediate +#define TAX 0xaa // Transfer accumulator to X +#define LDY_A 0xac // Load Y from memory, absolute +#define LDA_A 0xad // Load accumulator from memory, absolute +#define LDX_A 0xae // Load X from memory, absolute +/* 102 */ +#define BCS 0xb0 // Branch on carry set +#define LDA_ZiY 0xb1 // Load accumulator from memory, zero page indirect Y-indexed +#define LDY_XZ 0xb4 // Load Y from memory, X-indexed zero page +#define LDA_XZ 0xb5 // Load A from memory, X-indexed zero page +#define LDX_YZ 0xb6 // Load X from memory, Y-indexed zero page +#define CLV 0xb8 // Clear overflow flag +#define LDA_YA 0xb9 // Load A from memory, Y-indexed absolute +#define TSX 0xba // Transfer stack pointer to X +#define LDY_XA 0xbb // Load Y from memory, X-indexed absolute +#define LDA_XA 0xbd // Load A from memory, X-indexed absolute +#define LDX_YA 0xbe // Load X from memory, Y-indexed absolute +/* 113 */ +#define CPY_I 0xc0 // Compare Y to memory, immediate +#define CMP_XZi 0xc1 // Compare accumulator to memory, X-indexed zero page indirect +#define CPY_Z 0xc4 // Compare Y to memory, zero page +#define CMP_Z 0xc5 // Compare accumulator to memory, zero page +#define DEC_Z 0xc6 // Decrement memory by one, zero page +#define INY 0xc8 // Increment Y by one +#define CMP_I 0xc9 // Compare accumulator to memory, immediate +#define DEX 0xca // Decrement X by one +#define CPY_A 0xcc // Compare Y to memory, absolute +#define CMP_A 0xcd // Compare accumulator to memory, absolute +#define DEC_A 0xce // Decrement memory by one, absolute +/* 124 */ +#define BNE 0xd0 // Branch on result not zero +#define CMP_ZiY 0xd1 // Compare accumulator to memory, zero page indirect Y-indexed +#define CMP_XZ 0xd5 // Compare accumulator to memory, X-indexed zero page +#define DEC_XZ 0xd6 // Decrement memory by one, X-indexed zero page +#define CLD 0xd8 // Clear decimal mode +#define CMP_YA 0xd9 // Compare accumulator to memory, Y-indexed absolute +#define CMP_XA 0xdd // Compare accumulator to memory, X-indexed absolute +#define DEC_XA 0xde // Decrement memory by one, X-indexed absolute +/* 132 */ +#define CPX_I 0xe0 // Compare X to memory, immediate +#define SBC_XZi 0xe1 // Subtract memory from accumulator with borrow, X-indexed zero page indirect +#define CPX_Z 0xe4 // Compare X to memory, zero page +#define SBC_Z 0xe5 // Subtract memory from accumulator with borrow, zero page +#define INC_Z 0xe6 // Increment memory by one, zero page +#define INX 0xe8 // Increment X by one +#define SBC_I 0xe9 // Subtract memory from accumulator with borrow, immediate +#define NOP 0xea // No operation +#define CPX_A 0xec // Compare X to memory, absolute +#define SBC_A 0xed // Subtract memory from accumulator with borrow, absolute +#define INC_A 0xee // Increment memory by one, absolute +/* 143 */ +#define BEQ 0xf0 // Branch on result zero +#define SBC_ZiY 0xf1 // Subtract memory from accumulator with borrow, zero page indirect Y-indexed +#define SBC_XZ 0xf5 // Subtract memory from accumulator with borrow, X-indexed zero page +#define INC_XZ 0xf6 // Increment memory by one, X-indexed zero page +#define SED 0xf8 // Set decimal mode +#define SBC_YA 0xf9 // Subtract memory from accumulator with borrow, Y-indexed absolute +#define SBC_XA 0xfd // Subtract memory from accumulator with borrow, X-indexed absolute +#define INC_XA 0xfe // Increment memory by one, X-indexed absolute +/* 151 */ + +#endif // OPCODES_H \ No newline at end of file diff --git a/inc/ram.h b/inc/ram.h new file mode 100644 index 0000000..fd80f26 --- /dev/null +++ b/inc/ram.h @@ -0,0 +1,14 @@ +#ifndef RAM_H +#define RAM_H + +#include "stdint.h" + +#define RAM_SIZE (55 * 1024) // 55KiB +//#define RAM_SIZE (64 * 1024) // Full range, DEBUG mode + +#define RAM_BASE (0x00) // 0 + +int createRAM(uint8_t **ram); +void destroyRAM(uint8_t *ram); + +#endif // RAM_H \ No newline at end of file diff --git a/inc/rom.h b/inc/rom.h new file mode 100644 index 0000000..57135a7 --- /dev/null +++ b/inc/rom.h @@ -0,0 +1,15 @@ +#ifndef ROM_H +#define ROM_H + +#include "io.h" + +#include + +#define ROM_SIZE (8 * 1024) // 8KiB +#define ROM_BASE (IO_BASE + IO_SIZE) // 55KiB RAM + 1KiB IO + +uint8_t rom[ROM_SIZE]; + +void initRom(uint8_t *rom); + +#endif // ROM_H \ No newline at end of file diff --git a/src/cpu.c b/src/cpu.c new file mode 100644 index 0000000..793b549 --- /dev/null +++ b/src/cpu.c @@ -0,0 +1,1673 @@ +#include "cpu.h" +#include "memoryMap.h" +#include "opcodes.h" + +#define COMBINE(H, L) ((H << 8) | L) +#define PAGE_CROSSED(addr1, addr2) (((addr1) ^ (addr2)) & 0xFF00) +#define LO_BYTE(x) ((uint8_t)((x) & 0xFF)) +#define HI_BYTE(x) ((uint8_t)(((x) >> 8) & 0xFF)) + +void initCPU(CPU *cpu) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" + *cpu = (CPU){0x0000, 0xFF}; +#pragma clang diagnostic pop +} + +void resetCPU(CPU *cpu, uint8_t *rom) { + uint8_t low = rom[0xFFFC - ROM_BASE]; + uint8_t high = rom[0xFFFD - ROM_BASE]; + cpu->ProgrammeCounter = COMBINE(high, low); +} + +useconds_t runCycle(CPU *cpu, uint8_t *ram, uint8_t *rom) { + // Fetch opcode + uint8_t opcode = readMemory(cpu->ProgrammeCounter++, ram, rom); + + // Decode opcode, fetch operands, execute + + switch (opcode) { + case BRK: { + // Store return address on stack + uint16_t retAddr = cpu->ProgrammeCounter + 1; + writeStack(cpu->StackPointer--, HI_BYTE(retAddr), ram); + writeStack(cpu->StackPointer--, LO_BYTE(retAddr), ram); + // Set Break flag, Store flags on stack, including HW unused bit quirk + cpu->Flags.BreakCommand = 1; + writeStack(cpu->StackPointer--, cpu->FlagsByte | 0x30, ram); + // Set PC and remaining flags + uint8_t low = readMemory(0xFFFE, ram, rom); + uint8_t high = readMemory(0xFFFF, ram, rom); + cpu->ProgrammeCounter = COMBINE(high, low); + cpu->Flags.InterruptDisable = 1; + + return 7; + } + case ORA_XZi: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get indirect address, discard carry + uint8_t _address = (uint8_t)(cpu->X + offset); + // Get final address, wrap around page + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Perform OR, set flags + cpu->Accumulator |= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 6; + } + case ORA_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Perform OR, set flags + cpu->Accumulator |= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 3; + } + case ASL_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Perform shift + cpu->Flags.CarryFlag = (value & 0x80) != 0; + value = (uint8_t)(value << 1); + // Set flags and write to memory + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + writeMemory(offset, value, ram, rom); + + return 5; + } + case PHP: { + // Push to stack, force Break and unused to 1 (HW idiosyncrasy) + writeStack(cpu->StackPointer--, cpu->FlagsByte | 0x30, ram); + + return 3; + } + case ORA_I: { + // Get value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Perform OR, set flags + cpu->Accumulator |= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 2; + } + case ASL: { + // Perform shift + cpu->Flags.CarryFlag = (cpu->Accumulator & 0x80) != 0; + cpu->Accumulator = (uint8_t)(cpu->Accumulator << 1); + // Set flags + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 2; + } + case ORA_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Perform OR, set flags + cpu->Accumulator |= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4; + } + case ASL_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Perform shift + cpu->Flags.CarryFlag = (value & 0x80) != 0; + value = (uint8_t)(value << 1); + // Set flags and write to memory + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + writeMemory(COMBINE(high, low), value, ram, rom); + + return 6; + } + + case BPL: { + useconds_t cycles = 2; + + // Get offset + int8_t offset = (int8_t)readMemory(cpu->ProgrammeCounter++, ram, rom); + + // Check flag + if (cpu->Flags.NegativeFlag == 0) { + uint16_t oldPC = cpu->ProgrammeCounter; + // Modify PC + cpu->ProgrammeCounter += offset; + // Calculate cycles + cycles++; + cycles += PAGE_CROSSED(oldPC, cpu->ProgrammeCounter) ? 1 : 0; + } + + return cycles; + } + case ORA_ZiY: { + // Get _address + uint8_t _address = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get offset + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get address + uint16_t address = cpu->Y + COMBINE(high, low); + // Get value + uint8_t value = readMemory(address, ram, rom); + // Perform OR, set flags + cpu->Accumulator |= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 5 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case ORA_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // Perform OR, set flags + cpu->Accumulator |= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4; + } + case ASL_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // Perform shift + cpu->Flags.CarryFlag = (value & 0x80) != 0; + value = (uint8_t)(value << 1); + // Set flags and write to memory + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + writeMemory((uint8_t)(cpu->X + offset), value, ram, rom); + + return 6; + } + case CLC: { + cpu->Flags.CarryFlag = 0; + + return 2; + } + case ORA_YA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->Y + COMBINE(high, low), ram, rom); + // Perform OR, set flags + cpu->Accumulator |= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case ORA_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + // Perform OR, set flags + cpu->Accumulator |= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->X) > 0xFF); + } + case ASL_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + // Perform shift + cpu->Flags.CarryFlag = (value & 0x80) != 0; + value = (uint8_t)(value << 1); + // Set flags and write to memory + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + writeMemory(cpu->X + COMBINE(high, low), value, ram, rom); + + return 7; + } + + case JSR: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Write return address + writeStack(cpu->StackPointer--, HI_BYTE(cpu->ProgrammeCounter - 1), ram); + writeStack(cpu->StackPointer--, LO_BYTE(cpu->ProgrammeCounter - 1), ram); + // Set PC + cpu->ProgrammeCounter = COMBINE(high, low); + + return 6; + } + case AND_XZi: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get indirect address, discard carry + uint8_t _address = (uint8_t)(cpu->X + offset); + // Get final address, wrap around page + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Perform AND, set flags + cpu->Accumulator &= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 6; + } + case BIT_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Set flags + cpu->Flags.ZeroFlag = (cpu->Accumulator & value) == 0; + cpu->Flags.OverflowFlag = (value & 0x40) != 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 3; + } + case AND_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Perform AND, set flags + cpu->Accumulator &= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 3; + } + case ROL_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Save bit 7, rotate, set bit 0 + uint8_t bit7 = value >> 7; + value = value << 1; + value |= cpu->Flags.CarryFlag; + // Set flags + cpu->Flags.CarryFlag = bit7 != 0; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + // Write value + writeMemory(offset, value, ram, rom); + + return 5; + } + case PLP: { + // Pull from stack + cpu->FlagsByte = readStack(++cpu->StackPointer, ram); + + return 4; + } + case AND_I: { + // Get value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Perform AND, set flags + cpu->Accumulator &= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 2; + } + case ROL: { + // Save bit 7, rotate, set bit 0 + uint8_t bit7 = cpu->Accumulator >> 7; + cpu->Accumulator = cpu->Accumulator << 1; + cpu->Accumulator |= cpu->Flags.CarryFlag; + // Set flags + cpu->Flags.CarryFlag = bit7 != 0; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 2; + } + case BIT_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Set flags + cpu->Flags.ZeroFlag = (cpu->Accumulator & value) == 0; + cpu->Flags.OverflowFlag = (value & 0x40) != 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4; + } + case AND_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Perform AND, set flags + cpu->Accumulator &= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4; + } + case ROL_A: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Save bit 7, rotate, set bit 0 + uint8_t bit7 = value >> 7; + value = value << 1; + value |= cpu->Flags.CarryFlag; + // Set flags + cpu->Flags.CarryFlag = bit7 != 0; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + // Write value + writeMemory(COMBINE(high, low), value, ram, rom); + + return 6; + } + + case BMI: { + useconds_t cycles = 2; + + // Get offset + int8_t offset = (int8_t)readMemory(cpu->ProgrammeCounter++, ram, rom); + + // Check flag + if (cpu->Flags.NegativeFlag == 1) { + uint16_t oldPC = cpu->ProgrammeCounter; + // Modify PC + cpu->ProgrammeCounter += offset; + // Calculate cycles + cycles++; + cycles += PAGE_CROSSED(oldPC, cpu->ProgrammeCounter) ? 1 : 0; + } + + return cycles; + } + case AND_ZiY: { + // Get _address + uint8_t _address = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get offset + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get address + uint16_t address = cpu->Y + COMBINE(high, low); + // Get value + uint8_t value = readMemory(address, ram, rom); + // Perform AND, set flags + cpu->Accumulator &= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 5 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case AND_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // Perform AND, set flags + cpu->Accumulator &= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4; + } + case ROL_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // Save bit 7, rotate, set bit 0 + uint8_t bit7 = value >> 7; + value = value << 1; + value |= cpu->Flags.CarryFlag; + // Set flags + cpu->Flags.CarryFlag = bit7 != 0; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + // Write value + writeMemory((uint8_t)(cpu->X + offset), value, ram, rom); + + return 6; + } + case SEC: { + cpu->Flags.CarryFlag = 1; + + return 2; + } + case AND_YA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->Y + COMBINE(high, low), ram, rom); + // Perform AND, set flags + cpu->Accumulator &= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case AND_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + // Perform AND, set flags + cpu->Accumulator &= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->X) > 0xFF); + } + case ROL_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + // Save bit 7, rotate, set bit 0 + uint8_t bit7 = value >> 7; + value = value << 1; + value |= cpu->Flags.CarryFlag; + // Set flags + cpu->Flags.CarryFlag = bit7 != 0; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + // Write value + writeMemory(cpu->X + COMBINE(high, low), value, ram, rom); + + return 7; + } + + case RTI: { + // Pull flags + cpu->FlagsByte = readStack(++cpu->StackPointer, ram); + // Pull address + uint8_t low = readStack(++cpu->StackPointer, ram); + uint8_t high = readStack(++cpu->StackPointer, ram); + // Set PC + cpu->ProgrammeCounter = COMBINE(high, low); + + return 6; + } + case EOR_XZi: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get indirect address, discard carry + uint8_t _address = (uint8_t)(cpu->X + offset); + // Get final address, wrap around page + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Perform XOR, set flags + cpu->Accumulator ^= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 6; + } + case EOR_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Perform XOR, set flags + cpu->Accumulator ^= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 3; + } + case LSR_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Perform shift + cpu->Flags.CarryFlag = (value & 0x01) != 0; + value = (uint8_t)(value >> 1); + // Set flags, write to memory + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = 0; + writeMemory(offset, value, ram, rom); + + return 5; + } + case PHA: { + writeStack(cpu->StackPointer--, cpu->Accumulator, ram); + + return 3; + } + case EOR_I: { + // Get value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Perform XOR, set flags + cpu->Accumulator ^= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 2; + } + case LSR: { + // Perform shift + cpu->Flags.CarryFlag = (cpu->Accumulator & 0x01) != 0; + cpu->Accumulator = (uint8_t)(cpu->Accumulator >> 1); + // Set flags + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = 0; + + return 2; + } + case JMP_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Set PC + cpu->ProgrammeCounter = COMBINE(high, low); + + return 3; + } + case EOR_A: { + break; + } + case LSR_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Perform shift + cpu->Flags.CarryFlag = (value & 0x01) != 0; + value = (uint8_t)(value >> 1); + // Set flags, write to memory + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = 0; + writeMemory(COMBINE(high, low), value, ram, rom); + + return 6; + } + + case BVC: { + useconds_t cycles = 2; + + // Get offset + int8_t offset = (int8_t)readMemory(cpu->ProgrammeCounter++, ram, rom); + + // Check flag + if (cpu->Flags.OverflowFlag == 0) { + uint16_t oldPC = cpu->ProgrammeCounter; + // Modify PC + cpu->ProgrammeCounter += offset; + // Calculate cycles + cycles++; + cycles += PAGE_CROSSED(oldPC, cpu->ProgrammeCounter) ? 1 : 0; + } + + return cycles; + } + case EOR_ZiY: { + // Get _address + uint8_t _address = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get offset + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get address + uint16_t address = cpu->Y + COMBINE(high, low); + // Get value + uint8_t value = readMemory(address, ram, rom); + // Perform XOR, set flags + cpu->Accumulator ^= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 5 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case EOR_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // Perform XOR, set flags + cpu->Accumulator ^= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4; + } + case LSR_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // Perform shift + cpu->Flags.CarryFlag = (value & 0x01) != 0; + value = (uint8_t)(value >> 1); + // Set flags, write to memory + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = 0; + writeMemory((uint8_t)(cpu->X + offset), value, ram, rom); + + return 6; + } + case CLI: { + cpu->Flags.InterruptDisable = 0; + + return 2; + } + case EOR_YA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->Y + COMBINE(high, low), ram, rom); + // Perform XOR, set flags + cpu->Accumulator ^= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case EOR_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + // Perform XOR, set flags + cpu->Accumulator ^= value; + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->X) > 0xFF); + } + case LSR_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + // Perform shift + cpu->Flags.CarryFlag = (value & 0x01) != 0; + value = (uint8_t)(value >> 1); + // Set flags, write to memory + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = 0; + writeMemory(cpu->X + COMBINE(high, low), value, ram, rom); + + return 7; + } + + case RTS: { + // Pull address + uint8_t low = readStack(++cpu->StackPointer, ram); + uint8_t high = readStack(++cpu->StackPointer, ram); + // Set PC + cpu->ProgrammeCounter = COMBINE(high, low) + 1; + + return 6; + } + case ADC_XZi: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get indirect address, discard carry + uint8_t _address = (uint8_t)(cpu->X + offset); + // Get final address, wrap around page + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // BCD + if (cpu->Flags.DecimalMode == 1) { + break; // TODO: BCD + } else { + uint16_t result = cpu->Accumulator + value + cpu->Flags.CarryFlag; + cpu->Flags.CarryFlag = result > 0xFF; + cpu->Flags.ZeroFlag = result == 0; + cpu->Flags.OverflowFlag = ((cpu->Accumulator ^ result) & (value ^ result) & 0x80) != 0; + cpu->Flags.NegativeFlag = (result & 0x80) != 0; + cpu->Accumulator = (uint8_t)result; + } + + return 6; + } + case ADC_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // BCD + if (cpu->Flags.DecimalMode == 1) { + break; // TODO: BCD + } else { + uint16_t result = cpu->Accumulator + value + cpu->Flags.CarryFlag; + cpu->Flags.CarryFlag = result > 0xFF; + cpu->Flags.ZeroFlag = result == 0; + cpu->Flags.OverflowFlag = ((cpu->Accumulator ^ result) & (value ^ result) & 0x80) != 0; + cpu->Flags.NegativeFlag = (result & 0x80) != 0; + cpu->Accumulator = (uint8_t)result; + } + + return 3; + } + case ROR_Z: { + break; + } + case PLA: { + // Pull from stack + cpu->Accumulator = readStack(++cpu->StackPointer, ram); + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 4; + } + case ADC_I: { + // Get value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // BCD + if (cpu->Flags.DecimalMode == 1) { + break; // TODO: BCD + } else { + uint16_t result = cpu->Accumulator + value + cpu->Flags.CarryFlag; + cpu->Flags.CarryFlag = result > 0xFF; + cpu->Flags.ZeroFlag = result == 0; + cpu->Flags.OverflowFlag = ((cpu->Accumulator ^ result) & (value ^ result) & 0x80) != 0; + cpu->Flags.NegativeFlag = (result & 0x80) != 0; + cpu->Accumulator = (uint8_t)result; + } + + return 2; + } + case ROR: { + break; + } + case JMP_Ai: { + // Load pointer address + uint8_t _low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t _high = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint16_t address = COMBINE(_high, _low); + // Get address, replicating page-wrap hardware bug + uint8_t low = readMemory(address, ram, rom); + uint8_t high = readMemory((address & 0xFF00) | ((address + 1) & 0x00FF), ram, rom); + // Set PC + cpu->ProgrammeCounter = COMBINE(high, low); + + return 5; + } + case ADC_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + + // BCD + if (cpu->Flags.DecimalMode == 1) { + break; // TODO: BCD + } else { + uint16_t result = cpu->Accumulator + value + cpu->Flags.CarryFlag; + cpu->Flags.CarryFlag = result > 0xFF; + cpu->Flags.ZeroFlag = result == 0; + cpu->Flags.OverflowFlag = ((cpu->Accumulator ^ result) & (value ^ result) & 0x80) != 0; + cpu->Flags.NegativeFlag = (result & 0x80) != 0; + cpu->Accumulator = (uint8_t)result; + } + + return 4; + } + case ROR_A: { + break; + } + + case BVS: { + useconds_t cycles = 2; + + // Get offset + int8_t offset = (int8_t)readMemory(cpu->ProgrammeCounter++, ram, rom); + + // Check flag + if (cpu->Flags.OverflowFlag == 1) { + uint16_t oldPC = cpu->ProgrammeCounter; + // Modify PC + cpu->ProgrammeCounter += offset; + // Calculate cycles + cycles++; + cycles += PAGE_CROSSED(oldPC, cpu->ProgrammeCounter) ? 1 : 0; + } + + return cycles; + } + case ADC_ZiY: { + // Get _address + uint8_t _address = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get offset + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get address + uint16_t address = cpu->Y + COMBINE(high, low); + // Get value + uint8_t value = readMemory(address, ram, rom); + // BCD + if (cpu->Flags.DecimalMode == 1) { + break; // TODO: BCD + } else { + uint16_t result = cpu->Accumulator + value + cpu->Flags.CarryFlag; + cpu->Flags.CarryFlag = result > 0xFF; + cpu->Flags.ZeroFlag = result == 0; + cpu->Flags.OverflowFlag = ((cpu->Accumulator ^ result) & (value ^ result) & 0x80) != 0; + cpu->Flags.NegativeFlag = (result & 0x80) != 0; + cpu->Accumulator = (uint8_t)result; + } + + return 5 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case ADC_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // BCD + if (cpu->Flags.DecimalMode == 1) { + break; // TODO: BCD + } else { + uint16_t result = cpu->Accumulator + value + cpu->Flags.CarryFlag; + cpu->Flags.CarryFlag = result > 0xFF; + cpu->Flags.ZeroFlag = result == 0; + cpu->Flags.OverflowFlag = ((cpu->Accumulator ^ result) & (value ^ result) & 0x80) != 0; + cpu->Flags.NegativeFlag = (result & 0x80) != 0; + cpu->Accumulator = (uint8_t)result; + } + + return 4; + } + case ROR_XZ: { + break; + } + case SEI: { + cpu->Flags.InterruptDisable = 1; + + return 2; + } + case ADC_YA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->Y + COMBINE(high, low), ram, rom); + // BCD + if (cpu->Flags.DecimalMode == 1) { + break; // TODO: BCD + } else { + uint16_t result = cpu->Accumulator + value + cpu->Flags.CarryFlag; + cpu->Flags.CarryFlag = result > 0xFF; + cpu->Flags.ZeroFlag = result == 0; + cpu->Flags.OverflowFlag = ((cpu->Accumulator ^ result) & (value ^ result) & 0x80) != 0; + cpu->Flags.NegativeFlag = (result & 0x80) != 0; + cpu->Accumulator = (uint8_t)result; + } + + return 4 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case ADC_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + // BCD + if (cpu->Flags.DecimalMode == 1) { + break; // TODO: BCD + } else { + uint16_t result = cpu->Accumulator + value + cpu->Flags.CarryFlag; + cpu->Flags.CarryFlag = result > 0xFF; + cpu->Flags.ZeroFlag = result == 0; + cpu->Flags.OverflowFlag = ((cpu->Accumulator ^ result) & (value ^ result) & 0x80) != 0; + cpu->Flags.NegativeFlag = (result & 0x80) != 0; + cpu->Accumulator = (uint8_t)result; + } + + return 4 + (((uint16_t)low + cpu->X) > 0xFF); + } + case ROR_XA: { + break; + } + + case STA_XZi: { + break; + } + case STY_Z: { + break; + } + case STA_Z: { + break; + } + case STX_Z: { + break; + } + case DEY: { + // Decrement, set flags + cpu->Y--; + cpu->Flags.ZeroFlag = cpu->Y == 0; + cpu->Flags.NegativeFlag = (cpu->Y & 0x80) != 0; + + return 2; + } + case TXA: { + cpu->Accumulator = cpu->X; + // Set flags + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 2; + } + case STY_A: { + break; + } + case STA_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Store accumulator at address + writeMemory(COMBINE(high, low), cpu->Accumulator, ram, rom); + + return 4; + } + case STX_A: { + break; + } + + case BCC: { + useconds_t cycles = 2; + + // Get offset + int8_t offset = (int8_t)readMemory(cpu->ProgrammeCounter++, ram, rom); + + // Check flag + if (cpu->Flags.CarryFlag == 0) { + uint16_t oldPC = cpu->ProgrammeCounter; + // Modify PC + cpu->ProgrammeCounter += offset; + // Calculate cycles + cycles++; + cycles += PAGE_CROSSED(oldPC, cpu->ProgrammeCounter) ? 1 : 0; + } + + return cycles; + } + case STA_ZiY: { + break; + } + case STY_XZ: { + break; + } + case STA_XZ: { + break; + } + case STX_YZ: { + break; + } + case TYA: { + cpu->Accumulator = cpu->Y; + // Set flags + cpu->Flags.ZeroFlag = cpu->Accumulator == 0; + cpu->Flags.NegativeFlag = (cpu->Accumulator & 0x80) != 0; + + return 2; + } + case STA_YA: { + break; + } + case TXS: { + cpu->StackPointer = cpu->X; + + return 2; + } + case STA_XA: { + break; + } + + case LDY_I: { + // Load value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Store in accumulator, set flags + cpu->Y = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 2; + } + case LDA_XZi: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get indirect address, discard carry + uint8_t _address = (uint8_t)(cpu->X + offset); + // Get final address, wrap around page + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Store in accumulator, set flags + cpu->Accumulator = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 6; + } + case LDX_I: { + // Load value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Store in X, set flags + cpu->X = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 2; + } + case LDY_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Store in Y, set flags + cpu->Y = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 3; + } + case LDA_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Store in accumulator set flags + cpu->Accumulator = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 3; + } + case LDX_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Store in X, set flags + cpu->X = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 3; + } + case TAY: { + cpu->Y = cpu->Accumulator; + // Set flags + cpu->Flags.ZeroFlag = cpu->Y == 0; + cpu->Flags.NegativeFlag = (cpu->Y & 0x80) != 0; + + return 2; + } + case LDA_I: { + // Load value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Store in accumulator, set flags + cpu->Accumulator = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 2; + } + case TAX: { + cpu->X = cpu->Accumulator; + // Set flags + cpu->Flags.ZeroFlag = cpu->X == 0; + cpu->Flags.NegativeFlag = (cpu->X & 0x80) != 0; + + return 2; + } + case LDY_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Store in Y, set flags + cpu->Y = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4; + } + case LDA_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Store in accumulator, set flags + cpu->Accumulator = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4; + } + case LDX_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Store in X, set flags + cpu->X = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4; + } + + case BCS: { + useconds_t cycles = 2; + + // Get offset + int8_t offset = (int8_t)readMemory(cpu->ProgrammeCounter++, ram, rom); + + // Check flag + if (cpu->Flags.CarryFlag == 1) { + uint16_t oldPC = cpu->ProgrammeCounter; + // Modify PC + cpu->ProgrammeCounter += offset; + // Calculate cycles + cycles++; + cycles += PAGE_CROSSED(oldPC, cpu->ProgrammeCounter) ? 1 : 0; + } + + return cycles; + } + case LDA_ZiY: { + // Get _address + uint8_t _address = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get offset + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get address + uint16_t address = cpu->Y + COMBINE(high, low); + // Get value + uint8_t value = readMemory(address, ram, rom); + // Store in accumulator, set flags + cpu->Accumulator = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 5 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case LDY_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // Store in Y, set flags + cpu->Y = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4; + } + case LDA_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // Store in accumulator, set flags + cpu->Accumulator = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4; + } + case LDX_YZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->Y + offset), ram, rom); + // Store in X, set flags + cpu->X = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4; + } + case CLV: { + cpu->Flags.OverflowFlag = 0; + + return 2; + } + case LDA_YA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Store in Y, set flags + uint8_t value = readMemory(cpu->Y + COMBINE(high, low), ram, rom); + cpu->Accumulator = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case TSX: { + cpu->X = cpu->StackPointer; + // Set flags + cpu->Flags.ZeroFlag = cpu->X == 0; + cpu->Flags.NegativeFlag = (cpu->X & 0x80) != 0; + + return 2; + } + case LDY_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Store in Y, set flags + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + cpu->Y = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->X) > 0xFF); + } + case LDA_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Store in Y, set flags + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + cpu->Accumulator = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->X) > 0xFF); + } + case LDX_YA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Store in Y, set flags + uint8_t value = readMemory(cpu->Y + COMBINE(high, low), ram, rom); + cpu->X = value; + cpu->Flags.ZeroFlag = value == 0; + cpu->Flags.NegativeFlag = (value & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->Y) > 0xFF); + } + + case CPY_I: { + // Get value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Compare + uint8_t cmp = cpu->Y - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Y; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 2; + } + case CMP_XZi: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get indirect address, discard carry + uint8_t _address = (uint8_t)(cpu->X + offset); + // Get final address, wrap around page + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Compare + uint8_t cmp = cpu->Accumulator - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Accumulator; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 6; + } + case CPY_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Compare + uint8_t cmp = cpu->Y - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Y; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 3; + } + case CMP_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Compare + uint8_t cmp = cpu->Accumulator - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Accumulator; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 3; + } + case DEC_Z: { + break; + } + case INY: { + // Increment, set flags + cpu->Y++; + cpu->Flags.ZeroFlag = cpu->Y == 0; + cpu->Flags.NegativeFlag = (cpu->Y & 0x80) != 0; + + return 2; + } + case CMP_I: { + // Get value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Compare + uint8_t cmp = cpu->Accumulator - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Accumulator; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 2; + } + case DEX: { + // Decrement, set flags + cpu->X--; + cpu->Flags.ZeroFlag = cpu->X == 0; + cpu->Flags.NegativeFlag = (cpu->X & 0x80) != 0; + + return 2; + } + case CPY_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Compare + uint8_t cmp = cpu->Y - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Y; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 4; + } + case CMP_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Compare + uint8_t cmp = cpu->Accumulator - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Accumulator; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 4; + } + case DEC_A: { + break; + } + + case BNE: { + useconds_t cycles = 2; + + // Get offset + int8_t offset = (int8_t)readMemory(cpu->ProgrammeCounter++, ram, rom); + + // Check flag + if (cpu->Flags.ZeroFlag == 0) { + uint16_t oldPC = cpu->ProgrammeCounter; + // Modify PC + cpu->ProgrammeCounter += offset; + // Calculate cycles + cycles++; + cycles += PAGE_CROSSED(oldPC, cpu->ProgrammeCounter) ? 1 : 0; + } + + return cycles; + } + case CMP_ZiY: { + // Get _address + uint8_t _address = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get offset + uint8_t low = readMemory(_address, ram, rom); + uint8_t high = readMemory((uint8_t)(_address + 1), ram, rom); + // Get address + uint16_t address = cpu->Y + COMBINE(high, low); + // Get value + uint8_t value = readMemory(address, ram, rom); + // Compare + uint8_t cmp = cpu->Accumulator - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Accumulator; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 5 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case CMP_XZ: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory((uint8_t)(cpu->X + offset), ram, rom); + // Compare + uint8_t cmp = cpu->Accumulator - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Accumulator; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 4; + } + case DEC_XZ: { + break; + } + case CLD: { + cpu->Flags.DecimalMode = 0; + + return 2; + } + case CMP_YA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->Y + COMBINE(high, low), ram, rom); + // Compare + uint8_t cmp = cpu->Accumulator - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Accumulator; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->Y) > 0xFF); + } + case CMP_XA: { + // Get address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(cpu->X + COMBINE(high, low), ram, rom); + // Compare + uint8_t cmp = cpu->Accumulator - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->Accumulator; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 4 + (((uint16_t)low + cpu->X) > 0xFF); + } + case DEC_XA: { + break; + } + + case CPX_I: { + // Get value + uint8_t value = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Compare + uint8_t cmp = cpu->X - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->X; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 2; + } + case SBC_XZi: { + break; + } + case CPX_Z: { + // Get offset + uint8_t offset = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(offset, ram, rom); + // Compare + uint8_t cmp = cpu->X - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->X; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 3; + } + case SBC_Z: { + break; + } + case INC_Z: { + break; + } + case INX: { + // Increment, set flags + cpu->X++; + cpu->Flags.ZeroFlag = cpu->X == 0; + cpu->Flags.NegativeFlag = (cpu->X & 0x80) != 0; + + return 2; + } + case SBC_I: { + break; + } + case NOP: { + return 2; + } + case CPX_A: { + // Load address + uint8_t low = readMemory(cpu->ProgrammeCounter++, ram, rom); + uint8_t high = readMemory(cpu->ProgrammeCounter++, ram, rom); + // Get value + uint8_t value = readMemory(COMBINE(high, low), ram, rom); + // Compare + uint8_t cmp = cpu->X - value; + // Set flags + cpu->Flags.CarryFlag = value <= cpu->X; + cpu->Flags.ZeroFlag = cmp == 0; + cpu->Flags.NegativeFlag = (cmp & 0x80) != 0; + + return 4; + } + case SBC_A: { + break; + } + case INC_A: { + break; + } + + case BEQ: { + useconds_t cycles = 2; + + // Get offset + int8_t offset = (int8_t)readMemory(cpu->ProgrammeCounter++, ram, rom); + + // Check flag + if (cpu->Flags.ZeroFlag == 1) { + uint16_t oldPC = cpu->ProgrammeCounter; + // Modify PC + cpu->ProgrammeCounter += offset; + // Calculate cycles + cycles++; + cycles += PAGE_CROSSED(oldPC, cpu->ProgrammeCounter) ? 1 : 0; + } + + return cycles; + } + case SBC_ZiY: { + break; + } + case SBC_XZ: { + break; + } + case INC_XZ: { + break; + } + case SED: { + cpu->Flags.DecimalMode = 1; + + return 2; + } + case SBC_YA: { + break; + } + case SBC_XA: { + break; + } + case INC_XA: { + break; + } + + default: + break; + } + + // Return sleep duration in microseconds to emulate cycle count of executed instruction + + return 1; +} \ No newline at end of file diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..d42dccf --- /dev/null +++ b/src/io.c @@ -0,0 +1,14 @@ +#include "io.h" + +#include + +int putChar(uint8_t c) { + int _c = putchar(c); + fflush(stdout); + return _c; +} + +int getChar(void) { + clearerr(stdin); + return getchar(); +} \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..08ed31d --- /dev/null +++ b/src/main.c @@ -0,0 +1,83 @@ +#include "cpu.h" +#include "ram.h" +#include "rom.h" + +#include +#include +#include +#include +#include + +static struct termios orig_termios; + +void restore_terminal(void) { tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); } + +void set_nonblocking_input(void) { + struct termios raw; + + tcgetattr(STDIN_FILENO, &orig_termios); + atexit(restore_terminal); + + raw = orig_termios; + raw.c_lflag &= ~(ICANON | ECHO); // disable line buffering and echo + raw.c_cc[VMIN] = 0; // don't block waiting for input + raw.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSANOW, &raw); + + // make stdin reads non-blocking too + int flags = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); +} + +int main(void) { + uint8_t *ram = NULL; + CPU cpu; + + set_nonblocking_input(); + + initRom(rom); + + if (createRAM(&ram) != 0) { + printf("Could not create RAM\n"); + return 1; + } + + /* DEBUG */ + + FILE *fp = fopen("/Users/Kili2/Documents/Projekte/6502pc/6502_65C02_functional_tests/6502_functional_test.bin", "rb"); + if (!fp) { + perror("fopen"); + return 1; + } + + fread(ram, 1, 64 * 1024, fp); + if (ferror(fp)) { + perror("fread"); + fclose(fp); + return 1; + } + + fclose(fp); + + /* DEBUG END*/ + + initCPU(&cpu); + resetCPU(&cpu, rom); + + while (1) { + //printf("\tPC: 0x%x\tOP: 0x%x\n", cpu.ProgrammeCounter, ram[cpu.ProgrammeCounter]); + useconds_t sleep = runCycle(&cpu, ram, rom); + if (sleep == 1) { + printf("opcode missing: 0x%X\n", ram[cpu.ProgrammeCounter - 1]); + return 1; + } + fflush(stdout); + usleep(sleep); + } + + /* CPU WORK */ + + destroyRAM(ram); + + return 0; +} \ No newline at end of file diff --git a/src/memoryMap.c b/src/memoryMap.c new file mode 100644 index 0000000..4307147 --- /dev/null +++ b/src/memoryMap.c @@ -0,0 +1,44 @@ +#include "memoryMap.h" + +uint8_t readMemory(uint16_t address, uint8_t *ram, uint8_t *rom) { + if (address < RAM_SIZE || address >= IO_BASE + IO_SIZE) { // FIXME: Expanded for debug + return ram[address]; + } else if (address < RAM_SIZE + IO_SIZE) { + uint16_t _address = address - RAM_SIZE; + switch (_address) { + case KEYBOARD_ADDRESS: { + return getChar(); + } + + default: + break; + } + + return 0; + } else { + return rom[address - RAM_SIZE - IO_SIZE]; + } +} + +void writeMemory(uint16_t address, uint8_t value, uint8_t *ram, uint8_t *rom) { + if (address < RAM_SIZE) { + ram[address] = value; + } else if (address < RAM_SIZE + IO_SIZE || address >= IO_BASE + IO_SIZE) { // FIXME: Expanded for debug + uint16_t _address = address - RAM_SIZE; + switch (_address) { + case SCREEN_ADDRESS: { + putChar(value); + break; + } + + default: + break; + } + } else { + rom[address - RAM_SIZE - IO_SIZE] = value; + } +} + +uint8_t readStack(uint8_t address, uint8_t *ram) { return ram[0x100 + address]; } + +void writeStack(uint8_t address, uint8_t value, uint8_t *ram) { ram[0x100 + address] = value; } \ No newline at end of file diff --git a/src/ram.c b/src/ram.c new file mode 100644 index 0000000..01d8ac6 --- /dev/null +++ b/src/ram.c @@ -0,0 +1,12 @@ +#include "ram.h" + +#include + +int createRAM(uint8_t **ram) { + *ram = (uint8_t *)malloc(RAM_SIZE); + + if (*ram == NULL) return -1; + return 0; +} + +void destroyRAM(uint8_t *ram) { free(ram); } \ No newline at end of file diff --git a/src/rom.c b/src/rom.c new file mode 100644 index 0000000..91b6051 --- /dev/null +++ b/src/rom.c @@ -0,0 +1,17 @@ +#include "rom.h" + +uint8_t rom[ROM_SIZE] = {0}; + +void initRom(uint8_t *rom) { + rom[0] = 0xa9, // LDA + rom[1] = 'H', // H literal + rom[2] = 0x8d, // STA + rom[3] = (uint8_t)((IO_BASE + SCREEN_ADDRESS) & 0xFF), // Lower byte of screen + rom[4] = (uint8_t)((IO_BASE + SCREEN_ADDRESS) >> 8), // Upper byte of screen + rom[6] = + /* Reset Vector */ + rom[0xFFFC - ROM_BASE] = 0x00, // Lower byte base of ROM + rom[0xFFFD - ROM_BASE] = 0x04, // Upper byte of base of ROM + rom[0xFFFE - ROM_BASE] = 0x00, // Lower byte base of ROM + rom[0xFFFF - ROM_BASE] = 0x00; // Upper byte of base of ROM +} \ No newline at end of file