diff --git a/6502_decimal_test.a65 b/6502_decimal_test.a65 new file mode 100644 index 0000000..7a76ad1 --- /dev/null +++ b/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_functional_test.a65 b/6502_functional_test.a65 index 6ebaaae..3655e37 100644 --- a/6502_functional_test.a65 +++ b/6502_functional_test.a65 @@ -21,7 +21,7 @@ ; addressing modes with focus on propper setting of the processor status ; register bits. ; -; version 24-aug-2015 +; version 21-oct-2015 ; contact info at http://2m5.de or email K@2m5.de ; ; assembled with AS65 from http://www.kingswood-consulting.co.uk/assemblers/ @@ -72,6 +72,7 @@ ; 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 ; C O N F I G U R A T I O N @@ -120,6 +121,10 @@ report = 0 ;leave disabled if a monitor, OS or background interrupt is allowed to alter RAM ram_top = -1 +;disable test decimal mode ADC & SBC, 0=enable, 1=disable, +;2=disable including decimal flag in processor status +disable_decimal = 0 + noopt ;do not take shortcuts ;macros for error & success traps to allow user modification @@ -244,12 +249,16 @@ 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) @@ -298,6 +307,64 @@ 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 @@ -501,9 +568,9 @@ ccs3\? adc zero_page,x clc ccs2\? inx bne ccs3\? - ldx #hi(data_segment) ;set high byte of indirect pointer + ldx #hi(abs1) ;set high byte of indirect pointer stx zpt+1 - ldy #lo(data_bss)+15 ;data after write & execute test area + ldy #lo(abs1) ;data after write & execute test area ccs5\? adc (zpt),y bcc ccs4\? inc zpt+3 ;carry to high byte @@ -764,9 +831,9 @@ gcs3 adc zero_page,x clc gcs2 inx bne gcs3 - ldx #hi(data_segment) ;set high byte of indirect pointer + ldx #hi(abs1) ;set high byte of indirect pointer stx zpt+1 - ldy #lo(data_bss)+15 ;data after write & execute test area + ldy #lo(abs1) ;data after write & execute test area gcs5 adc (zpt),y bcc gcs4 inc ram_chksm+1 ;carry to high byte @@ -4666,7 +4733,7 @@ tand1 lda zpAN,x bpl tand1 ldx #3 ;zp -tand2 lda zpAN,x +tand2 lda zpAN,x sta zpt set_ax absANa,0 and zpt @@ -5101,6 +5168,7 @@ tadd1 ora adrh ;merge C to expected flags lda ad2 sta adrl bne tadd ;iterate op2 + if disable_decimal < 1 next_test ; decimal add/subtract test @@ -5239,7 +5307,8 @@ bin_rti_ret 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 @@ -5264,6 +5333,7 @@ bin_rti_ret ; 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 @@ -5459,6 +5529,7 @@ chkdad 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