/* z80sim.c
 * Zilog Z80 simulator - TDL ZASM Syntax
 * (c) 2007 Andre Adrian
 *
 * version 20may2007
 *
 * This Z80 simulator is not complete! It only works on a 32bit little endian host like x86.
 * Only Carry, Zero and Sign flags are simulated (Parity/Overflow flag only for CCIR opcode)
 * Simulator has Harvard architecture - different memory for code, data, data-stack, return-stack.
 *		code is plain C - every Z80 opcode is replaced by a C macro
 *		return-stack is plain C function call, return
 *		data is char array m[]
 *		register-stack is REG array s[] together with stack-pointer sp
 *
 * Sargon needs
 * JC, JNC (carry), JZ, JNZ (zero), JP, JM (sign)
 * LDIR, CCIR (compare increment) with CCIR special use of parity flag for JPE jump parity even
 * XCHG, EXAF, EXX
 * DSBC (only carry used?), DADX (IX+DE)
 * RRD, RLD (4-bit shift), RAL
 * BIT, SET, RES
 *
 * no JPO, JO, JNO (jump overflow/parity)
 * no CPO, CPE, CO, CNO (call overflow/parity)
 * no RPO, RPE, RO, RNO (return overflow/parity)
 * no XTHL (SP<->HL), XTIX, XTIY, PCHL (PC<-HL), PCIX, PCIY
 * no DADC
 * no DAA (decimal adjust)
 * no RLC, RRC, RLC, RALR, RLCR
 * no LSPD, SSPD, SPHL, SPIX, SPIY, 
 * no LDI, LDD, LDDR, CCI, CCD, CCDR
 * no ACI, SBB, SBI, ORA, ORI
 * no CMC, STC
 */
#include <stdio.h>

typedef unsigned char r8;	// unsigned for 8bit add, sub, logical shift
typedef signed char s8;		// signed for 8bit arithmetric shift
typedef unsigned short r16;	// for 16bit Z80 registers
typedef unsigned long r32;	// unsigned for 16bit arithmetric

r8	m[4096];	// data
r16	s[2048];	// register-stack

// Z80 is little endian. REG does only work on 32bit little endian hosts like x86
typedef union {
	r8 c[2];
	r16 w;
} REG;

typedef struct {
	int alu;	// result of last arithmetric/logic operation 
				// Zero flag set if alu==0. Sign flag set if (alu&0x80)!=0
	int	cy;		// Carry of last 8bit or 16bit operation. Carry flag set if cy!=0
	int	ov;		// Overflow flag set if CCIR completed with BC!=0
} FLAGS;

REG af, bc, de, hl, ix, iy;		// first register set
FLAGS f;
REG af2, bc2, de2, hl2;			// second register set
FLAGS f2;
REG wz;							// swap helper
FLAGS f3;
int ncy;						// new carry
s8 sr;							// for signed 8bit opcodes (arithmetric shift)
r32 noalu;

int sp = sizeof(s) / sizeof(r16);	// stack pointer

// 8bit Registers
#define A	af.c[0]
#define B	bc.c[1]
#define C	bc.c[0]
#define D	de.c[1]
#define E	de.c[0]
#define H	hl.c[1]
#define L	hl.c[0]
#define M	m[hl.w]

// Flags macros
#define	ALU	f.alu
#define CY	f.cy
#define OV	f.ov

// 16bit Registers
#define PSW	af.w
#define BC	bc.w
#define DE	de.w
#define HL	hl.w
#define X	ix.w
#define Y	iy.w

// 8bit
#define MOV(l,r)	l = r;
#define MVI(l,n)	MOV(l,n)
#define ADD(r)		A = ALU = A + r; CY = ALU & 0x100;
#define ADI(n)		ADD(n)
#define ADC(r)		ALU = A + r; if (CY) ++ALU; A = ALU; CY = ALU & 0x100;
#define SUB(r)		A = ALU = A - r; CY = ALU & 0x100;
#define SUI(n)		SUB(n)
#define CMP(r)		ALU = A - r; CY = ALU & 0x100;
#define CPI(n)		CMP(n)
#define ANA(r)		A = ALU = A & r; CY = 0;
#define ANI(n)		ANA(n)
#define XRA(r)		A = ALU = A ^ r; CY = 0;
#define XRI(n)		XRA(n)
#define INR(r)		ALU = ++r;
#define DCR(r)		ALU = --r;
#define BIT(b,r)	ALU = r & (1<<b);
#define RARR(r)		ncy = r & 1; r>>=1; if (CY) r|=0x80; ALU = r; CY = ncy;
#define RAL			ncy = A & 0x80; A<<=1; if (CY) A|=0x01; ALU = A; CY = ncy;
#define SRAR(r)		CY = r & 1; sr = r; sr>>=1; r = ALU = sr;
#define SRLR(r)		CY = r & 1; r>>=1; ALU = r;
#define SLAR(r)		CY = r & 0x80; r<<=1; ALU = r;

