User Tools

Site Tools


base:decimal_mode_in_nmos_6500_series

Differences

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

Link to this comparison view

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
 +</​code>​
  
base/decimal_mode_in_nmos_6500_series.txt ยท Last modified: 2015-04-17 04:31 (external edit)