#include "stdio.h"
#include "assm.d1"
#include "assm.d2"
#include "ctype.h"

extern	int	optab[];
extern	int	step[];

/* translate source line to machine language */

assemble()
{
	int	flg;
	int	i;		/* prlnbuf pointer */

	if ((prlnbuf[SFIELD] == ';') | (prlnbuf[SFIELD] == 0)) {
		if (pass == LAST_PASS)
			println();
		return;
	}
	lablptr = -1;
	i = SFIELD;
	udtype = UNDEF;
	if (colsym(&i) != 0 && (lablptr = stlook()) == -1)
		return;
	while (prlnbuf[++i] == ' ');	/* find first non-space */
	if ((flg = oplook(&i)) < 0) {	/* collect operation code */
		labldef(loccnt);
		if (flg == -1)
			error("Invalid operation code");
		if ((flg == -2) && (pass == LAST_PASS)) {
			if (lablptr != -1)
				loadlc(loccnt, 1, 0);
			println();
		}
		return;
	}
	if (opflg == PSEUDO)
		pseudo(&i);
	else if (labldef(loccnt) == -1)
		return;
	else {
		if (opflg == CLASS1)
			class1();
		else if (opflg == CLASS2)
			class2(&i);
		else class3(&i);
	}
}

/****************************************************************************/

/* printline prints the contents of prlnbuf */

println()
{
	if (lflag > 0)
		{
		if (paglin == pagesize) printhead();
		prlnbuf[linesize] = '\0';
		fprintf(stdout, "%s\n", prlnbuf);
		paglin++ ;
		}
}

/****************************************************************************/

/* printhead prints the page heading   */

printhead()
{
	if (pagesize == 0) return;
	pagect++ ;
	fprintf(stdout, "\f\nAmiga 6502 assembler :  -  %s PAGE %d\n",
	titlbuf,pagect);
	fprintf(stdout, "Line      Location     Label Opcode Operand  Comment   %s\n\n",date);
	paglin = 0;
}

/* colsym() collects a symbol from prlnbuf into symbol[],
 *    leaves prlnbuf pointer at first invalid symbol character,
 *    returns 0 if no symbol collected
 */

colsym(ip)
    int *ip;
{
	int	valid;
	int	i;
	char	ch;

	valid = 1;
	i = 0;
	while (valid == 1) {
		ch = prlnbuf[*ip];
		if (ch == '_' || ch == '.');
		else if (ch >= 'a' && ch <= 'z');
		else if (ch >= 'A' && ch <= 'Z');
		else if (i >= 1 && ch >= '0' && ch <= '9');
		else if (i == 1 && ch == '=');
		else valid = 0;
		if (valid == 1) {
			if (i < SBOLSZ - 1)
				symbol[++i] = ch;
			(*ip)++;
		}
	}
	if (i == 1) {
		switch (symbol[1]) {
		case 'A': case 'a':
		case 'X': case 'x':
		case 'Y': case 'y':
			error("Symbol is reserved (A, X or Y)");
			i = 0;
		}
	}
	symbol[0] = i;
	return(i);
}

/* symbol table lookup
 *	if found, return pointer to symbol
 *	else, install symbol as undefined, and return pointer
 */

stlook()
{
int ptr, ln, eq;
ptr = 0;
while (ptr < nxt_free)
	{
	ln = symbol[0]; if (symtab[ptr] < ln) ln = symtab[ptr];
	if ((eq = strncmp(&symtab[ptr+1], &symbol[1], ln)) == 0 &&
	   symtab[ptr] == symbol[0]) return ptr;
	if (eq > 0) return(stinstal(ptr));
	ptr = ptr+6+ symtab[ptr];
	ptr = ptr +1 + 2*(symtab[ptr] & 0xff);
	}
return (stinstal(ptr));
}


/*  instal symbol into symtab
 */

stinstal(ptr)
int ptr;
{
int ptr2, i;
if (openspc(ptr,symbol[0]+7) == -1) {
	error("Symbol Table Full"); /* print error msg and ...  */
	pass = DONE;		    /* cause termination of assembly */
	return -1; }
ptr2 = ptr;
for (i=0; i< symbol[0]+1; i++)
	symtab[ptr2++] = symbol[i];
symtab[ptr2++] = udtype;
symtab[ptr2+4] = 0;
return(ptr);
}


/*   addref : add a reference line to the  symbol pointed to    */
/*            by ip.                        */

addref(ip)
int ip;
{
int rct, ptr;
rct = ptr =ip + symtab[ip] + 6;
if ((symtab[rct] & 0xff) == 255) {	/* non-fatal error   */
	fprintf(stderr,"%s\n",prlnbuf);
	fprintf(stderr,"Too many references\n");
	return; }
ptr += (symtab[rct] & 0xff) * 2 +1;
if (openspc(ptr,2) == -1) {
	error("Symbol Table Full");
	return -1; }
symtab[ptr] = slnum & 0xff;
symtab[ptr+1] = (slnum >> 8) & 0xff;
symtab[rct]++;
}


