User Tools

Site Tools


base:decimal_mode_in_nmos_6500_series
no way to compare when less than two revisions

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) – created - external edit 127.0.0.1
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 by 127.0.0.1