[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Help with confirmation on a small bit of assembly?
Linards Ticmanis <ticmanis@gmx.de> wrote:
> David Empson wrote:
>
> > On the 6502, if you use decimal mode, it doesn't implement some of the
> > flags (if I remember right C is OK, but N, Z and V don't get modified by
> > decimal ADC/SBC). This behaviour was amended on the 65C02 (and is the
> > most reliable way of detecting a 6502 vs 65C02).
>
> Not quite... they're being set, just not in a very useful fashion. The
> following is the code from ADC in VICE (Commodore emulator):
Hi Linards. Thanks for the correction and detail.
I've verified some aspects of that behaviour on a real Apple IIe with a
6502 processor.
To summarise the code for anyone not able to follow it in detail, the N,
V and Z flags are set based on the temporary result of the add prior to
correcting the high nibble for decimal mode. The C flag is correct.
Some examples:
0x90 + 0x10 = 0xA0, decimal adjusted to 0x00 with N=1 (wrong), V=0
(right) and Z=0 (wrong).
0x80 + 0x80 = 0x100, decimal adjusted to 0x60 with N=0 (right), V=1
(right) and Z=1 (wrong).
0x90 + 0x90 = 0x120, decimal adjusted to 0x80 with N=0 (wrong), V=1
(wrong) and Z=0 (right).
My test code for a 6502 was doing 0x99 + 0x01 and checking that the N
flag was set, but I misunderstood it as being because ADC in decimal
mode didn't adjust N. It turns out that for that particular case, the
temporary result (0xA0) causes N=1, but the final result is 0x00,
resulting in an incorrect value of N.
The 65C02 generates the correct values for N, V and Z for a decimal mode
add, but in order to do so it has to use an extra cycle. The 65816
manages to generate the correct values for N, V and Z without wasting a
cycle.
> if (LOCAL_DECIMAL()) { \
> tmp = (reg_a & 0xf) + (tmp_value & 0xf) + (reg_p & 0x1); \
> if (tmp > 0x9) \
> tmp += 0x6; \
> if (tmp <= 0x0f) \
> tmp = (tmp & 0xf) + (reg_a & 0xf0) + (tmp_value & 0xf0); \
> else \
> tmp = (tmp & 0xf) + (reg_a & 0xf0) + (tmp_value & 0xf0) + 0x10;\
> LOCAL_SET_ZERO(!((reg_a + tmp_value + (reg_p & 0x1)) & 0xff)); \
> LOCAL_SET_SIGN(tmp & 0x80); \
> LOCAL_SET_OVERFLOW(((reg_a ^ tmp) & 0x80) \
> && !((reg_a ^ tmp_value) & 0x80)); \
> if ((tmp & 0x1f0) > 0x90) \
> tmp += 0x60; \
> LOCAL_SET_CARRY((tmp & 0xff0) > 0xf0); \
> }
--
David Empson
dempson@actrix.gen.nz