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

/* class 1 machine operations processor - 1 byte, no operand field */

class1(int *ip)
{
	check_eol(ip);
	if (pass == LAST_PASS) {
		loadlc(loccnt, 0);
		loadv(loccnt, opval, 0);
		println();
	}
	loccnt++;
}

/* class 2 machine operations processor - 2 byte, relative addressing */

class2(int *ip)
{
	if (pass == LAST_PASS) {
		loadlc(loccnt, 0);
		loadv(loccnt, opval, 0);
		while (isspace(prlnbuf[++(*ip)]));
		if (evaluate(ip, ';')) {
			value -= ((loccnt + 2) + (page << 13));
			if (value >= -128 && value < 128) {
				loadv(loccnt + 1, value, 1);
				println();
			} else
				error("Invalid branch address!");
		}
	}
	loccnt += 2;
}

/* class 3 machine operations processor - 2 byte, inherent addressing */

class3(int *ip)
{
	check_eol(ip);
	if (pass == LAST_PASS) {
		loadlc(loccnt, 0);
		loadv(loccnt, opval, 0);
		loadv(loccnt+1, optype, 1);
		println();
	}
	loccnt += 2;
}

/* class 4 machine operations processor - various addressing modes */

class4(int *ip)
{
	char c;
	int	code;
	int	flag;
	int	i;
	int temp;

	while (isspace(c = prlnbuf[++(*ip)]));
	switch(c) {
	case '\0':
	case ';':
		error("Operand field missing!");
		return;
	case 'A':
	case 'a':
		if ((c = prlnbuf[*ip + 1]) == ' ' || c == 0 || c == '\t') {
			flag = ACC;
			break;
		}
	default:
		switch(c = prlnbuf[*ip]) {
		case '#':
			flag = IMM;
			++(*ip);
			break;
		case '<':
			flag = ZP | ZP_X | ZP_Y;
			++(*ip);
			break;
		case '[':
			flag = ABS_IND | ABS_IND_X | ZP_IND | ZP_IND_X | ZP_IND_Y;
			++(*ip);
			break;
		default:
			flag = ABS | ABS_X | ABS_Y;
		}
		if (!evaluate(ip, 0))
			return;

		/* --  range checking. */

		if (flag & (ZP | ZP_X | ZP_Y | ZP_IND | ZP_IND_X | ZP_IND_Y) & opflg) {
			if ((value & 0xFFFFFF00) && ((value & 0xFFFFFF00) != 0x2000)) {
				error("Zero page index out of range!");
				return;
			}
		} else if (flag & (IMM) & opflg) {
			if ((value > 0xFF) && (value < 0xFFFFFF00)) {
				error("Immediate value out of range!");
				return;
			}
		} else if (flag & (ABS | ABS_X | ABS_Y | ABS_IND | ABS_IND_X) & opflg) {
			if (value & 0xFFFF0000) {
				error("Address out of range!");
				return;
			}
		}
		code = 0;
		while (c = prlnbuf[(*ip)++]) {
			if (c == ';')
				break;
			if (!isspace(c))
				code *= 8;
			switch(c) {
			case ']':		/* ] = 4 */
				++code;
			case ',':		/* , = 3 */
				++code;
			case 'X':		/* X = 2 */
			case 'x':
				++code;
			case 'Y':		/* Y = 1 */
			case 'y':
				++code;
			case ' ':
			case '\t':
				break;
			default:
				flag = 0;
			}
		}
		switch(code) {
		case 0:			/* no termination characters */
			flag &= (ABS | ZP | IMM);
			break;
		case 4:			/* termination = ] */
			flag &= (ZP_IND | ABS_IND);
			break;
		case 25:		/* termination = ,Y */
			flag &= (ABS_Y | ZP_Y);
			break;
		case 26:		/* termination = ,X */
			flag &= (ABS_X | ZP_X);
			break;
		case 212:		/* termination = ,X] */
			flag &= (ZP_IND_X | ABS_IND_X);
			break;
		case 281:		/* termination = ],Y */
			flag &= ZP_IND_Y;
			break;
		default:
			flag = 0;
		}
	}
	opflg &= flag;

	i = 0;
	temp = opflg;
	while (temp >>= 1)
		i++;
	opval += opvaltab[optype][i];

	switch(opflg) {
	case ACC:		/* single byte - class 4 */
		if (pass == LAST_PASS) {
			loadlc(loccnt, 0);
			loadv(loccnt, opval, 0);
			println();
		}
		loccnt++;
		return;
	case IMM:		/* double byte - class 4 */
	case ZP:
	case ZP_X:
	case ZP_Y:
	case ZP_IND:
	case ZP_IND_X:
	case ZP_IND_Y:
		if (pass == LAST_PASS) {
			loadlc(loccnt, 0);
			loadv(loccnt, opval, 0);
			loadv(loccnt+1, value & 0xFF, 1);
			println();
		}
		loccnt += 2;
		return;
	case ABS:		/* triple byte - class 4 */
	case ABS_X:
	case ABS_Y:
	case ABS_IND:
	case ABS_IND_X:
		if (pass == LAST_PASS) {
			loadlc(loccnt, 0);
			loadv(loccnt,   opval, 0);
			loadv(loccnt+1, value, 1);
			loadv(loccnt+2, value >> 8, 2);
			println();
		}
		loccnt += 3;
		return;
	default:
		error("Invalid addressing mode!");
		return;
	}
}