/* openspc : open up a space in the symbol table        */
/*           the space will be at (ptr) and will be     */
/*           len characters long. return -1 if no room. */

openspc(ptr,len)
int ptr,len;
{
int ptr2, ptr3;
if (nxt_free + len > size) return -1;
if (ptr != nxt_free)
	{
	ptr2 = nxt_free -1;
	ptr3 = ptr2 + len;
	while (ptr2 >= ptr) symtab[ptr3--] = symtab[ptr2--];
	}
nxt_free += len;
if (lablptr >= ptr) lablptr += len;
return 0;
}


/* operation code table lookup
 *	if found, return pointer to symbol,
 *	else, return -1
 */

oplook(ip)
   int	*ip;
{
register	char	ch;
register	int	i;
register	int	j;
	int	k;
	int	temp[2];

	i = j = 0;
	temp[0] = temp[1] = 0;
	while((ch=prlnbuf[*ip])!= ' ' && ch!= 0 && ch!= '\t' && ch!= ';') {
		if (ch >= 'A' && ch <= 'Z')
			ch &= 0x1f;
		else if (ch >= 'a' && ch <= 'z')
			ch &= 0x1f;
		else if (ch == '.')
			ch = 31;
		else if (ch == '*')
			ch = 30;
		else if (ch == '=')
			ch = 29;
		else return(-1);
		temp[j] = (temp[j] * 0x20) + (ch & 0xff);
		if (ch == 29)
			break;
		++(*ip);
		if (++i >= 3) {
			i = 0;
			if (++j >= 2) {
				return(-1);
			}
		}
	}
	if ((j = temp[0]^temp[1]) == 0)
		return(-2);
	k = 0;
	i = step[k] - 3;
	do {
		if (j == optab[i]) {
			opflg = optab[++i];
			opval = optab[++i];
			return(i);
		}
		else if (j < optab[i])
			i -= step[++k];
		else i += step[++k];
	} while (step[k] != 0);
	return(-1);
}

/* error printing routine */

error(stptr)
   char *stptr;
{
	loadlc(loccnt, 0, 1);
	loccnt += 3;
	loadv(0,0,0);
	loadv(0,1,0);
	loadv(0,2,0);
	fprintf(stderr, "%s\n", prlnbuf);
	fprintf(stderr, "%s\n", stptr);
	errcnt++;
}

/* load 16 bit value in printable form into prlnbuf */

loadlc(val, f, outflg)
    int val;
    int f;
    int outflg;
{
	int	i;

	i = 6 + 7*f;
	hexcon(4, val);
	if (nflag == 0) {
		prlnbuf[i++]  = hex[3];
		prlnbuf[i++]  = hex[4];
		prlnbuf[i++]  = ':';
		prlnbuf[i++]  = hex[1];
		prlnbuf[i] = hex[2];
	}
	else {
		prlnbuf[i++] = hex[1];
		prlnbuf[i++] = hex[2];
		prlnbuf[i++] = hex[3];
		prlnbuf[i] = hex[4];
	}
	if ((pass == LAST_PASS)&&(oflag != 0)&&(objcnt <= 0)&&(outflg != 0))
		{
		if (mflag != 0) start_obj(val);
		else fprintf(optr, "\n;%c%c%c%c", hex[3], hex[4], hex[1], hex[2]);
		objcnt=22;
		}
}



/* load value in hex into prlnbuf[contents[i]] */
/* and output hex characters to obuf if LAST_PASS & oflag == 1 */

loadv(val,f,outflg)
   int	val;
   int	f;		/* contents field subscript */
   int	outflg;		/* flag to output object bytes */
{

	hexcon(2, val);
	prlnbuf[13 + 3*f] = hex[1];
	prlnbuf[14 + 3*f] = hex[2];
	if ((pass == LAST_PASS) && (oflag != 0) && (outflg != 0)) {
		if (mflag != 0) put_obj(val);
		else {	fputc(hex[1], optr);
			fputc(hex[2], optr); }
		--objcnt;
	}
}

/* convert number supplied as argument to hexadecimal in hex[digit] (lsd)
		through hex[1] (msd)		*/

hexcon(digit, num)
    int digit;
   int	num;
{

	for (; digit > 0; digit--) {
		hex[digit] = (num & 0x0f) + '0';
		if (hex[digit] > '9')
			hex[digit] += 'A' -'9' - 1;
		num >>= 4;
	}
}

/* assign <value> to label pointed to by lablptr,
 *	checking for valid definition, etc.
 */

