//	Disassembly.c

#include <stdio.h>
#include <string.h>
#include "IC_Errors.h"

#include "Disassembly.h"

//	NOTE: including a C file here :)
#include "Disassembly_data.c"

ushort			i_pc, i_start, i_finish, i_base, i_limit, i_offset = 0;
short			i_msb, i_mflag, i_xflag;
char			**i_mnemonic;
Byte			*i_mode;
extern	Boolean	i_endOfDataB;
Boolean			i_mliB;

#define		DIS_MNEMONIC	\
	i_mnemonic[byte], (int)((i_offset && val >= i_base && val <= i_limit) ? val - i_offset : val)

#define		DIS_L_MNEMONIC	\
	i_mnemonic[byte], (int)((i_offset && lval >= i_base && lval <= i_limit) ? lval - i_offset : lval)

static	short		InstructionSize(Byte byte)
{
	short	sizeS = 1;
	
	if (i_mliB) {
		sizeS++;
		sizeS++;
	} else switch (i_mode[byte]) {
	
		case 1:		/* Implied */
			break;
		case 2:		/* Immediate */
			sizeS++;
			break;
		case 24:	/* Immediate Acc */
			sizeS++;
			if (!i_mflag) {
				sizeS++;
			}
			break;
		case 25:	/* Immediate Index */
			sizeS++;
			if (!i_xflag) {
				sizeS++;
			}
			break;
		case 3:		/* Accumulator */
			break;
		case 4:		/* Zero Page */
			sizeS++;
			break;
		case 5:		/* Zero Page , X */
			sizeS++;
			break;
		case 6:		/* Zero Page , Y */
			sizeS++;
			break;
		case 7:		/* ( Zero Page ) */
			sizeS++;
			break;
		case 8:		/* ( Zero Page , X ) */
			sizeS++;
			break;
		case 9:		/* ( Zero Page ) , Y */
			sizeS++;
			break;
		case 10:	/* Absolute */
			sizeS++;
			sizeS++;
			break;
		case 11:	/* Absolute , X */
			sizeS++;
			sizeS++;
			break;
		case 12:	/* Absolute , Y */
			sizeS++;
			sizeS++;
			break;
		case 13:	/* ( Absolute ) */
			sizeS++;
			sizeS++;
			break;
		case 14:	/* ( Absolute , X ) */
			sizeS++;
			sizeS++;
			break;
		case 15:	/* Relative */
			sizeS++;
			break;
		case 16:	/* Long Relative */
			sizeS++;
			sizeS++;
			break;
		case 17:	/* [ Zero Page ] */
			sizeS++;
			break;
		case 18:	/* [ Zero Page ] , Y */
			sizeS++;
			break;
		case 19:	/* Long Absolute */
			sizeS++;
			sizeS++;
			sizeS++;
			break;
		case 20:	/* Long Absolute , X */
			sizeS++;
			sizeS++;
			sizeS++;
			break;
		case 21:	/* Stack Relative */
			sizeS++;
			break;
		case 22:	/* Stack Relative Indexed */
			sizeS++;
			break;
		case 23:	/* Source , Dest Banks */
			sizeS++;
			sizeS++;
			break;
		default:	/* Illegal */
			break;
	}
	
	return sizeS;
}

#define		ONE_BYTE_VAL		(ushort)(bytesZ[i_pc++ - pc_start] & 0x00FF)
#define		TWO_BYTE_VAL		GetRboShort(*(RboShort *)&bytesZ[i_pc - pc_start]); i_pc += 2
#define		THREE_BYTE_VAL		GetRbo3Bytes(*(Rbo3Byte *)bytesZ[i_pc - pc_start]); i_pc += 3

