From 80414c38726a664c1fe3d13d66103c2e6fb0e81f Mon Sep 17 00:00:00 2001 From: Kevin Goodsell Date: Wed, 11 Dec 2024 18:17:43 -0800 Subject: [PATCH 1/2] Add ca65 version of interrupt test --- ca65/6502_interrupt_test.ca65 | 1035 +++++++++++++++++++++++++++++++++ ca65/Makefile | 1 + 2 files changed, 1036 insertions(+) create mode 100644 ca65/6502_interrupt_test.ca65 diff --git a/ca65/6502_interrupt_test.ca65 b/ca65/6502_interrupt_test.ca65 new file mode 100644 index 0000000..a26ca58 --- /dev/null +++ b/ca65/6502_interrupt_test.ca65 @@ -0,0 +1,1035 @@ +; +; 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 CA65, linked with LD65 (cc65.github.io): +; ca65 -l 6502_interrupt_test.lst 6502_interrupt_test.ca65 +; ld65 6502_interrupt_test.o -o 6502_interrupt_test.bin \ +; -m 6502_interrupt_test.map -C example.cfg +; example linker config (example.cfg): +; MEMORY { +; RAM: start = $0000, size=$8000, type = rw, fill = yes, \ +; fillval = $FF, file = %O; +; ROM: start = $8000, size=$7FFA, type = ro, fill = yes, \ +; fillval = $FF, file = %O; +; ROM_VECTORS: start = $FFFA, size=6, type = ro, fill = yes, \ +; fillval = $FF, file = %O; +; } +; SEGMENTS { +; ZEROPAGE: load=RAM, type=rw; +; DATA: load=RAM, type=rw, offset=$0200; +; CODE: load=RAM, type=rw, offset=$0400; +; VECTORS: load=ROM_VECTORS, type=ro; +; } +; +; 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 = $a + +;data_segment memory start address, 4 consecutive Bytes required +; check that this matches the linker configuration file +data_segment = $200 + +;code_segment memory start address +; check that this matches the linker configuration file +code_segment = $400 + +;report errors through I/O channel (0=use standard self trap loops, 1=include +;report.i65 as I/O channel) +report = 0 + +;macros for error & success traps to allow user modification +;example: +; .macro trap +; jsr my_error_handler +; .endmacro +; .macro trap_eq +; bne :+ +; trap ;failed equal (zero) +;: +; .endmacro +; +; 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 + .macro trap + jmp * ;failed anyway + .endmacro + .macro trap_eq + beq * ;failed equal (zero) + .endmacro + .macro trap_ne + bne * ;failed not equal (non zero) + .endmacro +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible + .macro success + jmp * ;test passed, no errors + .endmacro + .endif + .if report = 1 + .macro trap + jsr report_error + .endmacro + .macro trap_eq + bne :+ + trap ;failed equal (zero) +: + .endmacro + .macro trap_ne + beq :+ + trap ;failed not equal (non zero) +: + .endmacro +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible + .macro success + jsr report_success + .endmacro + .endif + + .define equ = + +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 + .macro push_stat p1 ;setting flags in the processor status register + lda #p1 + pha ;use stack to load status + .endmacro + + .macro set_stat p1 ;setting flags in the processor status register + lda #p1 + pha ;use stack to load status + plp + .endmacro + + .ZEROPAGE + .res zero_page, 0 + .org zero_page +;BRK, IRQ, NMI test interrupt save +zpt: +irq_a: .res 1,0 ;a register +irq_x: .res 1,0 ;x register +irq_f: .res 1,0 ;flags +nmi_a: .res 1,0 ;a register +nmi_x: .res 1,0 ;x register +nmi_f: .res 1,0 ;flags +zp_bss: + +;fixed stack locations +lst_f equ $1fe ;last flags before interrupt +lst_a equ $1ff ;last accumulator before interrupt + + .DATA + .org data_segment +;concurrent NMI, IRQ & BRK test result +nmi_count: .res 1,0 ;lowest number handled first, $ff = never +irq_count: .res 1,0 ;separation-1 = instructions between interrupts +brk_count: .res 1,0 +;expected interrupt mask +I_src: .res 1,0 ;bit: 0=BRK, 1=IRQ, 2=NMI +data_bss: + + .CODE + .org code_segment + .P02 ; disable 65SC02, 65C02 and 65816 instructions +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) + .macro I_set ibit ;ibit = interrupt bit + lda I_port ;turn on interrupt by bit + and #I_filter-(1< 0 ;with DDR + lda I_ddr ;set DDR for IRQ to enabled + and #I_filter + ora #(1< I_DDR or I_port to force interrupt + .if I_ddr <> 0 ;with DDR + .macro I_set ibit ;ibit = interrupt bit + lda I_ddr ;turn on interrupt by bit + and #I_filter + ora #(1< I_port to force interrupt) + .macro I_set ibit ;ibit = interrupt bit + lda I_port ;turn on interrupt by bit + .if ibit > 7 ;set both NMI & IRQ + and #I_filter-(1< 0 ;with DDR + lda I_ddr ;set DDR for IRQ & NMI to enabled + and #I_filter + ora #(1< I_DDR or I_port to force interrupt + .if I_ddr <> 0 ;with DDR + .macro I_set 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< Date: Wed, 11 Dec 2024 18:30:44 -0800 Subject: [PATCH 2/2] Translate more of functional test to ca65 This enables the RAM integrity test and report options. --- ca65/6502_functional_test.ca65 | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ca65/6502_functional_test.ca65 b/ca65/6502_functional_test.ca65 index a7b98e4..3c6ef2e 100644 --- a/ca65/6502_functional_test.ca65 +++ b/ca65/6502_functional_test.ca65 @@ -569,7 +569,8 @@ m8i equ $ff&~intdis ;8 bit mask - interrupt disable ; designated write areas. ; uses zpt word as indirect pointer, zpt+2 word as checksum .if ram_top > -1 -check_ram macro + .macro check_ram + .local ccs3, ccs2, ccs5, ccs4 cld lda #0 sta zpt ;set low byte of indirect pointer @@ -585,9 +586,9 @@ ccs3: adc zero_page,x clc ccs2: inx bne ccs3 - ldx #hi(abs1) ;set high byte of indirect pointer + ldx #>(abs1) ;set high byte of indirect pointer stx zpt+1 - ldy #lo(abs1) ;data after write & execute test area + ldy #<(abs1) ;data after write & execute test area ccs5: adc (zpt),y bcc ccs4 inc zpt+3 ;carry to high byte @@ -622,7 +623,7 @@ test_num .set test_num + 1 .endmacro .ZEROPAGE - .res zero_page, 0 + .res zero_page, 0 .org zero_page ;break test interrupt save @@ -831,7 +832,7 @@ ld_vect:lda vec_init,x pla and #4 ;isolate flag sta flag_I_on ;or mask - eor #lo(~4) ;reverse + eor #<(~4) ;reverse sta flag_I_off ;and mask .endif @@ -851,9 +852,9 @@ gcs3: adc zero_page,x clc gcs2: inx bne gcs3 - ldx #hi(abs1) ;set high byte of indirect pointer + ldx #>(abs1) ;set high byte of indirect pointer stx zpt+1 - ldy #lo(abs1) ;data after write & execute test area + ldy #<(abs1) ;data after write & execute test area gcs5: adc (zpt),y bcc gcs4 inc ram_chksm+1 ;carry to high byte @@ -6013,7 +6014,7 @@ break2: ;BRK pass 2 jmp start ;catastrophic error - cannot continue .if report = 1 - include "report.i65" + .include "report.i65" .endif ;copy of data to initialize BSS segment @@ -6103,13 +6104,13 @@ absEOa_:.byte $ff,$f0,$f0,$0f ;test pattern for EOR ;logical results absrlo_:.byte 0,$ff,$7f,$80 absflo_:.byte fz,fn,0,fn -data_end +data_end: .if (data_end - data_init) <> (data_bss_end - data_bss) ;force assembler error if size is different .error "mismatch between bss and data" .endif -vec_init +vec_init: .word nmi_trap .word res_trap .word irq_trap