; 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 = 1 ; 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 = 0 ; 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