labldef(lval)
    int lval;
{
	int	i;

	if (lablptr != -1) {
		lablptr += symtab[lablptr] + 1;
		if (pass == FIRST_PASS) {
			if (symtab[lablptr] == UNDEF) {
				symtab[lablptr + 1] = lval & 0xff;
				i = symtab[lablptr + 2] = (lval >> 8) & 0xff;
				if (i == 0)
					symtab[lablptr] = DEFZRO;
				else	symtab[lablptr] = DEFABS;
			}
			else if (symtab[lablptr] == UNDEFAB) {
				symtab[lablptr] = DEFABS;
				symtab[lablptr + 1] = lval & 0xff;
				symtab[lablptr + 2] = (lval >> 8) & 0xff;
			}
			else {
				symtab[lablptr] = MDEF;
				symtab[lablptr + 1] = 0;
				symtab[lablptr + 2] = 0;
				error("Label multiply defined");
				return(-1);
			}
		symtab[lablptr+3] = slnum & 0xff;
		symtab[lablptr+4] = (slnum >> 8) & 0xff;
		}
		else {
			i = (symtab[lablptr + 2] << 8) +
				(symtab[lablptr+1] & 0xff);
			i &= 0xffff;
			if (i != lval && pass == LAST_PASS) {
				error("Sync error");
				return(-1);
			}
		}
	}
	return(0);
}

/* determine the value of the symbol,
 * given pointer to first character of symbol in symtab
 */

symval(ip)
    int *ip;
{
	int	ptr;
	int	svalue;

	svalue = 0;
	colsym(ip);
	if ((ptr = stlook()) == -1)
		undef = 1;		/* no room error */
	else if (symtab[ptr + symtab[ptr] + 1] == UNDEF)
		undef = 1;
	else if (symtab[ptr + symtab[ptr] + 1] == UNDEFAB)
		undef = 1;
	else svalue = ((symtab[ptr + symtab[ptr] + 3] << 8) +
		(symtab[ptr + symtab[ptr] + 2] & 0xff)) & 0xffff;
	if (symtab[ptr + symtab[ptr] + 1] == DEFABS)
		zpref = 1;
	if (undef != 0)
		zpref = 1;

	/* add a reference entry to symbol table on first pass only,
	   except for branch instructions (CLASS2) which do not come
	   through here on the first pass                            */
	if (ptr >= 0 && pass == FIRST_PASS) addref(ptr);
	if (ptr >= 0 && opflg == CLASS2) addref(ptr); /* branch addresses */
	return(svalue);
}

/*    object code record generation routines    */
/*    added to generate MOS Technology format   */
/*       object records                         */
/*           By Joel Swank 12/86                */

char obj_rec[60];		/* buffer for object record */
unsigned  obj_ptr = 0;		/* pointer for above  */
unsigned  obj_bytes = 0;		/* count of bytes in current record */
unsigned  rec_cnt = 0;		/* count of records in this file */
unsigned  cksum = 0;			/* record check sum accumulator  */

/*   put one object byte in hex   */

put_obj(val)
unsigned val;
{
	hexcon(2,val);
	obj_rec[obj_ptr++] = hex[1];
	obj_rec[obj_ptr++] = hex[2];
	cksum += (val & 0xff);
	obj_bytes++;
}


/*    start an object record (end previous) */

start_obj(val)
unsigned val; 	/*  current location counter */
{
	prt_obj();	/* print the current record if any */
	hexcon(4,val);
	obj_bytes=0;
	for (obj_ptr=0; obj_ptr<4; obj_ptr++) obj_rec[obj_ptr] = hex[obj_ptr+1];
	cksum = (val>>8) + (val & 0xff);
	rec_cnt++;
}


/*    print the current object record if any */

prt_obj()
{
	if (obj_bytes == 0) return;
	cksum += obj_bytes;
	hexcon(2,obj_bytes);
	obj_rec[obj_ptr] = '\0';
	fprintf(optr,";%c%c%s",hex[1],hex[2],obj_rec);
	hexcon(4,cksum);
	fprintf(optr,"%c%c%c%c\n",hex[1],hex[2],hex[3],hex[4]);
}


/*    finish object file       */

fin_obj()
{
	unsigned i;
	prt_obj();
	hexcon(4,++rec_cnt);
	fprintf(optr,";00");
	for (i=1; i<5; i++) fputc(hex[i],optr);
	rec_cnt = rec_cnt/256 + (rec_cnt & 0xff);
	hexcon(4,rec_cnt);
	for (i=1; i<5; i++) fputc(hex[i],optr);
	fputc('\n',optr);
}
/*     MOS Tech. object format is as follows    */
/*
	( all data is in ASCII encoded hexidecimal)

 Data record : ;nnaaaadddd...xxxx[cr]
 Last record : ;00ccccxxxx[cr]

 Where:
	;	= Start of record (ASCII 3B)
	nn	= Number of data bytes in the record.
		  max = 24 bytes.
	aaaa	= address of first data byte in the record.
	dd	= 1 data byte.
	xxxx	= checksum that is the twos compliment sum of all
		  data bytes, the count byte and the address bytes.
	cccc	= count of records in the file.
	[cr]	= ASCII Carriage Return (ASCII 0D).

*/
