/*
   This file is provided under the LGPL license ver 2.1.
   Written by Katsumi.
   http://hp.vector.co.jp/authors/VA016157/
   kmorimatsu@users.sourceforge.jp
*/

#include "compiler.h"

unsigned int g_label;

char* get_label(void){
	unsigned int i;
	char b1;
	int prevpos;
	next_position();
	prevpos=g_srcpos;
	i=0;
	b1=g_source[g_srcpos];
	if ('0'<= b1 && b1<='9') {
		// May be line number
		do {
			i*=10;
			i+=b1-'0';
			g_srcpos++;
			b1=g_source[g_srcpos];
		} while ('0'<= b1 && b1<='9');
		// Check if end of the statement.
		if (i==0 || 65535<i) {
			// Line number 0 or more than 65535 is not available
			g_srcpos=prevpos;
			return ERR_SYNTAX;
		} else if (get_operator()) {
			// Oparator not found.
			g_label=i;
			return 0;
		} else {
			// This is not constant line number.
			g_srcpos=prevpos;
			g_label=0;
			return 0;
		}
	} else if ('A'<=b1 && b1<='Z') {
		// May be label
		do {
			// First character must be A-Z
			// From second, A-Z and 0-9 can be used.
			i*=36;
			if ('0'<=b1 && b1<='9') {
				i+=b1-'0';
			} else if (g_srcpos==prevpos) {
				// First character must be A-Z.
				// Subtract 9, resulting 1-26 but not 10-35.
				// This subtraction is required to maintain
				// final number being <0x80000000.
				i+=b1-'A'+1;
			} else {
				i+=b1-'A'+10;
			}
			g_srcpos++;
			b1=g_source[g_srcpos];
		} while ('0'<= b1 && b1<='9' || 'A'<=b1 && b1<='Z');
		// Length of the label must be less than 7.
		if (6<g_srcpos-prevpos) {
			g_srcpos=prevpos;
			return ERR_LABEL_LONG;
		}
		// Must not be a function
		next_position();
		if (g_source[g_srcpos]=='(') {
			g_srcpos=prevpos;
			g_label=0;
			return 0;
		}
		g_label=i+65536;
		return 0;
	} else {
		g_label=0;
		return 0;
	}
}

void* search_label(unsigned int label){
	unsigned int i,code,search1,search2;
	if (label&0xFFFF0000) {
		// Label
		search1=0x3C160000|((label>>16)&0x0000FFFF); //lui s6,yyyy;
		search2=0x36D60000|(label&0x0000FFFF);       //ori s6,s6,zzzz;
		for(i=0;i<g_objpos;i++){
			code=g_object[i];
			if (code==search1) {
				if (g_object[i+1]==search2) {
					// Label number found
					return &(g_object[i]);
				}
			}
			if (code&0xFFFF0000==0x04110000) {
				// "bgezal zero," assembly found. Skip following block (strig).
				i+=code&0x0000FFFF;
				i--;
			}
		}
		// Line number not found.
		return 0;
	} else {
		// Line number
		search1=0x34160000|label; //ori         s6,zero,xxxx;
		for(i=0;i<g_objpos;i++){
			code=g_object[i];
			if (code==search1) {
				// Line number found
				return &(g_object[i]);
			}
			if (code&0xFFFF0000==0x04110000) {
				// "bgezal zero," assembly found. Skip following block (strig).
				i+=code&0x0000FFFF;
				i--;
			}
		}
		// Line number not found.
		return 0;
	}
}

/*
	Following codes are dedicated to specific use:
	0x0810xxxx, 0x0811xxxx: GOTO statement
	0x0812xxxx, 0x0813xxxx: GOSUB statement
	0x0814xxxx, 0x0815xxxx: SOUND etc, for setting v0 as pointer to DATA array.
*/

char* link(void){
	int pos;
	unsigned int code1,code2,label;
	for(pos=0;pos<g_objpos;pos++){
		code1=g_object[pos];
		switch(code1&0xFFFF0000){
			case 0x08100000:
				// GOTO
				code2=g_object[pos+1];
				if ((code2&0xFFFF0000)!=0x08110000) continue;
				code1&=0x0000FFFF;
				code2&=0x0000FFFF;
				label=(code1<<16)|code2;
				code1=(int)search_label(label);
				g_label=label;
				if (!code1) return ERR_LABEL_NF;
				code1&=0x0FFFFFFF;
				code1>>=2;
				code1|=0x08000000; // j xxxx
				g_object[pos++]=code1;
				g_object[pos]=0x00000000; // nop
				break;
			case 0x08120000:
				// GOSUB
				code2=g_object[pos+1];
				if ((code2&0xFFFF0000)!=0x08130000) continue;
				code1&=0x0000FFFF;
				code2&=0x0000FFFF;
				label=(code1<<16)|code2;
				code2=(int)search_label(label);
				g_label=label;
				if (!code2) return ERR_LABEL_NF;
				code2&=0x0FFFFFFF;
				code2>>=2;
				code2|=0x08000000; // j xxxx
				g_object[pos++]=0x00000000; // nop
				g_object[pos]=code2;
				break;
			case 0x08140000:
				// SOUND etc, for setting v0 as pointer to label/line
				code2=g_object[pos+1];
				if ((code2&0xFFFF0000)!=0x08150000) continue;
				code1&=0x0000FFFF;
				code2&=0x0000FFFF;
				g_label=label;
				label=(code1<<16)|code2;
				code1=(int)search_label(label);
				if (!code1) return ERR_LABEL_NF;
				g_object[pos++]=0x3C020000|((code1>>16)&0x0000FFFF); // lui   v0,xxxx
				g_object[pos]  =0x34420000|(code1&0x0000FFFF);       // ori v0,v0,xxxx
				break;
			default:
				break;
		}
	}
	return 0;
}