#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "symbol.h"
#include "config.h"


/* external functions */
void			Skipline(void);
void			ReportError(char *filename, short linenr, int errnum);
void			Subroutine_addr(int opc0, int opc);
void			JP_instr(int opc0, int opc);
void			PushPop_instr(int opcode);
void			RotShift_instr(int opcode);
void			BitTest_instr(int opcode);
void			ArithLog8_instr(int opcode);
void			DeclSymGlobal(char *identifier,	unsigned char libtype);
void			DeclSymExtern(char *identifier,	unsigned char libtype);
void			DeclModuleName(void);
void			DefSym(void);
void			ifstatement(enum flag interpret);
void			DEFVARS(void), DEFS(void), ORG(void), _INCLUDE(void), BINARY(void), CALLOZ(void), FPP(void);
void			ADC(void), ADD(void), DEC(void), IM(void), IN(void), INC(void);
void			JR(void), LD(void), OUT(void), RET(void), SBC(void);
void			DEFB(void), DEFC(void),	DEFM(void), DEFW(void),	DEFL(void);
void			RST(void), DEFGROUP(void);

/* local functions */
void			ParseIdent(enum	flag interpret);
void			AND(void), BIT(void), CALL(void), CCF(void), CP(void), CPD(void);
void			CPDR(void), CPI(void), CPIR(void), CPL(void), DAA(void);
void			DI(void), DJNZ(void);
void			EI(void), EX(void), EXX(void), HALT(void);
void			IND(void);
void			INDR(void), INI(void), INIR(void), JP(void);
void			LDD(void), LDDR(void);
void			LDI(void), LDIR(void), NEG(void), NOP(void), OR(void), OTDR(void), OTIR(void);
void			OUTD(void), OUTI(void),	POP(void), PUSH(void), RES(void);
void			RETI(void), RETN(void);
void			RL(void), RLA(void), RLC(void),	RLCA(void), RLD(void), RR(void), RRA(void), RRC(void);
void			RRCA(void), RRD(void);
void			SCF(void), SET(void), SLA(void), SLL(void), SRA(void);
void			SRL(void), SUB(void), XOR(void);
void			DeclExternIdent(void), DeclGlobalIdent(void), ListingOn(void), ListingOff(void);
void			DeclLibIdent(void), DeclGlobalLibIdent(void);
void			IFstat(void), ELSEstat(void), ENDIFstat(void);
void			DeclModule(void);


/* global variables */
extern FILE	       *z80asmfile;
extern enum symbols	sym, GetSym(void);
extern enum flag	listing, writeline, listing_CPY, EOL;
extern char		ident[], line[];
extern unsigned	short	PC;
extern unsigned	char   *codeptr;
extern struct module   *CURRENTMODULE;


typedef	void		(*ptrfunc) ();	/* ptr to function returning void */
typedef	int		(*fptr)	(const void *, const void *);

struct Z80sym {
	char		       *z80mnem;
	ptrfunc			z80func;
};


struct Z80sym Z80ident[] =
{"ADC", ADC,                    /* 0 */
 "ADD",	ADD,
 "AND",	AND,
 "BINARY", BINARY,
 "BIT",	BIT,
 "CALL", CALL,			/* 5 */
 "CALL_OZ", CALLOZ,
 "CCF",	CCF,
 "CP", CP,
 "CPD",	CPD,
 "CPDR", CPDR,			/* 10 */
 "CPI",	CPI,
 "CPIR", CPIR,
 "CPL",	CPL,
 "DAA",	DAA,
 "DEC",	DEC,			/* 15 */
 "DEFB", DEFB,
 "DEFC", DEFC,
 "DEFGROUP", DEFGROUP,
 "DEFINE", DefSym,
 "DEFL", DEFL,			/* 20 */
 "DEFM", DEFM,
 "DEFS", DEFS,
 "DEFVARS", DEFVARS,
 "DEFW", DEFW,
 "DI", DI,			/* 25 */
 "DJNZ", DJNZ,
 "EI", EI,
 "ELSE", ELSEstat,
 "ENDIF", ENDIFstat,
 "EX", EX,			/* 30 */
 "EXX",	EXX,
 "FPP",	FPP,
 "HALT", HALT,
 "IF", IFstat,
 "IM", IM,			/* 35 */
 "IN", IN,
 "INC",	INC,
 "INCLUDE", _INCLUDE,
 "IND",	IND,
 "INDR", INDR,
 "INI",	INI,
 "INIR", INIR,
 "JP", JP,
 "JR", JR,
 "LD", LD,
 "LDD",	LDD,
 "LDDR", LDDR,
 "LDI",	LDI,
 "LDIR", LDIR,
 "LIB",	DeclLibIdent,
 "LSTOFF", ListingOff,
 "LSTON", ListingOn,
 "MODULE", DeclModule,
 "NEG",	NEG,
 "NOP",	NOP,
 "OR", OR,
 "ORG",	ORG,
 "OTDR", OTDR,
 "OTIR", OTIR,
 "OUT",	OUT,
 "OUTD", OUTD,
 "OUTI", OUTI,
 "POP",	POP,
 "PUSH", PUSH,
 "RES",	RES,
 "RET",	RET,
 "RETI", RETI,
 "RETN", RETN,
 "RL", RL,
 "RLA",	RLA,
 "RLC",	RLC,
 "RLCA", RLCA,
 "RLD",	RLD,
 "RR", RR,
 "RRA",	RRA,
 "RRC",	RRC,
 "RRCA", RRCA,
 "RRD",	RRD,
 "RST",	RST,
 "SBC",	SBC,
 "SCF",	SCF,
 "SET",	SET,
 "SLA",	SLA,
 "SLL",	SLL,
 "SRA",	SRA,
 "SRL",	SRL,
 "SUB",	SUB,
 "XDEF", DeclGlobalIdent,
 "XLIB", DeclGlobalLibIdent,
 "XOR",	XOR,
 "XREF", DeclExternIdent
};
size_t			totalz80id = 92;

