====== Decimal mode in NMOS 6500 series ====== Most sources claim that the NMOS 6500 series sets the N, V and Z flags unpredictably when performing addition or subtraction in decimal mode. Of course, this is not true. While testing how the flags are set, I also wanted to see what happens if you use illegal BCD values. ADC works in Decimal mode in a quite complicated way. It is amazing how it can do that all in a single cycle. Here's a C code version of the instruction: [ Warning: this code is NOT accurate. ] unsigned A, /* Accumulator */ AL, /* low nybble of accumulator */ AH, /* high nybble of accumulator */ C, /* Carry flag */ Z, /* Zero flag */ V, /* oVerflow flag */ N, /* Negative flag */ s; /* value to be added to Accumulator */ AL = (A & 15) + (s & 15) + C; /* Calculate the lower nybble. */ AH = (A >> 4) + (s >> 4) + (AL > 15); /* Calculate the upper nybble. */ Z = ((A + s + C) & 255 != 0); /* Zero flag is set just like in Binary mode. */ if (AL > 9) AL += 6; /* BCD fixup for lower nybble. */ /* Negative and Overflow flags are set with the same logic than in Binary mode, but after fixing the lower nybble. */ N = (AH & 8 != 0); V = ((AH << 4) ^ A) & 128 && !((A ^ s) & 128); if (AH > 9) AH += 6; /* BCD fixup for upper nybble. */ /* Carry is the only flag set after fixing the result. */ C = (AH > 15); A = ((AH << 4) | (AL & 15)) & 255; The C flag is set as the quiche eaters expect, but the N and V flags are set after fixing the lower nybble but before fixing the upper one. They use the same logic than binary mode ADC. The Z flag is set before any BCD fixup, so the D flag does not have any influence on it. Proof: The following test program tests all 131072 ADC combinations in Decimal mode, and aborts with BRK if anything breaks this theory. If everything goes well, it ends in RTS. begin 600 dadc M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'BI&* A/N$_$B@+)$KH(V1 M*Q@(I?PI#X7]I?LI#V7]R0J0 FD%J"D/A?VE^RGP9?PI\ C $) ":0^JL @H ML ?)H) &""@X:5\X!?V%_0AH*3W@ ! ""8"HBD7[$ JE^T7\, 28"4"H**7[ M9?S0!)@) J@8N/BE^V7\V A%_= G:(3]1?W0(.;[T(?F_-"#:$D8\ )88*D= 0&&4KA?NI &4LA?RI.&S[ A% end Decimal Mode AC +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +10 +11 59 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 69 70 5a 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 5b 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 60 71 72 5c 62 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 60 61 72 73 5d 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 60 61 62 73 74 5e 65 66 67 68 69 6a 6b 6c 6d 6e 6f 60 61 62 63 74 75 5f 66 67 68 69 6a 6b 6c 6d 6e 6f 60 61 62 63 64 75 76 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 70 71 Table 1: Sample results The triangular area (5b+f, 5f+b, 5f+f) with significantly smaller results is due to the fact that Carry cannot reach value of "2". All programs in this chapter have been successfully tested on a Vic20 and a Commodore 64 and a Commodore 128D in C64 mode. They should run on C16, +4 and on the PET series as well. If not, please report the problem to Marko M"akel"a. Each test in this chapter should run in less than a minute at 1 MHz. SBC is much easier. Just like CMP, its flags are not affected by the D flag. Proof: begin 600 dsbc-cmp-flags M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'B@ (3[A/RB XH8:66HL2N@ M09$KH$R1*XII::BQ*Z!%D2N@4)$K^#BXI?OE_-@(:(7].+BE^^7\"&A%_? ! 5 .;[T./F_-#?RA"_8!@X&#CEY<7% end The only difference in SBC's operation in decimal mode from binary mode is the result-fixup: unsigned A, /* Accumulator */ AL, /* low nybble of accumulator */ AH, /* high nybble of accumulator */ C, /* Carry flag */ Z, /* Zero flag */ V, /* oVerflow flag */ N, /* Negative flag */ s; /* value to be added to Accumulator */ AL = (A & 15) - (s & 15) - !C; /* Calculate the lower nybble. */ if (AL & 16) AL -= 6; /* BCD fixup for lower nybble. */ AH = (A >> 4) - (s >> 4) - (AL & 16); /* Calculate the upper nybble. */ if (AH & 16) AH -= 6; /* BCD fixup for upper nybble. */ /* The flags are set just like in Binary mode. */ C = (A - s - !C) & 256 != 0; Z = (A - s - !C) & 255 != 0; V = ((A - s - !C) ^ s) & 128 && (A ^ s) & 128; N = (A - s - !C) & 128 != 0; A = ((AH << 4) | (AL & 15)) & 255; Again Z flag is set before any BCD fixup. The N and V flags are set at any time before fixing the high nybble. The C flag may be set in any phase. Decimal subtraction is easier than decimal addition, as you have to make the BCD fixup only when a nybble overflows. In decimal addition, you had to verify if the nybble was greater than 9. The processor has an internal "half carry" flag for the lower nybble, used to trigger the BCD fixup. When calculating with legal BCD values, the lower nybble cannot overflow again when fixing it. So, the processor does not handle overflows while performing the fixup. Similarly, the BCD fixup occurs in the high nybble only if the value overflows, i.e. when the C flag will be cleared. Because SBC's flags are not affected by the Decimal mode flag, you could guess that CMP uses the SBC logic, only setting the C flag first. But the SBX instruction shows that CMP also temporarily clears the D flag, although it is totally unnecessary. The following program, which tests SBC's result and flags, contains the 6502 version of the pseudo code example above. begin 600 dsbc M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'BI&* A/N$_$B@+)$KH':1 M*S@(I?PI#X7]I?LI#^7]L /I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL KI7RBP M#ND/.+ )*+ &Z0^P NE?A/T%_87]*+BE^^7\"&BH.+CXI?OE_-@(1?W0FVB$ 8_47]T)3F^]">YOS0FFA)&- $J3C0B%A@ end Obviously the undocumented instructions RRA (ROR+ADC) and ISB (INC+SBC) have inherited also the decimal operation from the official instructions ADC and SBC. The program droradc proves this statement for ROR, and the dincsbc test proves this for ISB. Finally, dincsbc-deccmp proves that ISB's and DCP's (DEC+CMP) flags are not affected by the D flag. begin 644 droradc M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH(V1 M*S@(I?PI#X7]I?LI#V7]R0J0`FD%J"D/A?VE^RGP9?PI\`C`$)`":0^JL`@H ML`?)H)`&""@X:5\X!?V%_0AH*3W@`!`""8"HBD7[$`JE^T7\,`28"4"H**7[ M9?S0!)@)`J@XN/BE^R;\9_S8"$7]T"=HA/U%_=`@YOO0A>;\T(%H21CP`EA@ 2J1T892N%^ZD`92R%_*DX;/L` ` end begin 644 dincsbc M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH':1 M*S@(I?PI#X7]I?LI#^7]L`/I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL`KI7RBP M#ND/.+`)*+`&Z0^P`NE?A/T%_87]*+BE^^7\"&BH.+CXI?O&_.?\V`A%_="9 ::(3]1?W0DN;[T)SF_-"8:$D8T`2I.-"&6&#\ ` end begin 644 dincsbc-deccmp M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'B@`(3[A/RB`XH8:7>HL2N@ M3Y$KH%R1*XII>ZBQ*Z!3D2N@8)$KBFE_J+$KH%61*Z!BD2OX.+BE^^;\Q_S8 L"&B%_3BXI?OF_,?\"&A%_?`!`.;[T-_F_-#;RA"M8!@X&#CFYL;&Q\?GYP#8 ` end