static	char		*DecodeBytes(char *bytesZ, char *bufZ)
{
	ushort			val;
	long			lval;
	Byte			byte;
	ushort			pc_start = i_pc;
	char			buf2AC[256];
				

	bufZ[0] = 0;
	
	if (i_pc == i_start) {
		sprintf(buf2AC, "\t\torg\t$%4.4hX\r", i_start);
		strcat(bufZ, buf2AC);
	}
	
	if (i_mliB) {		
		sprintf(buf2AC, "%4.4hX:\tdcb\t", i_pc);
		strcat(bufZ, buf2AC);
		
		val = ONE_BYTE_VAL;
		if (val >= 0xc0 && val <= 0xd3)
			val -= 0xc0 - 7;
		else if (val >= 0x80 && val <= 0x82)
			val -= 0x80 - 4;
		else if (val >= 0x40 && val <= 0x41)
			val -= 0x40 - 1;
		else if (val == 0x65)
			val = 3;
		else
			val = 0;
			
		sprintf(buf2AC, "%s\r%4.4hX:\t", prodos[val], i_pc);
		strcat(bufZ, buf2AC);

		val = TWO_BYTE_VAL;
		sprintf(buf2AC, "dcw\t0x%4.4hX\t; space for return value\r", val);

		i_mliB = FALSE;
	} else {
		sprintf(buf2AC, "%4.4hX:\t", i_pc);
		strcat(bufZ, buf2AC);
		
		byte = ONE_BYTE_VAL;
		
		switch (i_mode[byte]) {
		
			case 1:		/* Implied */
				sprintf(buf2AC, "%s\r", i_mnemonic[byte]);
				break;
			case 2:		/* Immediate */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t#$%2.2hX\r", i_mnemonic[byte], val);
				break;
			case 24:	/* Immediate Acc */
				val = ONE_BYTE_VAL;
				if (!i_mflag) {
					val |= ONE_BYTE_VAL << 8;
					sprintf(buf2AC, "%s\t#$%4.4hX\r", i_mnemonic[byte], val);
				} else {
					sprintf(buf2AC, "%s\t#$%2.2hX\r", i_mnemonic[byte], val);
				}
				break;
			case 25:	/* Immediate Index */
				val = ONE_BYTE_VAL;
				if (!i_xflag) {
					val |= ONE_BYTE_VAL << 8;
					sprintf(buf2AC, "%s\t#$%4.4hX\r", i_mnemonic[byte], val);
				} else {
					sprintf(buf2AC, "%s\t#$%2.2hX\r", i_mnemonic[byte], val);
				}
				break;
			case 3:		/* Accumulator */
				sprintf(buf2AC, "%s\ta\r", i_mnemonic[byte]);
				break;
			case 4:		/* Zero Page */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t$%2.2hX\r", i_mnemonic[byte], val);
				break;
			case 5:		/* Zero Page , X */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t$%2.2hX,x\r", i_mnemonic[byte], val);
				break;
			case 6:		/* Zero Page , Y */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t$%2.2hX,y\r", i_mnemonic[byte], val);
				break;
			case 7:		/* ( Zero Page ) */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t($%2.2hX)\r", i_mnemonic[byte], val);
				break;
			case 8:		/* ( Zero Page , X ) */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t($%2.2hX,x)\r", i_mnemonic[byte], val);
				break;
			case 9:		/* ( Zero Page ) , Y */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t($%2.2hX),y\r", i_mnemonic[byte], val);
				break;
			case 10:	/* Absolute */
				val = TWO_BYTE_VAL;
				
				if (byte == 0x20 && (ushort)val == 0xBF00) {	/* Call to MLI */
					i_mliB = TRUE;
					sprintf(buf2AC, "%s\tPRODOS_MLI\t; $BF00\r", i_mnemonic[byte]);
				} else {
					sprintf(buf2AC, "%s\t$%4.4hX\r", DIS_MNEMONIC);
				}
				
				break;
			case 11:	/* Absolute , X */
				val = TWO_BYTE_VAL;
				sprintf(buf2AC, "%s\t$%4.4hX,x\r", DIS_MNEMONIC);
				break;
			case 12:	/* Absolute , Y */
				val = TWO_BYTE_VAL;
				sprintf(buf2AC, "%s\t$%4.4hX,y\r", DIS_MNEMONIC);
				break;
			case 13:	/* ( Absolute ) */
				val = TWO_BYTE_VAL;
				sprintf(buf2AC, "%s\t($%4.4hX)\r", DIS_MNEMONIC);
				break;
			case 14:	/* ( Absolute , X ) */
				val = TWO_BYTE_VAL;
				sprintf(buf2AC, "%s\t($%4.4hX,x)\r", DIS_MNEMONIC);
				break;
			case 15:	/* Relative */
				val = ONE_BYTE_VAL;
				
				if (val > 127)
					val += i_pc - 256;
				else
					val += i_pc;
				
				sprintf(buf2AC, "%s\t$%4.4hX\r", i_mnemonic[byte], val);
				break;
			case 16:	/* Long Relative */
				lval = TWO_BYTE_VAL;
				lval += i_pc;
				lval &= 0xffff;
				sprintf(buf2AC, "%s\t$%4.4X\r", i_mnemonic[byte], lval);
				break;
			case 17:	/* [ Zero Page ] */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t[$%2.2hX]\r", i_mnemonic[byte], val);
				break;
			case 18:	/* [ Zero Page ] , Y */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t[$%2.2hX],y\r", i_mnemonic[byte], val);
				break;
			case 19:	/* Long Absolute */
				lval = THREE_BYTE_VAL;
				sprintf(buf2AC, "%s\t$%6.6X\r", DIS_L_MNEMONIC);
				break;
			case 20:	/* Long Absolute , X */
				lval = THREE_BYTE_VAL;
				sprintf(buf2AC, "%s\t$%6.6X,x\r", DIS_L_MNEMONIC);
				break;
			case 21:	/* Stack Relative */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t$%2.2hX,s\r", i_mnemonic[byte], val);
				break;
			case 22:	/* Stack Relative Indexed */
				val = ONE_BYTE_VAL;
				sprintf(buf2AC, "%s\t($%2.2hX,s),y\r", i_mnemonic[byte], val);
				break;
			case 23:	/* Source , Dest Banks */
				ushort	val2;
				
				val = ONE_BYTE_VAL;
				val2 = TWO_BYTE_VAL;
				sprintf(buf2AC, "%s\t$%2.2hX,$%2.2hX\r", i_mnemonic[byte], val2, val);
				break;
			default:	/* Illegal */
				sprintf(buf2AC, "???\t%2.2hX\r", byte);
				break;
		}
	}
	
	strcat(bufZ, buf2AC);
	
	return bufZ;
}