int			idcmp(const char idptr[], const	struct Z80sym *	symptr)
{
	return strcmp(idptr, symptr->z80mnem);
}

int			SearchId(void)
{
	struct Z80sym	       *foundsym;

	foundsym = (struct Z80sym *) bsearch(ident, Z80ident, totalz80id, sizeof(struct	Z80sym), (fptr)	idcmp);

	if (foundsym ==	NULL)
		return -1;
	else
		return foundsym	- Z80ident;
}


void			ParseIdent(enum	flag interpret)
{
	int			id;

	if ((id	= SearchId()) == -1)
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 10);
	else {
		switch (id) {
			case 34:
				if (interpret == OFF)
					Skipline();	/* skip	current	line until EOL */
				ifstatement(interpret);
				break;

			case 28:	/* 28 =	ELSE */
			case 29:
				(Z80ident[id].z80func) ();	/* 29 =	ENDIF */
				Skipline();
				break;

			default:
				if (interpret == ON)
					(Z80ident[id].z80func) ();
				Skipline();	/* skip	current	line until EOL */
		}
	}
}


void			ListingOn(void)
{
	if (listing_CPY	== ON) {
		listing	= ON;	/* switch listing ON again... */
		writeline = OFF;/* but don't write this line in listing file */
		line[0]	= '\0';
	}
}


void			ListingOff(void)
{
	if (listing_CPY	== ON) {
		listing	= writeline = OFF;	/* but don't write this line in listing file */
		line[0]	= '\0';
	}
}


/* dummy function - not	used */
void			IFstat(void)
{
}


void			ELSEstat(void)
{
	sym = elsestatm;
	writeline = OFF;	/* but don't write this line in listing file */
}


void			ENDIFstat(void)
{
	sym = endifstatm;
	writeline = OFF;	/* but don't write this line in listing file */
}



void			DeclGlobalIdent(void)
{
	do {
		if (GetSym() ==	name)
			DeclSymGlobal(ident, 0);
		else {
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
			return;
		}
	}
	while (GetSym()	== comma);
	if (sym	!= newline)
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
}


void			DeclGlobalLibIdent(void)
{
	if (GetSym() ==	name) {
		DeclModuleName();			/* XLIB	name is	implicit MODULE	name */
		DeclSymGlobal(ident, SYMDEF);
	}
	else {
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
		return;
	}
}


void			DeclExternIdent(void)
{
	do {
		if (GetSym() ==	name)
			DeclSymExtern(ident, 0);	/* Define symbol as extern */
		else {
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
			return;
		}
	}
	while (GetSym()	== comma);
	if (sym	!= newline)
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
}


void			DeclLibIdent(void)
{
	do {
		if (GetSym() ==	name)
			DeclSymExtern(ident, SYMDEF);	/* Define symbol as extern LIB reference */
		else {
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
			return;
		}
	}
	while (GetSym()	== comma);
	if (sym	!= newline)
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
}


void			DeclModule(void)
{
	GetSym();
	DeclModuleName();
}


void			NOP(void)
{
	*codeptr++ = 0;
	++PC;
}

void			HALT(void)
{
	*codeptr++ = 118;
	++PC;
}

