### Site Tools

base:decimal_mode_in_nmos_6500_series

# Differences

This shows you the differences between two versions of the page.

 — base:decimal_mode_in_nmos_6500_series [2015-04-17 04:31] (current) Line 1: Line 1: + ====== 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:​ + + <​code>​ + [ 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 +