// 16bit
#define LXI(r,n)	r = n;
#define LBCD(a)		C = m[a]; B = m[a+1];
#define LDED(a)		E = m[a]; D = m[a+1];
#define LHLD(a)		L = m[a]; H = m[a+1];
#define LIXD(a)		ix.c[0] = m[a]; ix.c[1] = m[a+1];
#define LIYD(a)		iy.c[0] = m[a]; iy.c[1] = m[a+1];
#define SBCD(a)		m[a] = C; m[a+1] = B;
#define SDED(a)		m[a] = E; m[a+1] = D;
#define SHLD(a)		m[a] = L; m[a+1] = H;
#define SIXD(a)		m[a] = ix.c[0]; m[a+1] = ix.c[1];
#define SIYD(a)		m[a] = iy.c[0]; m[a+1] = iy.c[1];
#define PUSH(r)		s[--sp] = r;
#define POP(r)		r = s[sp++];
#define DAD(r)		noalu = HL + r; HL = (r16)noalu; CY = noalu & 0x10000;
#define DSBC(r)		ALU = HL - r; if (CY) --ALU; HL = ALU; CY = ALU & 0x10000; if (ALU&0x8000) ALU=-1;
#define INX(r)		++r;
#define DCX(r)		--r;

// exchange & block copy/compare
#define swapreg(l,r)	wz = l, l = r, r = wz
#define	XCHG		swapreg(DE, HL);
#define	EXAF		swapreg(PSW, af2.w), f3 = f, f = f2, f2 = f3;
#define EXX			swapreg(BC, bc2), swapreg(DE, de2), swapreg(HL, hl2);
#define LDIR		do {m[DE++] = m[HL++]; } while (--BC);
#define CCIR		do {ALU = (int)A - m[HL++]; } while (--BC || (0 == ALU)), OV = BC;

// jumps
#define JMP(a)		goto a;
#define JMPR(a)		JMP(a)
#define JC(a)		if (CY) goto a;
#define JNC(a)		if (0 == CY) goto a;
#define JZ(a)		if (0 == ALU) goto a;
#define JRZ(a)		JZ(a)
#define JNZ(a)		if (ALU) goto a;
#define JP(a)		if (0 == (ALU & 0x80)) goto a;
#define JM(a)		if (ALU & 0x80) goto a;
#define JPE(a)		if (ov) goto a;
#define DJNZ(a)		if (--B) goto a;

#define CALL(a)		a();
#define	RET			return;


// ***********************************************************
// POSITIVE INTEGER DIVISION
// D = AD / E
// ***********************************************************
void DIVIDE(void) {	
	PUSH	(BC)
	MVI		(B,8)
DD04: SLAR	(D)
	RAL
	SUB		(E)
	JM		(l1)	// .+6
	INR		(D)
	JMPR	(l2)	// .+3
l1:
	ADD		(E)
l2:
	DJNZ	(DD04)
	POP		(BC)
	RET
}

// ***********************************************************
// POSITIVE INTEGER MULTIPLICATION
// AD = D * E, D < 0x80, E < 0x80
// ***********************************************************
void MLTPLY(void) {	
	PUSH	(BC)
	SUB		(A)
	MVI		(B,8)
ML04:	
	BIT		(0,D)
	JRZ		(l1)	// .+3
	ADD		(E)
l1:
//	SRAR	(A)		// adrian
	SRLR	(A)		// adrian
	RARR	(D)
	DJNZ	(ML04)
	POP		(BC)
	RET
}

