Date : Sat, 16 Jul 1994 22:20:09 EST
From : Stephen Quan <quan@...>
Subject: more on ADC lookup tables.
Hi, I have been looking at lookup tables for
1. ADC's BCD arithmetic
2. ADC's status register
I have already covered my technique in 1 and 2, but today I have
some real example code of this! :-)
For BCD addition, I have
A2 = lo_bcd_ADC[sum = (A & 15) + (M & 15) + get_C()] ^
hi_bcd_ADC[sum = (A >> 4) + (M >> 4) + lo_bcd_ADC_carry[sum]];
Firstly, I have two lookup tables that handle the low-nibble addition
lo_bcd_ADC[32]
lo_bcd_ADC_carry[32]
which is indexed by the sum of the low nibbles of A and M with C.
These tables contain 32 numbers in them! They are small! :-)
Then I have the same two tables but reserved for hi-nibble addition
hi_bcd_ADC[32]
hi_bcd_ADC_carry[32]
In coding this, I have noted that hi_bcd_ADC_carry and lo_bcd_ADC_carry
are exactly the same. The complete lookup tables are given at the end
of this document.
Also, I have decided to look at updating the status register
if (hi_bcd_ADC_carry[sum])
SR = (SR & ~NVZC_msk) ^ NVZC[beforeNZ[A] ^ afterNZ[A2]];
else
SR = (SR & ~NVZC_msk) ^ NVZ[beforeNZ[A] ^ afterNZ[A2]];
Basically, calculating the status register was actually easier to
understand than the above. We already have C, it's hi_bcd_ADC_carry[sum].
N and Z are pretty straight forward, as a matter of fact I had NZ
coming out automatically from the tables I used for DEC. I think I
posted the table previously as P_DEC. Since then I have renamed that
table as NZ_lookup, and have statements like
P = P & ~NZ_msk | NZ_lookup[--X]; /* DEX */
P = P & ~NZ_msk | NZ_lookup[++Y]; /* INY */
P = P & ~NZ_msk | NZ_lookup[A=immediate()]; /* LDA #immediate. */
Which leaves the problem of how to obtain V??? As it turns out,
V = (Nbefore != Nafter)
You may have to try out a few examples before you'll see this!
In my C lookup-table implementation, I have everything being looked
up and very few places where I do actual arithmetic. I have many
redundancies in my lookup tables, but I did this deliberately to
cut down the amount of C code required.
beforeNZ[A]
I am only really interested in the N-before and N-after, but I thought,
whilst I am at it, I can go off and work out Z-before and Z-after at
now cost in speed. ie. I also have
afterNZ[A2]
I also decided that beforeNZ[A] will have the NZ bits moved to bits 1
and bit 0. (Z-before is bit 0, N-before is bit 1). So that with
afterNZ[A2] I can have NZ bits moved to bits 3 and bits 2. (Z-after
is bit 3 and N-after is bit 2).
beforeNZ[A] ^ afterNZ[A2]
Therefore beforeNZ[A] ^ afterNZ[A2] will be a number between 0 to 15.
I can construct a lookup table that turns this into a V value. Again,
being redundant, I decided to have a NVZ lookup table
NVZ[beforeNZ[A] ^ afterNZ[A2]]
Whereby all status bits for N, Z and V are now in the correct positions.
Also I have a duplicate of this table which simply has the carry bit
always set
NVZC[beforeNZ[A] ^ afterNZ[A2]]
Wrapping everything together I get the final version of updating the
status register.
if (hi_bcd_ADC_carry[sum])
SR = (SR & ~NVZC_msk) ^ NVZC[beforeNZ[A] ^ afterNZ[A2]];
else
SR = (SR & ~NVZC_msk) ^ NVZ[beforeNZ[A] ^ afterNZ[A2]];
My complete definition for my ADC macro is :
#define ADC(Mfunc) \
{ \
register BYTE sum; \
register BYTE A2; \
register BYTE M; \
\
if (SR & D_msk) \
{ \
M = Mfunc; \
A2 = lo_bcd_ADC[sum = (A & 15) + (M & 15) + get_C()] ^ \
hi_bcd_ADC[sum = (A >> 4) + (M >> 4) + lo_bcd_ADC_carry[sum]]; \
if (hi_bcd_ADC_carry[sum]) \
SR = (SR & ~NVZC_msk) ^ NVZC[beforeNZ(A) ^ afterNZ(A2)]; \
else \
SR = (SR & ~NVZC_msk) ^ NVZ[beforeNZ(A) ^ afterNZ(A2)]; \
} \
else \
{ \
A2 = A + M + get_C(); \
if (A2 < A) \
SR = (SR & ~NVZC_msk) ^ NVZC[beforeNZ(A) ^ afterNZ(A2)]; \
else \
SR = (SR & ~NVZC_msk) ^ NVZ[beforeNZ(A) ^ afterNZ(A2)]; \
}
And my lookup tables are :
/*----------------------------------------------------------------------*/
/* Lookup table for ADC in BCD mode. */
/*----------------------------------------------------------------------*/
static BYTE lo_bcd_ADC[32]
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,
0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,
0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05
};
static BYTE lo_bcd_ADC_carry[32]
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01
};
static BYTE hi_bcd_ADC[32]
{
0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,
0x80,0x90,0x00,0x10,0x20,0x30,0x40,0x50,
0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,
0xe0,0xf0,0x00,0x10,0x20,0x30,0x40,0x50
};
static BYTE hi_bcd_ADC_carry[32]
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01
};
static BYTE beforeNZ[256]
{
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02
};
static BYTE afterNZ[256]
{
0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08
};
static BYTE NVZ[16]
{
0x00,0x00,0x40,0x40,
0x02,0x02,0x42,0x42,
0xc0,0xc0,0x80,0x80,
0xc2,0xc2,0x82,0x82
};
static BYTE NVZC[16]
{
0x01,0x01,0x41,0x41,
0x03,0x03,0x43,0x43,
0xc1,0xc1,0x81,0x81,
0xc3,0xc3,0x83,0x83
};
BTW, I have yet still to try this out myself. All of the above code
is sitting in my emulator but is still commented. I will be uncommented
them soon and see how they perform!
--
Stephen Quan (quan@... ), SysAdmin, Analyst/Programmer.
Centre for Spatial Information Studies, University of Tasmania, Hobart.
GPO BOX 252C, Australia, 7001. Local Tel: (002) 202898 Fax: (002) 240282
International Callers use +6102 instead of (002).