#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "defs.h"
#include "externs.h"

char filename[128];

/* pseudo operations processor */

pseudo(int *ip)
{
	switch(opval) {
	case 0:				/*  .db */
		do_db(ip);
		break;
	case 1:				/*  .dw */
		do_dw(ip);
		break;
	case 2:				/*  .equ / = */
		do_assign(ip);
		break;
	case 3:				/*  .page */
		do_page(ip);
		break;
	case 4:				/*  .org */
		do_org(ip);
		break;
	case 5:				/*  .bank */
		do_bank(ip);
		break;
	case 6:				/*  .incbin */
		do_incbin(ip);
		break;
	case 7:				/*  .include */
		do_include(ip);
		break;
	case 8:				/*  .incchr/spr/pal */
	case 9:
	case 10:
		do_pcx(ip);
		break;
	case 11:			/*  .macro */
		do_macro(ip);
		break;
	case 12:			/*  .endm */
		do_endm(ip);
		break;
	case 13:			/*  .list */
		check_eol(ip);
		clist = 1;
		xlist = 1;
		break;
	case 14:			/*  .mlist */
		check_eol(ip);
		mlist = 1;
		break;
	case 15:			/*  .nolist */
		check_eol(ip);
		clist = 0;
		break;
	case 16:			/*  .nomlist */
		check_eol(ip);
		mlist = 0;
		break;
	case 17:			/*  .rsset */
		do_rsset(ip);
		break;
	case 18:			/*  .rs */
		do_rs(ip);
		break;
	}
}

/* .db pseudo */

do_db(int *ip)
{
	char c;
	int	count;
	int	i;
	int	tvalue;

	labldef(loccnt, 1);
	loadlc(loccnt, 0);
	while (isspace(prlnbuf[++(*ip)]));	/*    field	*/
	count = 0;
	do {
		if (prlnbuf[*ip] == '"') {
			while ((tvalue = prlnbuf[++(*ip)]) != '"') {
				if (tvalue == 0) {
					error("Unterminated ASCII string!");
					return;
				}
				if (tvalue == '\\')
					switch(tvalue = prlnbuf[++(*ip)]) {
					case 'r':
						tvalue = '\r';
						break;
					case 'n':
						tvalue = '\n';
						break;
					case 't':
						tvalue = '\t';
						break;
					}
				loccnt++;
				if (pass == LAST_PASS) {
					loadv(loccnt-1, tvalue, count);
					if (++count >= 3) {
						println();
						clearln();
						count = 0;
						loadlc(loccnt, 0);
					}
				}
			}
			++(*ip);
		}
		else {
			if (!evaluate(ip, 0)) {
				loccnt++;
				return;
			}
			loccnt++;
			if (value > 0xFF) {
				error("Operand field size error!");
				return;
			}
			else if (pass == LAST_PASS) {
				loadv(loccnt-1, value, count);
				if (++count >= 3) {
					println();
					clearln();
					count = 0;
					loadlc(loccnt, 0);
				}
			}
		}
	} while ((c = prlnbuf[(*ip)++]) == ',');
	if (c != ';' && c != '\0')
		error("Syntax error!");
	if ((pass == LAST_PASS) && (count != 0))
		println();
}

/* .dw pseudo */

do_dw(int *ip)
{
	char c;
	int	i;

	labldef(loccnt, 1);
	loadlc(loccnt, 0);
	do {
		if (!evaluate(ip, 0)) {
			loccnt += 2;
			return;
		}
		loccnt += 2;
		if (pass == LAST_PASS) {
			loadv(loccnt-2, value, 0, 1);
			loadv(loccnt-1, value>>8, 1, 1);
			println();
			clearln();
			loadlc(loccnt, 0);
		}
	} while ((c = prlnbuf[(*ip)++]) == ',');
	if (c != ';' && c != '\0')
		error("Syntax error!");
}

/* = pseudo */

do_assign(int *ip)
{
	if (!evaluate(ip, ';'))
		return;
	labldef(value, 0);
	if (pass == LAST_PASS) {
		loadlc(value, 1);
		println();
	}
}

/* .page pseudo */