#define		ASM_kMaxBytes	6
char		s_bufAC[ASM_kMaxBytes];
short		s_bufLenS = 0;

static	char		*DecodeAsm(char curChar, char *bufAC)
{
	char	*returnZ	= NULL;
//	Boolean	doneB		= TRUE;
	
	bufAC[0] = 0;

	if (i_pc < i_finish) {
		Byte	instruct;
		short	instructSizeS;

		s_bufAC[s_bufLenS++] = curChar;

		instruct		= s_bufAC[0];
		instructSizeS	= InstructionSize(instruct);
		
		ASSERT(s_bufLenS <= instructSizeS);

		if (s_bufLenS == instructSizeS) {
			ushort	prev_pc = i_pc;
			short	num_usedS, num_leftS;
			
			returnZ = DecodeBytes(s_bufAC, bufAC);
			num_usedS = i_pc		- prev_pc;
			num_leftS = s_bufLenS	- num_usedS;
			
			ASSERT(num_usedS == instructSizeS);
			ASSERT(num_leftS >= 0);
			
			memmove(s_bufAC, &s_bufAC[num_usedS], num_leftS);
			s_bufLenS = num_leftS;
		} else {
			returnZ = bufAC;
		}
	} else {
		i_endOfDataB = TRUE;
	}
	
	return returnZ;
}

void		ResetDisasm(ushort load_addrS, ulong file_lenS)
{
	i_pc		= i_base = i_start = load_addrS;	
	i_finish	= (ushort)(((ulong)i_start + file_lenS) & 0xFFFF);
	i_mnemonic	= std_mnemonic;
	i_mode		= std_mode;
	i_mflag		= i_xflag = 1;
	i_msb		= 0;
	s_bufLenS	= 0;
	i_mliB		= FALSE;

	i_endOfDataB	= FALSE;
}

OSErr	DeTokenizeAssembly(
	char	*inBufZ,		ushort	inBufSizeS,
	char	*outBufZ,		ushort 	*outBufSizeSP)
{
	OSErr		err = noErr;
	ushort		curBufS;
	char		bufAC[256], *curCharZ;
	ushort		strLenS;
	ulong		inMaxL	= GetPtrSize((Ptr)inBufZ), 
				outMaxL	= GetPtrSize((Ptr)outBufZ);
	
	ASSERT((ulong)inBufSizeS <= inMaxL);
	
	*outBufSizeSP	= 0;
	outBufZ[0]		= 0;
	
	for (curBufS = 0; !err && !i_endOfDataB && curBufS < inBufSizeS; curBufS++) {
		curCharZ	= DecodeAsm(inBufZ[curBufS], bufAC);
		
		if (curCharZ) {
			strLenS 	= strlen(curCharZ);
			
			if (strLenS) {
				err = ASSERT((ulong)(*outBufSizeSP + strLenS) <= outMaxL);
				
				if (!err) {
					*outBufSizeSP += strLenS;
					strcat(outBufZ, curCharZ);
				}
			}
		} else {
			err = eofErr;
		}
	}
	
	return err;
}
