It was thus said that the Great Richard A. Cini, Jr. once stated:
Hello, all:
Futher errors have been found in the Altair32 8080 processor emulator code
by some eagle-eyed users. So, I'm trying to fix them, but my lack of depth
in C programming has become evident. So, I'd like to enlist some help again.
Here's the code (cut and paste; written by Claus Giloi):
Okay, as one of the few token C programmers here, I'll take a shot at
this. I'll give several solutions of varying amount of portability and
speed, so pick wisely 8-) The code is ANSI C, which was passed in 1989,
making it on topic for this group 8-P
uchar regs[8] ; // A, F, B, C, D, E, H, L - actual
storage order!
// 0 1 2 3 4 5 6 7
// pointer array for sss, ddd addressing
uchar * b_regs[8]=
{®s[2],®s[3],®s[4],®s[5],®s[6],®s[7],&dummy,®s[0]}
;
// B C D E H L trap! ACC
// pointer array for rp addressing
ushort * w_regs[4] =
{ (ushort *)®s[2], (ushort *)®s[4], (ushort *)®s[6], &SP } ;
// B D H SP
The following assumes an 8-bit character, 2 character in a short (most
modern machines), either big endian or little endian (you need to define
LITTLE_END):
#include <limits.h>
/*---------------------------------------------
; if Intel based, set to 1, otherwise (say, for
; Motorola systems) set to 0
;---------------------------------------------*/
#define LITTLE_END 1
#if CHAR_BIT != 8
# error No 8bit bytes supported on this system
#else
typedef unsigned char byte;
#endif
#if (USHRT_MAX != 65535UL)
# error No 16bit type supported on this system
#else
typedef unsigned short word;
#endif
union defregs
{
byte b[2];
word w;
} regs[4]; /* AF , BC, DE, HL */
#if LITTLE_END
# define A regs[0].1[1]
# define F regs[0].l[0]
# define B regs[1].l[1]
# define C regs[1].l[0]
# define D regs[2].l[1]
# define E regs[2].l[0]
# define H regs[3].l[1]
# define L regs[3].l[0]
#else
# define A regs[0].l[0]
# define F regs[0].l[1]
# define B regs[1].l[0]
# define C regs[1].l[1]
# define D regs[2].l[0]
# define E regs[2].l[1]
# define H regs[3].l[0]
# define L regs[3].l[1]
#endif
#define BC regs[1].w
#define DE regs[2].w
#define HL regs[3].w
You now have an easier way to reference the registers, the following
code:
switch ((u&RP_MASK)>>4){ // RP_MASK
isolates register pairs
case 2: //HL
tmpH = *pH ; // pointer to H storage
tmpL = tmpL2 = *pL ;
tmpL++;
if (((tmpL & 0xff) == 0) && (tmpL2 == 0xff)) tmpH++ ;
*pH = tmpH ; // save regs
*pL = tmpL ;
break ;
// other cases omitted.
}
Can be replaced with:
switch((u & RP_MASK) >> 4)
{
case 2: /* HL */
HL++;
break;
/* other cases */
}
To increment a register pair and check for carry (say, BC):
unsigned long tmp;
tmp = BC;
tmp++;
if (tmp > USHRT_MAX)
{
/* set carry flag */
}
BC = tmp & 0xFFFFul;
The more portable, but slower method (and not dependant upon endianness)
is:
#include <limits.h>
typedef unsigned char byte;
byte regs[8];
#define A regs[0]
#define F regs[1]
#define B regs[2]
#define C regs[3]
#define D regs[4]
#define E regs[5]
#define H regs[6]
#define L regs[7]
#define rBC ( ((B) << 8) | (C) )
#define rDE ( ((D) << 8) | (E) )
#define rHL ( ((H) << 8) | (L) )
/*------------------------------------------------------------
; The following macros evaluate the parameter more than once.
; be careful on how you use these. Beware of side effects!
;------------------------------------------------------------*/
#define wBC(w) (B) = ((w) >> 8) & 0xFF) ; (C) = ((w) & 0xFF)
#define wDE(w) (D) = ((w) >> 8) & 0xFF) ; (E) = ((w) & 0xFF)
#define wHL(w) (H) = ((w) >> 8) & 0xFF) ; (L) = ((w) & 0xFF)
switch((u & RP_MASK) >> 4)
{
unsigned int tmp;
case 2: /* HL */
tmp = rHL + 1;
wHL(tmp);
break;
/* other cases */
}
/*-------------------------------------
; That carry thang ...
;--------------------------------------*/
unsigned long tmp;
tmp = BC;
tmp++;
if (tmp > USHRT_MAX)
{
/* set carry flag */
}
wBC(tmp);
Sigh. I've been programming in C for waaaaay too long 8-)
-spc (Sorry about the C code ... )