do_page(int *ip)
{
	labldef(loccnt, 1);
	if (!evaluate(ip, ';'))
		return;
	if (value < 0) {
		error("Page index out of range!");
		return;
	} else if (value < 8) {
		page = value;
	} else {
		if (value & 0x7FFF1FF8) {
			error("Invalid page address!");
			return;
		}
		page = value >> 13;
	}
	if (pass == LAST_PASS) {
		loadlc(page << 13, 1);
		println();
	}
}

/* .org pseudo */

do_org(int *ip)
{
	if (!evaluate(ip, ';'))
		return;
	if (undef != 0) {
		error("Undefined symbol in operand field!");
		return;
	}
	if (value & 0xFFFF0000) {
		error("Address out of range!");
		return;
	}
	page   = (value >> 13) & 0x07;
	value &= 0x1FFF;
	loccnt = value;
	labldef(value, 1);
	if (pass == LAST_PASS) {
		loadlc(loccnt | (page << 13), 1);
		println();
	}
}

/* .bank pseudo */

do_bank(int *ip)
{
	labldef(loccnt, 1);
	if (!evaluate(ip, ';'))
		return;
	if ((value < 0) || (value > 127)) {
		error("Bank index out of range!");
		return;
	}
	bank_offset[bank] = loccnt;
	bank_page[bank] = page;
	bank = value;
	page = bank_page[bank];
	loccnt = bank_offset[bank];
	glablptr = NULL;
	if (pass == LAST_PASS) {
		loadlc(bank, 1);
		println();
	}
}

/* .incbin pseudo */

do_incbin(int *ip)
{
	FILE *f;
	int size;
	int i;

	labldef(loccnt, 1);
	if (!get_filename(ip))
		return;
	if ((f = fopen(filename, "rb")) == NULL) {
		error("Can not open file!");
		return;
	}
	fseek(f, 0, SEEK_END);
	size = ftell(f);
	fseek(f, 0, SEEK_SET);
	if (((bank << 13) + loccnt + size) > (8192 * 128)) {
		fclose(f);
		error("File too big!");
		return;
	}
	if (pass == LAST_PASS) {
		fread(&rom[bank][loccnt], 1, size, f);
		loadlc(loccnt, 0);
		println();
	}
	fclose(f);
	bank  += (loccnt + size) >> 13;
	loccnt = (loccnt + size) & 0x1FFF;
	if (bank > max_bank) {
		if (loccnt)
			max_bank = bank;
		else
			max_bank = bank - 1;
	}
}

/* .include pseudo */

do_include(int *ip)
{
	labldef(loccnt, 1);
	if (!get_filename(ip))
		return;
	if (pass == LAST_PASS)
		println();
	if (open_input(filename) == -1) {
		error("Can not open file!");
		return;
	}
}

/* .rsset pseudo */

do_rsset(int *ip)
{
	labldef(loccnt, 1);
	if (!evaluate(ip, ';'))
		return;
	if (value & 0xFFFF0000) {
		error("Address out of range!");
		return;
	}
	rsbase = value;
	if (pass == LAST_PASS) {
		loadlc(rsbase, 1);
		println();
	}
}

/* .rs pseudo */

do_rs(int *ip)
{
	labldef(rsbase, 0);
	if (!evaluate(ip, ';'))
		return;
	if (value & 0xFFFFFF00) {
		error("Size out of range!");
		return;
	}
	if (pass == LAST_PASS) {
		loadlc(rsbase, 1);
		println();
	}
	rsbase += value;
}

/* get a file name from prlnbuf */

get_filename(int *ip)
{
	char c;
	int i;
	char conv[128];
	char *p, *q;

	while (isspace(prlnbuf[++(*ip)]));
	if (prlnbuf[(*ip)++] != '\"') {
		error("Invalid file name!");
		return (0);
	}
	i = 0;
	while (((c = prlnbuf[(*ip)++]) != '\"') && (i < 128))
		filename[i++] = c;
	if ((i == 128) || (i == 0)) {
		error("Invalid file name!");
		return (0);
	}
	filename[i] = '\0';

        if (!(p = strrchr(filename, '.'))) return(1);
        *(p++) = '\0';
        q = strrchr(filename, '.');
        if (!q) q = strrchr(filename, ':');
        if (q) {
            q++;
            strncpy(conv, filename, q - filename);
            strcat(conv, p);
            strcat(conv, q);
        } else {
            strcpy(conv, p);
            strcat(conv, ".");
            strcat(conv, filename);
        }
        strcpy(filename, conv);

	return (1);
}