/* class 5 machine operations processor - 3 bytes, zp/relative addressing */

class5(int *ip)
{
	char c;
	int	zp;

	while (isspace(c = prlnbuf[(*ip)++]));
	if (c != '<') {
		error("Invalid addressing mode!");
		return;
	}
	if (!evaluate(ip, ','))
		return;
    zp = value;
	if (!evaluate(ip, ';'))
		return;
	if (pass == LAST_PASS) {
		if ((zp & 0xFFFFFF00) && ((zp & 0xFFFFFF00) != 0x2000))
			error("Zero page index out of range!");
		else {
			loadlc(loccnt, 0);
			loadv(loccnt, opval, 0);
			loadv(loccnt + 1, zp & 0xFF, 1);
			value  -= ((loccnt + 3) + (page << 13));
			if (value >= -128 && value < 128) {
				loadv(loccnt + 2, value, 2);
				println();
			} else
				error("Invalid branch address!");
		}
	}
	loccnt += 3;
}

/* class 6 machine operations processor - 7 bytes, src/dest/length */

class6(int *ip)
{
	char c;
	int	 i, j, temp[3];

    for (i = 0; i < 3; i++) {
		if (!evaluate(ip, 0))
			return;
	    temp[i] = value;
		if (i == 2)
			check_eol(ip);
		else {
			if (prlnbuf[(*ip)++] != ',') {
				error("Syntax error!");
				return;
			}
		}
	}
	if (pass == LAST_PASS) {
	    for (i = 0; i < 3; i++) {
			if (temp[i] & 0xFFFF0000) {
				error("Number out of range!");
				loccnt += 7;
				return;
			}
		}
		loadlc(loccnt, 0);
		loadv(loccnt, opval, 0);
		loadv(loccnt + 1,  temp[0] & 0xFF, 1);
		loadv(loccnt + 2, (temp[0] & 0xFF00) >> 8, 2);
		println(); clearln();
		loadlc(loccnt + 3, 0);
		loadv(loccnt + 3,  temp[1] & 0xFF, 0);
		loadv(loccnt + 4, (temp[1] & 0xFF00) >> 8, 1);
		loadv(loccnt + 5,  temp[2] & 0xFF, 2);
		println(); clearln();
		loadlc(loccnt + 6, 0);
		loadv(loccnt + 6, (temp[2] & 0xFF00) >> 8, 0);
		println();
	}
	loccnt += 7;
}

/* class 7 machine operations processor - TST instruction */

class7(int *ip)
{
	char c;
	int	 code, imm;

	while (isspace(c = prlnbuf[(*ip)++]));
	if (c != '#') {
		error("Invalid addressing mode!");
		return;
	}
	if (!evaluate(ip, ','))
		return;
    imm = value;
	if (prlnbuf[*ip] != '<')
		opval = 0x93;
	else {
		(*ip)++;
		opval = 0x83;
	}
	if (!evaluate(ip, 0))
		return;
	code = 0;
	while (c = prlnbuf[(*ip)++]) {
		if (c == ';')
			break;
		if (!isspace(c))
			code *= 4;
		switch(c) {
		case ',':		/* , = 2 */
			code++;
		case 'X':		/* X = 1 */
		case 'x':
			code++;
			break;
		case ' ':
		case '\t':
			break;
		}
	}
    switch (code) {
	case 0:
		break;
	case 9:
		opval += 0x20;
		break;
	default:
		error("Syntax error!");
		return;
	}
	switch (opval & 0x93) {
	case 0x83:
		if (pass == LAST_PASS) {
			if ((value & 0xFFFFFF00) && ((value & 0xFFFFFF00) != 0x2000))
				error("Zero page index out of range!");
			else if (imm & 0xFFFFFF00)
				error("Immediate value out of range!");
			else {
				loadlc(loccnt, 0);
				loadv(loccnt, opval, 0);
				loadv(loccnt + 1,  imm, 1);
				loadv(loccnt + 2,  value & 0xFF, 2);
				println();
			}
		}
		loccnt += 3;
		break;
	case 0x93:
		if (pass == LAST_PASS) {
			if (value & 0xFFFF0000)
				error("Address out of range!");
			else if (imm & 0xFFFFFF00)
				error("Immediate value out of range!");
			else {
				loadlc(loccnt, 0);
				loadv(loccnt, opval, 0);
				loadv(loccnt + 1, imm, 1);
				loadv(loccnt + 2, value & 0xFF, 2);
				println(); clearln();
				loadlc(loccnt + 3, 0);
				loadv(loccnt + 3, (value & 0xFF00) >> 8, 0);
				println();
			}
		}
		loccnt += 4;
		break;
	}
}

/* class 8 machine operations processor - TAM/TMA instruction */

class8(int *ip)
{
	char c;

	while (isspace(c = prlnbuf[(*ip)++]));
	if (c != '#') {
		error("Invalid addressing mode!");
		return;
	}
	if (!evaluate(ip, ';'))
		return;
	if (pass == LAST_PASS) {
		if (value & 0xFFFFFFF8)
			error("Page index out of range!");
		else {
			loadlc(loccnt, 0);
			loadv(loccnt, opval, 0);
			loadv(loccnt + 1, (1 << value), 1);
			println();
		}
	}
	loccnt += 2;
}

