/* emitter.c */

#include "picasm.h"

void init_code_generator()
{
	asm_current_address = 0;
	max_address_touched = 0;
	min_address_touched = MAXPICSIZE;
	PU_Clear(&ourpic);
	architecture = NOARCH;	/* force a definition of the architecture */
				/* before any code statements		  */
}

void save_picfile(char *filename, char *asmname)
{
	char comment[80];

	if(min_address_touched == MAXPICSIZE)
		error("No output file generated"," as no instructions assembled","","");

	sprintf(comment,"Pic File generated by %s Version %.1f", asmname, VERSION);
	PU_WriteHeader(filename,&ourpic,comment);
	PU_WriteBlock(filename,&ourpic,	min_address_touched, max_address_touched+1);
	PU_WriteTrailer(filename,&ourpic,"");
}

void assemble(int opcode)
{
	if (asm_current_address == INVALIDADDR)
		error("Valid address not set (use org)","","","");

	ourpic.picmemmap[asm_current_address] = (unsigned short) opcode;

	if (asm_current_address > max_address_touched)
		max_address_touched = asm_current_address;

	if (asm_current_address < min_address_touched)
		min_address_touched = asm_current_address;

	++asm_current_address;
}

void emit_fixed(int t)
{
	switch(architecture)
	{
		case PIC14:	switch(t)
				{
					case CLRW:	assemble(0x0100);
							break;
					case NOP:	assemble(0x0000);
							break;
					case CLRWDT:	assemble(0x0064);
							break;
					case RETFIE:	assemble(0x0009);
							break;
					case RETURN:	assemble(0x0008);
							break;
					case SLEEP:	assemble(0x0063);
							break;
					case OPTION:	assemble(0x0062);
							break;
					default:	error("error not implemented"," emitter.c","emit_fixed","pic14");
				}
				break;

		case PIC12:	switch(t)
				{
					case CLRW:	assemble(0x0040);
							break;
					case NOP:	assemble(0x0000);
							break;
					case CLRWDT:	assemble(0x0004);
							break;
					case SLEEP:	assemble(0x0003);
							break;
					case OPTION:	assemble(0x0002);
							break;
					case RETFIE:
					case RETURN:	error("error not implemented"," emitter.c","emit_fixed","illegal instruction for pic12");
					default:	error("error not implemented"," emitter.c","emit_fixed","pic12");
				}
				break;

		default:	error("architecture not defined","","","");
	}
}

void emit_literal(int t, int literal)
{
	switch(architecture)
	{
		case PIC14:	switch(t)
				{
					case DATA:	assemble(literal & 0x3fff);
							break;
					case ADDLW:	assemble(0x3e00 | (literal & 0x00ff));
							break;
					case ANDLW:	assemble(0x3900 | (literal & 0x00ff));
							break;
					case CALL:	assemble(0x2000 | (literal & 0x07ff));
							break;
					case GOTO:	assemble(0x2800 | (literal & 0x07ff));
							break;
					case IORLW:	assemble(0x3800 | (literal & 0x00ff));
							break;
					case MOVLW:	assemble(0x3000 | (literal & 0x00ff));
							break;
					case RETLW:	assemble(0x3400 | (literal & 0x00ff));
							break;
					case SUBLW:	assemble(0x3c00 | (literal & 0x00ff));
							break;
					case XORLW:	assemble(0x3a00 | (literal & 0x00ff));
							break;
					case TRIS:	assemble(0x0060 | (literal & 0x0007));
							break;
					default:	error("error not implemented"," emitter.c","emit_literal","pic14");
				}
				break;

		case PIC12:	switch(t)
				{
					case DATA:	assemble(literal & 0x0fff);
							break;
					case ANDLW:	assemble(0x0e00 | (literal & 0x00ff));
							break;
					case CALL:	assemble(0x0900 | (literal & 0x00ff));
							break;
					case GOTO:	assemble(0x0a00 | (literal & 0x01ff));
							break;
					case IORLW:	assemble(0x0d00 | (literal & 0x00ff));
							break;
					case MOVLW:	assemble(0x0c00 | (literal & 0x00ff));
							break;
					case RETLW:	assemble(0x0800 | (literal & 0x00ff));
							break;
					case XORLW:	assemble(0x0f00 | (literal & 0x00ff));
							break;
					case TRIS:	assemble(0x0000 | (literal & 0x0007));
							break;
					case ADDLW:
					case SUBLW:	error("error not implemented"," emitter.c","emit_literal","illegal instruction pic12");
					default:	error("error not implemented"," emitter.c","emit_literal","pic12");
				}
				break;

		default:	error("architecture not defined","","","");
	}
}