int main(int argc, char *argv[]) {
	MVI		(A,0x81)
	ANA		(A)
	RARR	(A)
	printf("RARR(0x81) = %x cy = %x ALU = %x\n", A, CY, ALU);
	RARR	(A)
	printf("RARR = %x cy = %x ALU = %x\n", A, CY, ALU);

	MVI		(A,0x81)
	ANA		(A)
	RAL
	printf("RAL(0x81) = %x cy = %x ALU = %x\n", A, CY, ALU);
	RAL
	printf("RAL = %x cy = %x ALU = %x\n", A, CY, ALU);

	MVI		(A,0x81)
	ANA		(A)
	SRAR	(A)
	printf("SRAR(0x81) = %x cy = %x ALU = %x\n", A, CY, ALU);

	MVI		(A,0x81)
	ANA		(A)
	SRLR	(A)
	printf("SRLR(0x81) = %x cy = %x ALU = %x\n", A, CY, ALU);

	XRA		(A)
	DCR		(A)
	printf("DEC(0) = %x cy = %x ALU = %x\n", A, CY, ALU);
	XRA		(A)
	SUI		(1)
	printf("0-1 = %x cy = %x ALU = %x\n", A, CY, ALU);

	MVI		(A,0x80)
	ADI		(0x7F)
	printf("0x80+0x7F = %x cy = %x ALU = %x\n", A, CY, ALU);

	MVI		(A,0x81)
	ADI		(0x7F)
	printf("0x81+0x7F = %x cy = %x ALU = %x\n", A, CY, ALU);

	XRA		(A)
	LXI		(HL,0x8000)
	LXI		(DE,0x7FFF)
	DSBC	(DE)
	printf("0x8000-0x7FFF = %x cy = %x ALU = %x\n", HL, CY, ALU);	

	XRA		(A)
	MOV		(L,A)
	MOV		(H,A)
	LXI		(DE,0x0001)
	DSBC	(DE)
	printf("0x0000-0x0001 = %x cy = %x ALU = %x\n", HL, CY, ALU);	

	MOV		(L,A)
	MOV		(H,A)
	MOV		(E,A)
	MOV		(D,A)
	DSBC	(DE)
	printf("0x0000-0x0000-CY = %x cy = %x ALU = %x\n", HL, CY, ALU);	

	LXI		(HL,0x8000)
	LXI		(DE,0x7FFF)
	DAD		(DE)
	printf("0x8000+0x7FFF = %x cy = %x ALU = %x\n", HL, CY, ALU);

	LXI		(HL,0x8001)
	LXI		(DE,0x7FFF)
	DAD		(DE)
	printf("0x8001+0x7FFF = %x cy = %x ALU = %x\n", HL, CY, ALU);

	MVI		(D,0xAA)
	MVI		(E,0x55)
	printf("D = %x E = %x D*E = %x\t", D, E, D*E);
	CALL	(MLTPLY)
	printf("MLTPLY = %x\n", A*256+D);

	MVI		(D,0x80)
	MVI		(E,0x7f)
	printf("D = %x E = %x D*E = %x\t", D, E, D*E);
	CALL	(MLTPLY)
	printf("MLTPLY = %x\n", A*256+D);

	MVI		(D,0x7f)
	MVI		(E,0x80)
	printf("D = %x E = %x D*E = %x\t", D, E, D*E);
	CALL	(MLTPLY)
	printf("MLTPLY = %x\n", A*256+D);

	MVI		(A,0x38)
	MVI		(D,0x72)
	MVI		(E,0xAA)
	printf("AD = %x E = %x AD/E = %x\t", A*256+D, E, (A*256+D)/E);
	CALL	(DIVIDE)
	printf("DIVIDE = %x\n", D);

	MVI		(A,0x3f)
	MVI		(D,0x80)
	MVI		(E,0x7f)
	printf("AD = %x E = %x AD/E = %x\t", A*256+D, E, (A*256+D)/E);
	CALL	(DIVIDE)
	printf("DIVIDE = %x\n", D);

	MVI		(A,0x3f)
	MVI		(D,0x80)
	MVI		(E,0x80)
	printf("AD = %x E = %x AD/E = %x\t", A*256+D, E, (A*256+D)/E);
	CALL	(DIVIDE)
	printf("DIVIDE = %x\n", D);

	MVI		(A,0x11)
	LXI		(HL,0x0102)
	MOV		(M,A)
	printf("memory m[0x0102] = %02X\n", m[0x0102]);
	printf("reg H = %02X L = %02X\n", H, L);
	ADI		(0xEF)
	JC		(CARRY)
	printf("no carry\n");
	JMP		(ENDE)
CARRY:
	printf("with carry\n");
ENDE:
	return 0;
}