void			LDI(void)
{
	*codeptr++ = 237;
	*codeptr++ = 160;
	PC += 2;
}

void			LDIR(void)
{
	*codeptr++ = 237;
	*codeptr++ = 176;
	PC += 2;
}

void			LDD(void)
{
	*codeptr++ = 237;
	*codeptr++ = 168;
	PC += 2;
}

void			LDDR(void)
{
	*codeptr++ = 237;
	*codeptr++ = 184;
	PC += 2;
}

void			CPI(void)
{
	*codeptr++ = 237;
	*codeptr++ = 161;
	PC += 2;
}

void			CPIR(void)
{
	*codeptr++ = 237;
	*codeptr++ = 177;
	PC += 2;
}

void			CPD(void)
{
	*codeptr++ = 237;
	*codeptr++ = 169;
	PC += 2;
}

void			CPDR(void)
{
	*codeptr++ = 237;
	*codeptr++ = 185;
	PC += 2;
}

void			IND(void)
{
	*codeptr++ = 237;
	*codeptr++ = 170;
	PC += 2;
}

void			INDR(void)
{
	*codeptr++ = 237;
	*codeptr++ = 186;
	PC += 2;
}

void			INI(void)
{
	*codeptr++ = 237;
	*codeptr++ = 162;
	PC += 2;
}

void			INIR(void)
{
	*codeptr++ = 237;
	*codeptr++ = 178;
	PC += 2;
}

void			OUTI(void)
{
	*codeptr++ = 237;
	*codeptr++ = 163;
	PC += 2;
}

void			OUTD(void)
{
	*codeptr++ = 237;
	*codeptr++ = 171;
	PC += 2;
}

void			OTIR(void)
{
	*codeptr++ = 237;
	*codeptr++ = 179;
	PC += 2;
}

void			OTDR(void)
{
	*codeptr++ = 237;
	*codeptr++ = 187;
	PC += 2;
}

void			CP(void)
{
	ArithLog8_instr(7);
}

void			AND(void)
{
	ArithLog8_instr(4);
}

void			OR(void)
{
	ArithLog8_instr(6);
}

void			XOR(void)
{
	ArithLog8_instr(5);
}

void			SUB(void)
{
	ArithLog8_instr(2);
}


void			SET(void)
{
	BitTest_instr(192);
}

void			RES(void)
{
	BitTest_instr(128);
}

void			BIT(void)
{
	BitTest_instr(64);
}

void			RLC(void)
{
	RotShift_instr(0);
}

void			RRC(void)
{
	RotShift_instr(1);
}

void			RL(void)
{
	RotShift_instr(2);
}

void			RR(void)
{
	RotShift_instr(3);
}

void			SLA(void)
{
	RotShift_instr(4);
}

void			SRA(void)
{
	RotShift_instr(5);
}

void			SLL(void)
{
	RotShift_instr(6);
}

void			SRL(void)
{
	RotShift_instr(7);
}

void			CPL(void)
{
	*codeptr++ = 47;
	++PC;
}

void			RLA(void)
{
	*codeptr++ = 23;
	++PC;
}

void			RRA(void)
{
	*codeptr++ = 31;
	++PC;
}

void			RRCA(void)
{
	*codeptr++ = 15;
	++PC;
}

void			RLCA(void)
{
	*codeptr++ = 7;
	++PC;
}

void			EXX(void)
{
	*codeptr++ = 217;
	++PC;
}

void			PUSH(void)
{
	PushPop_instr(197);
}

void			POP(void)
{
	PushPop_instr(193);
}


void			RETI(void)
{
	*codeptr++ = 237;
	*codeptr++ = 77;
	PC += 2;
}

void			RETN(void)
{
	*codeptr++ = 237;
	*codeptr++ = 69;
	PC += 2;
}

void			RLD(void)
{
	*codeptr++ = 237;
	*codeptr++ = 111;
	PC += 2;
}

void			RRD(void)
{
	*codeptr++ = 237;
	*codeptr++ = 103;
	PC += 2;
}

void			NEG(void)
{
	*codeptr++ = 237;
	*codeptr++ = 68;
	PC += 2;
}

void			CALL(void)
{
	Subroutine_addr(205, 196);
}

void			JP(void)
{
	JP_instr(195, 194);
}

void			CCF(void)
{
	*codeptr++ = 63;
	++PC;
}

void			SCF(void)
{
	*codeptr++ = 55;
	++PC;
}

void			DI(void)
{
	*codeptr++ = 243;
	++PC;
}

void			EI(void)
{
	*codeptr++ = 251;
	++PC;
}

void			DAA(void)
{
	*codeptr++ = 39;
	++PC;
}