void emit_f_b(int t, int f, int b)
{
	int v;

	switch(architecture)
	{
		case PIC14:	v = (f & 0x7f) | ((b & 0x07) << 7);

				switch(t)
				{
					case BCF:	assemble(0x1000 | v);
							break;
					case BSF:	assemble(0x1400 | v);
							break;
					case BTFSC:	assemble(0x1800 | v);
							break;
					case BTFSS:	assemble(0x1c00 | v);
							break;
					default:	error("error not implemented"," emitter.c","emit_f_b","pic14");
				}
				break;

		case PIC12:	v = (f & 0x1f) | ((b & 0x07) << 5);

				switch(t)
				{
					case BCF:	assemble(0x0400 | v);
							break;
					case BSF:	assemble(0x0500 | v);
							break;
					case BTFSC:	assemble(0x0600 | v);
							break;
					case BTFSS:	assemble(0x0700 | v);
							break;
					default:	error("error not implemented"," emitter.c","emit_f_b","pic12");
				}
				break;

		default:	error("architecture not defined","","","");
	}
}

void emit_f_d(int t, int f, int d)
{
	int v;

	switch(architecture)
	{
		case PIC14:	v = (f & 0x7f) | ((d | 0x01) << 7);

				switch(t)
				{
					case ADDWF:	assemble(0x0700 | v);
							break;
					case ANDWF:	assemble(0x0500 | v);
							break;
					case CLRF:	assemble(0x0180 | v);
							break;
					case COMF:	assemble(0x0900 | v);
							break;
					case DECF:	assemble(0x0300 | v);
							break;
					case DECFSZ:	assemble(0x0b00 | v);
							break;
					case INCF:	assemble(0x0a00 | v);
							break;
					case INCFSZ:	assemble(0x0f00 | v);
							break;
					case IORWF:	assemble(0x0400 | v);
							break;
					case MOVF:	assemble(0x0800 | v);
							break;
					case MOVWF:	assemble(0x0080 | v);
							break;
					case RLF:	assemble(0x0d00 | v);
							break;
					case RRF:	assemble(0x0c00 | v);
							break;
					case SUBWF:	assemble(0x0200 | v);
							break;
					case SWAPF:	assemble(0x0e00 | v);
							break;
					case XORWF:	assemble(0x0600 | v);
							break;
					default:	error("error not implemented"," emitter.c","emit_f_d","pic14");
				}
				break;

		case PIC12:	v = (f & 0x1f) | ((d | 0x01) << 5);

				switch(t)
				{
					case ADDWF:	assemble(0x01c0 | v);
							break;
					case ANDWF:	assemble(0x0140 | v);
							break;
					case CLRF:	assemble(0x0060 | v);
							break;
					case COMF:	assemble(0x0240 | v);
							break;
					case DECF:	assemble(0x00c0 | v);
							break;
					case DECFSZ:	assemble(0x02c0 | v);
							break;
					case INCF:	assemble(0x0280 | v);
							break;
					case INCFSZ:	assemble(0x03c0 | v);
							break;
					case IORWF:	assemble(0x0100 | v);
							break;
					case MOVF:	assemble(0x0200 | v);
							break;
					case MOVWF:	assemble(0x0020 | v);
							break;
					case RLF:	assemble(0x0340 | v);
							break;
					case RRF:	assemble(0x0300 | v);
							break;
					case SUBWF:	assemble(0x0080 | v);
							break;
					case SWAPF:	assemble(0x0380 | v);
							break;
					case XORWF:	assemble(0x0180 | v);
							break;
					default:	error("error not implemented"," emitter.c","emit_f_d","pic12");
				}
				break;

		default:	error("architecture not defined","","","");
	}
}

void forwardjump(int typeofjump)
{
	SYMBOL *ptr;

	ptr = lookup(current_symbol.lexptr);

	forjump[current_forwardjump].typeofjump	= typeofjump;
	forjump[current_forwardjump].label	= ptr;
	forjump[current_forwardjump].address	= asm_current_address;
	forjump[current_forwardjump].lineno	= lexstack[last_lexlevel].lineno;
	strcpy(forjump[current_forwardjump].filename, lexstack[last_lexlevel].filename);

	current_forwardjump++;
	if (current_forwardjump > MAXFORWARDJUMPS)
		error("Too many forward jumps!","","","");
}

void fillinjumps(int lasttime)
{
	int p;
	int save_asmaddr;

	save_asmaddr = asm_current_address;

	for(p=0;p<current_forwardjump;p++)
	{
		if (forjump[p].typeofjump)
		{
			if(forjump[p].label->type == VALUE_UNDEF)
			{
				if (lasttime)
				{
					lexstack[last_lexlevel].lineno = forjump[p].lineno;
					strcpy(lexstack[last_lexlevel].filename, forjump[p].filename);
					error("Label \"",forjump[p].label->lexptr,"\" not defined","");
				}
			}
			else
			{
				asm_current_address = forjump[p].address;
				emit_literal(forjump[p].typeofjump, forjump[p].label->value);
				forjump[p].typeofjump = FALSE;	/* mark as filled */
			}
		}
	}
	asm_current_address = save_asmaddr;
}

/* ... The End ... */
