#include "dis.h"

#define NTSTART 20

char *cur_file = NULL;			/* the file thats open */
int  pre_index = 0;
int  tstart[NTSTART];			/* .trace directive keep locations */
int  tstarti = 0;

VALUE token;

#ifdef AMIGA
unsigned char *d,*f; /* Manx has a bug preventing us from declaring arrays >64K */
extern unsigned char *calloc();
#else
unsigned char d[0x10000];	 	/* The data */
unsigned char f[0x10000];		/* Flags for memory usage */ 
#endif

#define RUNLOC  0x2e0
#define INITLOC 0x2e2

main(argc, argv)
int argc;
char *argv[];
{
	int i;

#ifdef AMIGA
d = calloc(0x10000,1);
f = calloc(0x10000,1);
#endif

	initopts(argc, argv);
	if (npredef > 0) {
		cur_file = predef[0];
		pre_index++;
		yyin = fopen(cur_file, "r");
		if (!yyin)
			crash ("Cant open predefine file");
		get_predef();
	}
	switch (bopt) {
		case 0:
			loadboot();
			break;
		case 1:
			loadfile();
			break;
		case 2:
			c64loadfile();
			break;
		}
	for (i = 0; i<tstarti; i++)
		start_trace(tstart[i], "*PTRACE*");

	dumpitout();

#ifdef AMIGA
free(d);
free(f);
#endif

	exit(0);
}

crash(p)
char *p;
{
	fprintf(stderr, "%s: %s\n", progname, p);
	if (cur_file != NULL)
		fprintf(stderr, "Line %d of %s\n", lineno+1, cur_file);
#ifdef AMIGA
free(d);
free(f);
#endif
	exit(1);
}

get_predef()
{
	int loc;
	char *name;

	for(;;) 
		switch (yylex()) {
		case '\n':
			break;
		case 0:
			return;
		case TSTART:
			if (yylex() != NUMBER) 
				crash(".trace needs a number operand");
			loc = token.ival;
			if (loc > 0x10000 || loc < 0)
				crash("Number out of range");
			if (tstarti == NTSTART) 
				crash("Too many .trace directives");
			tstart[tstarti++] = loc;
			while (yylex() != '\n')
				;
			break;
		case TSTOP:
			if (yylex() != NUMBER) 
				crash(".stop needs a number operand");
			loc = token.ival;
			if (loc > 0x10000 || loc < 0)
				crash("Number out of range");
			f[loc] |= TDONE;
			while (yylex() != '\n')
				;
			break;
		case NUMBER:
			switch (yylex()) {
			case LI:
			case COMMENT:
				while (yylex() != '\n')
					;
				break;
			case '\n':
				break;
			case NAME:
				name = token.sval;
				if (yylex() != EQ) 
					crash("Only EQ and LI supported in defines file");
				if (yylex() != NUMBER)
					crash("EQ operand must be a number");
				loc = token.ival;
				if (loc > 0x10000 || loc < 0)
					crash("Number out of range");
				f[loc] |= NAMED;
				save_name(loc, name); 
				while (yylex() != '\n') 
					;
				break;
			default:
				crash("Invalid line in predef file");
			}
			break;
		default:
			crash("Invalid line in predef file");
		}
}

loadboot()
{
	struct boot_hdr {
		unsigned char flags;
		unsigned char nsec;
		unsigned char base_low;
		unsigned char base_hi;
		unsigned char init_low;
		unsigned char init_hi;
	} bh;

	FILE *fp;
	int base_addr;
	register int i;
	int len;

	fp = fopen(file, "r");
	cur_file = NULL;
	if (!fp) { 
		fprintf(stderr, "Cant open %s\n", file);

#ifdef AMIGA
free(d);
free(f);
#endif

		exit(1);
	}

	if(fread((char *)&bh, sizeof(bh), 1, fp) != 1) 
		crash("Input too short");
	
	base_addr = bh.base_low + (bh.base_hi << 8);
	len = bh.nsec * 128;
	rewind(fp);
	if (fread((char *)&d[base_addr], 1, len, fp) != len) 
		crash("input too short");

	for(i = base_addr; len > 0; len--) 
		f[i++] |= LOADED;

	start_trace(base_addr+6, "**BOOT**");
}


loadfile()
{
	FILE *fp;
	int base_addr;
	int last_addr;
	register int i;
	int had_header;
	int tmp;

	had_header = 0;
	fp = fopen(file, "r");
	cur_file = NULL;
	if (!fp) { 
		fprintf(stderr, "Cant open %s\n", file);

#ifdef AMIGA
free(d);
free(f);
#endif

		exit(1);
	}
	for(;;) {

		i = getc(fp);

		if (i == EOF) {
			if (f[RUNLOC] & LOADED & f[RUNLOC+1]) {
				i = getword(RUNLOC);
				start_trace(i, "**RUN**");
			}
			return;
		}

		i = i | (getc(fp) << 8);
		if (i == 0xffff)  {
			had_header = 1;
			base_addr = getc(fp);
			base_addr = base_addr | (getc(fp) << 8);
			if (base_addr < 0 || base_addr > 0xffff) 
				crash("Invalid base addr in input file");
		} else {
			if (!had_header)
				crash("Invalid header in input file");
			base_addr = i;
		}

		last_addr = getc(fp);
		last_addr = last_addr | (getc(fp) << 8);
		if (last_addr < base_addr || last_addr > 0xffff) 
			crash("Invalid length in input file");

		printf("Load:  %4x -> %4x\n", base_addr, last_addr);
		for(i = base_addr; i <= last_addr; i++) {
			tmp = getc(fp);
			if (tmp == EOF) 
				crash("File too small");
			d[i] = tmp;
			f[i] |= LOADED;
		}

		if (f[INITLOC] & LOADED & f[INITLOC+1])  {
			i = getword(INITLOC);
			start_trace(i, "**INIT**");
		}

		f[INITLOC] &= ~LOADED;
		f[INITLOC+1] &= ~LOADED;
	}

}


c64loadfile()
{
	FILE *fp;
	unsigned int base_addr,i;
	int c;

	fp = fopen(file, "r");
	cur_file = NULL;
	if (!fp) { 
		fprintf(stderr, "Cant open %s\n", file);

#ifdef AMIGA
		free(d);
		free(f);
#endif

		exit(1);
	}

	base_addr = getc(fp);
	i = ( base_addr += ( (unsigned int)getc(fp) << 8 ) );

	while( (c = getc(fp)) != EOF) {
		d[i] = c;
		f[i++] |= LOADED;
		}

	start_trace(base_addr, "**C64BIN**");
}


start_trace(loc, name)
unsigned int loc;
char *name;
{
	printf("Trace: %4x %s\n", loc, name);
	f[loc] |= (NAMED | SREF);
	if (!get_name(loc))
		save_name(loc, name);
	save_ref(0, loc);
	trace(loc);
}
	
trace(addr)
register unsigned int addr;
{
	int opcode;
	register struct info *ip; 
	int operand;
	int istart;

	if (f[addr] & TDONE)
		return;
	else 
		f[addr] |= TDONE;

	istart = addr;
	opcode = getbyte(addr);
	ip = &optbl[opcode];

	if (ip->flag & ILL)
		return;

	f[addr] |= ISOP;

	addr++;

	/* Get the operand */

	switch(ip->nb) {
		case 1:
			break;
		case 2:
			operand = getbyte(addr);
			f[addr++] |= TDONE;
			break;
		case 3:
			operand = getword(addr);
			f[addr++] |= TDONE;
			f[addr++] |= TDONE;
			break;
	}

	/* Mark data references */

	switch (ip->flag & ADRMASK) {
		case IMM:
		case ACC:
		case IMP:
		case REL:
		case IND:
			break;
		case ABS:
			if (ip->flag & (JUMP | FORK))
				break;
			/* Fall into */
		case ABX:
		case ABY:
		case INX:
		case INY:
		case ZPG:
		case ZPX:
		case ZPY:
			f[operand] |= DREF;
			save_ref(istart, operand);
			break;
		default:
			crash("Optable error");
			break;
	}

	/* Trace the next instruction */

	switch (ip->flag & CTLMASK) {
		case NORM:
			trace(addr);
			break;
		case JUMP:
			f[operand] |= JREF;
			save_ref(istart, operand);
			trace(operand);
			break;
		case FORK:
			if (ip->flag & REL) {
				if (operand > 127) 
					operand = (~0xff | operand);
				operand = operand + addr;
				f[operand] |= JREF;
			} else {
				f[operand] |= SREF;
			}
			save_ref(istart, operand);
			trace(operand);
			trace(addr);
			break;
		case STOP:
			break;
		default:
			crash("Optable error");
			break;
	}
}

int
yywrap()
{
	(void)fclose(yyin);
	if (npredef == pre_index) {
		return(1);
	} else  {
		lineno = 0;
		cur_file = predef[pre_index];
		pre_index++;
		yyin = fopen(cur_file, "r");
		if (!yyin) 
			crash("Can't open predefines file");
		return (0);
	}
}
