package com.violet.v2.vasm;

import java.io.*;

public final class Vasm {

// class BinVasm : buffer de stockage du binaire produit
class BinVasm {
	int bc_max;
	int bc_ind;
	byte bc[];

public BinVasm()
{
	bc_max=1024;
	bc_ind=0;
	bc=new byte[1024];
}
public boolean isempty()
{
	if (bc_ind!=0) return false;
	return true;
}
public void empty()
{
	bc_ind=0;
}
public void add(int b)
{
	bc[bc_ind++]=(byte)b;
	if (bc_ind>=bc_max)
	{
		int nmax=bc_max*2;
		byte nbc[]=new byte[nmax];
		int i; for(i=0;i<bc_max;i++) nbc[i]=bc[i];
		bc_max=nmax;
		bc=nbc;
	}
}
public int index()
{
	return bc_ind;
}
public String get()
{
	try {
	return new String(bc,0,bc_ind,"ISO-8859-1");
    } catch (Throwable t) {
    }
	return "";	
}
public byte remove()
{
	if (bc_ind>0)
	{
		bc_ind--;
		return bc[bc_ind];
	}
	return 0;
}
public void set(int ind,int val)
{
	bc[ind]=(byte)val;
}
public void setw(int ind,int val)
{
	set(ind,(val>>8)&255);
	set(ind+1,val&255);
}

}
// fin de la classe BinVasm

// classe Asso : gestion des Equ et Labels
class AssoVasm {

class AssoNode {
	String label;
	String value;
	int ivalue;
	AssoNode next;
public AssoNode(String l,String v,int i,AssoNode n)
{
	label=l;
	value=v;
	ivalue=i;
	next=n;
}
public String find(String l)
{
	AssoNode p=this;
	while(p!=null)
	{
		if (l.compareTo(p.label)==0) return p.value;
		p=p.next;
	}
	return null;
}
public int findi(String l)
{
	AssoNode p=this;
	while(p!=null)
	{
		if (l.compareTo(p.label)==0) return p.ivalue;
		p=p.next;
	}
	return -1;
}
}

AssoNode equ_list;

public AssoVasm()
{
	equ_list=null;
}
public void add(String l,String v,int i)
{
	equ_list=new AssoNode(l,v,i,equ_list);
}
public String find(String l)
{
	if (equ_list==null) return null;
	return equ_list.find(l);
}
public int findi(String l)
{
	if (equ_list==null) return -1;
	return equ_list.findi(l);
}

}
// fin de la classe Asso

// classe Links
class Links {
	String label;
	int value;
	Links next;
public Links(String l,int i,Links n)
{
	label=l;
	value=i;
	next=n;
}
public String label()
{
	return label;
}
public int value()
{
	return value;
}
public Links next()
{
	return next;
}
}


String vasm_error;
BinVasm bin;
int line;

void add_error(String msg)
{
	vasm_error+="line "+line+" / "+msg+"\n";
}

String itoa(int i)
{	
	return String.valueOf(i);
}

int atoi(String s)
{	
	if (s==null) return 0;
	s=s.trim();
	if (s.length()==0) return 0;
	if ((s.startsWith("0x"))||(s.startsWith("0X")))
	{
		return Integer.parseInt(s.substring(2),16);
	}
	Integer y=new Integer(s);
	return y.intValue();
}

int htoi(String s)
{
	int i;
	int res=0;
	for(i=0;i<s.length();i++)
	{
		res<<=4;
		byte c=(byte)s.charAt(i);
		if ((c>='0')&&(c<='9')) res+=c-'0';
		if ((c>='a')&&(c<='f')) res+=c-'a'+10;
		if ((c>='A')&&(c<='F')) res+=c-'A'+10;
	}
	return res;
}

String hexfromint(int v)
{
	byte b[]=new byte[1];
	b[0]=(new Integer(v)).byteValue();
	if (v<10) b[0]+='0';
	else b[0]+='a'-10;
	return new String(b);
}
String hex2fromint(int v)
{
	return hexfromint((v>>4)&15)+hexfromint(v&15);
}
String hex4fromint(int v)
{
	return hexfromint((v>>12)&15)+hexfromint((v>>8)&15)+hexfromint((v>>4)&15)+hexfromint(v&15);
}


void setbyte(byte [] tab,int ind,int v)
{
	v&=255;
	tab[ind]=(byte)v;
}


public String itobin4(int v)
{
	try {
		byte [] b=new byte[4];
		setbyte(b,0,v>>24);
		setbyte(b,1,v>>16);
		setbyte(b,2,v>>8);
		setbyte(b,3,v);
		return new String(b,"ISO-8859-1");
	} catch (Throwable t) {
	}
	return "xxxx";
}
public String itobin3(int v)
{
	try {
		byte [] b=new byte[3];
		setbyte(b,0,v>>16);
		setbyte(b,1,v>>8);
		setbyte(b,2,v);
		return new String(b,"ISO-8859-1");
	} catch (Throwable t) {
	}
	return "xxxx";
}
public String itobin2(int v)
{
	try {
		byte [] b=new byte[2];
		setbyte(b,0,v>>8);
		setbyte(b,1,v);
		return new String(b,"ISO-8859-1");
	} catch (Throwable t) {
	}
	return "xxxx";
}
public String itobin1(int v)
{
	try {
		byte [] b=new byte[1];
		setbyte(b,0,v);
		return new String(b,"ISO-8859-1");
	} catch (Throwable t) {
	}
	return "xxxx";
}
String getrealpath(String path,String filename)
{
//	System.out.println(path+" - "+filename);
	
	if (path==null) return filename;
	if (filename.charAt(0)=='/') return filename;
	if (filename.charAt(1)==':') return filename;
//	System.out.println(path+"/"+filename);
	return path+"/"+filename;
}
String getfile(String filename,String path)
{
	try{
		File f=new File(getrealpath(path,filename));
		int length=(int)f.length();
		byte[] buf= new byte[length];
		FileInputStream fr= new FileInputStream(f);
		fr.read(buf);
		fr.close();
		return new String(buf,"ISO-8859-1");
    } catch (Throwable t) {
    }
	return null;
}
byte[] getfilebytes(String filename,String path)
{
	try{
		File f=new File(getrealpath(path,filename));
		int length=(int)f.length();
		byte[] buf= new byte[length];
		FileInputStream fr= new FileInputStream(f);
		fr.read(buf);
		fr.close();
		return buf;
    } catch (Throwable t) {
    }
	return null;
}
String opcodes[]=
{"LD_ri","","","","","","","",
"","","","","","","","",
"ADD_ri","","","","","","","",
"","","","","","","","",
"SUB_ri","","","","","","","",
"","","","","","","","",
"AND_ri","","","","","","","",
"","","","","","","","",
"OR_ri","","","","","","","",
"","","","","","","","",
"LDR_ri","","","","","","","",
"","","","","","","","",
"STR_ri","","","","","","","",
"","","","","","","","",
"NOP_o","TRANSITION_o","RTI_o","CLRCC_o","SETCC_o","ADDCC_rr","SUBCC_rr","INCW_rr",
"DECW_rr","MULW_rr","INPUTRST_r","INT_r","ACK_r","WAIT_r","WAIT_i","RND_r",
"DEC_r","INC_r","CLR_r","NEG_r","NOT_r","TST_r","LD_rr","ADD_rr",
"SUB_rr","MUL_rr","AND_rr","OR_rr","EOR_rr","LSL_rr","LSR_rr","ASR_rr",
"ROL_rr","ROR_rr","CMP_rr","BIT_rr","LDR_rr","LDR_rir","STR_rr","STR_rir",
"LDT_rrw","LDTW_rw","INPUT_rw","RTIJ_w","BRA_w","BEQ_w","BNE_w","BGT_w",
"BGE_w","BLT_w","BLE_w","BHI_w","BHS_w","BLO_w","BLS_w","LED_rr",
"PALETTE_rr","ECHO_r","PUSH_ii","PULL_ii","BSR_w","RTS_o","MOTOR_rr","MIDIPLAY_r",
"MIDISTOP_o","WAVPLAY_r","WAVSTOP_o","MSEC_rr","SEC_rr","BUT3_r","VOL_r","MVOL_r",
"PUSHBUTTON_r","SRC_rr","BRAT_rw","BSRT_rw","OSC_rr","INV_rr","DIV_rr","HSV_o",
"MOTORGET_rr","MUSIC_r","DOWNLOAD_r","MOTORRST_r","SEND_rr","SENDREADY_r","LASTPING_rr","TRAME_iiii",
"","","","","","","","",
"","","","","","","","",
"","","","","","","","",
"","","","","","","","",
"","","","","","","","",
"","","","","","","","",
"","","","","","","",""
};
int opcodeslen[]=
{1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,2,1,2,
3,3,3,2,2,2,2,2,
2,2,2,2,2,2,2,1,
1,1,2,2,2,0,1,1,
0,1,0,1,1,1,1,1,
1,1,3,3,1,1,1,0,
1,1,1,1,1,1,1,4,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0
};

int getop(String src)
{
	int i;
	for(i=0;i<256;i++) if (src.compareToIgnoreCase(opcodes[i])==0) return i;
	return -1;
}

int cutline(String src,int i0,String words[],int n)
{
	int i,l,k,go;
	l=src.length();
	k=0;
	byte c;

	// on avance jusqu'au prochain dét de ligne
	go=1; while(go>0)
	{
		if (i0>=l) go=0;
		else
		{
			c=(byte)src.charAt(i0);
			if ((c<32)&&(c!=9)) i0++;
			else go=0;
			if (c==10) line++;
		}
	}
	i=i0;
	go=1; while(go>0)
	{
		if (i>=l) go=0;
		else
		{
			c=(byte)src.charAt(i);
			if (i0>=0)
			{
				if (c<=32)
				{
					words[k++]=src.substring(i0,i);
					i0=-1;
				}
			}
			else if (c>32) i0=i;
			if ((c<32)&&(c!=9)) go=0;
			else if (k>=n) go=0;
			else i++;
		}
	}
	if ((i==l)&&(i0>=0)) words[k++]=src.substring(i0,l);
	while(k<n) words[k++]="";
	go=1; while(go>0)
	{
		if (i>=l) go=0;
		else
		{
			c=(byte)src.charAt(i);
			if ((c==10)||(c==13)) go=0;
			else i++;
		}
	}
	return i;
}

int cutvirg(String src,String args[],int n,AssoVasm equ)
{
	int i,j,k;
	
	j=0;
	k=0;
	for(i=0;((i<src.length())&&(k<n));i++)
		if (src.charAt(i)==',')
		{
			args[k++]=src.substring(j,i);
			j=i+1;
		}
	if (k<n) args[k++]=src.substring(j,src.length());
	i=k;
	for(k=0;k<n;k++)
		if (k>=i) args[k]="";
		else
		{
			String val=equ.find(args[k]);
			if (val!=null)
			{
				args[k]=val;
			}
		}
	return i;
}

String typeoper(String args[],int k)
{
	String res="";
	int i;
	for(i=0;i<k;i++)
		if (args[i].length()==0) res+="o";
		else if (args[i].charAt(0)=='R') res+="r";
		else if (args[i].charAt(0)=='r') res+="r";
		else if (args[i].charAt(0)=='@') res+="w";
		else res+="i";
	return res;
}

		
		
int checkint(String s,int start,int end)
{
	int i;
	byte c;
	for(i=start;i<end;i++)
	{
		c=(byte)s.charAt(i);
		if ((c<'0')||(c>'9'))
			if ((c!='-')||(i!=start)) return 0;
	}
	return 1;
}

int checkhexa(String s,int start,int end)
{
	int i;
	byte c;
	for(i=start+1;i<end;i++)
	{
		c=(byte)s.charAt(i);
		if (((c<'0')||(c>'9'))&&((c<'a')||(c>'f'))&&((c<'A')||(c>'F'))) return 0;
	}
	return 1;
}


void checkInt(String reg)
{
//	if (bin.isempty()) return;
	if (reg.charAt(0)=='$')
	{
		if (checkhexa(reg,1,reg.length())==0)
		{
			bin.empty();
			add_error(reg+" is not an int");
			// message d'erreur
		}
		else bin.add(htoi(reg.substring(1,reg.length())));
	}
	else
	if (checkint(reg,0,reg.length())==0)
	{
		bin.empty();
		add_error(reg+" is not an int");
		// message d'erreur
	}
	else bin.add(atoi(reg));
}

void checkRegOP(String reg)
{
	if (bin.isempty()) return;
	if (checkint(reg,1,reg.length())==0)
	{
		bin.empty();
		add_error(reg+" is not a register");
		// message d'erreur
	}
	else
	{
		byte b=bin.remove();
		bin.add(b+atoi(reg.substring(1,reg.length())));
	}
}

void checkReg(String reg)
{
	if (bin.isempty()) return;
	if (checkint(reg,1,reg.length())==0)
	{
		bin.empty();
		add_error(reg+" is not a register");
		// message d'erreur
	}
	else
	{
		bin.add(atoi(reg.substring(1,reg.length())));
	}
}
void checkReg2(String a,String b)
{
	if (bin.isempty()) return;
	if (checkint(a,1,a.length())==0)
	{
		bin.empty();
		add_error(a+" is not a register");
		// message d'erreur
	}
	else
	if (checkint(b,1,b.length())==0)
	{
		bin.empty();
		add_error(b+" is not a register");
		// message d'erreur
	}
	else
	{
		bin.add(((atoi(a.substring(1,a.length()))&15)<<4)+(atoi(b.substring(1,b.length()))&15));
	}
}
Links checkAbs(String label,Links links)
{
	if (bin.isempty()) return links;
	links=new Links(label,bin.index(),links);
	bin.add(0);
	bin.add(0);
	return links;
}


public Vasm()
{
	bin=new BinVasm();
}

public String error()
{
	return vasm_error;
}

boolean includebin(String name,String path)
{
	byte[] content=getfilebytes(name,path);
	if (content==null)
	{
		add_error("unknown file : "+name);
		return false;
	}
	int x=content[0]&255;
	x=(x<<8)+(content[1]&255);
	x=(x<<8)+(content[2]&255);
	x=(x<<8)+(content[3]&255);
	for(int i=0;i<x;i++) bin.add(content[i+4]);
	return true;
}
public String asm(String src,int offset0,String path)
{
	try{
	int i=0;
	line=1;
	String words[]=new String[3];
	String args[]=new String[128];
	Links links=null;
	AssoVasm labels=new AssoVasm();
	AssoVasm equ=new AssoVasm();
	int offset=offset0;
	int rmb_index=0;
	int src_index=0;
	
	int countfile=0;
	String offfile="";
	String datafile="";
	
	vasm_error="";
	bin.empty();	
	while(i<src.length())
	{
		i=cutline(src,i,words,3);
//		System.out.println(words[0]+" "+words[1]+" "+words[2]);
		if ((words[0].length()==0)||(words[0].charAt(0)!=';'))
		{
			if ((words[0].length()!=0)&&(words[0].charAt(0)=='@'))
			{
				labels.add(words[0],null,offset);
			}
			
			if (words[1].compareToIgnoreCase("END")==0)
			{
				// finir le calcul
				while(links!=null)
				{
					int jmp=labels.findi(links.label());
					if (jmp>=0) bin.setw(links.value(),jmp);
					else
					{
						add_error("cannot link "+links.label()+" : unknown label");
						// error
						return "";
					}
					links=links.next();
				}	
				String binres=bin.get();
				return itobin4(binres.length())+binres+itobin4(countfile)+offfile+datafile;
			}
			if (words[1].compareToIgnoreCase("EQU")==0)
			{
				equ.add(words[0],words[2],0);
			}
			else
			if (words[1].compareToIgnoreCase("RMB")==0)
			{
				equ.add(words[0],itoa(rmb_index),0);
				rmb_index+=atoi(words[2]);
			}
			else
			if (words[1].compareToIgnoreCase("FCB")==0)
			{
				int n=cutvirg(words[2],args,128,equ);
				int j;
				for(j=0;j<n;j++)
				{
					checkInt(args[j]);
					if (bin.isempty())
					{
						add_error("fcb error "+args[j]);
						return "";
					}
				}
				offset+=n;
			}
			else
			if (words[1].compareToIgnoreCase("FDB")==0)
			{
				int n=cutvirg(words[2],args,128,equ);
				int j;
				for(j=0;j<n;j++)
				{
					links=checkAbs(args[j],links);
					if (bin.isempty())
					{
						add_error("fdb error");
						return "";
					}
				}
				offset+=n+n;
			}
			else
			if ((words[0].length()!=0)&&(words[0].charAt(0)=='/')&&
				(words[1].compareToIgnoreCase("FILE")==0))
			{
				String cont=getfile(words[2],path);
				if (cont==null)
				{
					add_error("unknown file : "+words[2]);
					return "";
				}
				if (cont.length()>360000) cont=cont.substring(0,360000);
				datafile+=cont;
				offfile+=itobin4(datafile.length());
				equ.add(words[0],itoa(countfile),0);
				countfile++;
			}
			else if ((words[0].length()!=0)&&(words[0].charAt(0)=='/')&&
					(words[1].compareToIgnoreCase("FILECUT")==0))
				{
					String cont=getfile(words[2],path);
					if (cont==null)
					{
						add_error("unknown file : "+words[2]);
						return "";
					}
					if (cont.length()>30000) cont=cont.substring(0,30000);
					datafile+=cont;
					offfile+=itobin4(datafile.length());
					equ.add(words[0],itoa(countfile),0);
					countfile++;
				}
			else if ((words[0].length()!=0)&&(words[0].charAt(0)=='/')&&
				(words[1].compareToIgnoreCase("SRCDEF")==0))
			{
				equ.add(words[0],itoa(src_index),0);
				src_index++;
			}
			else if (words[1].compareToIgnoreCase("BINARY")==0)
			{
				if(!includebin(words[2],path)) return "";
			}
			else if (words[1].length()!=0)
			{
				int n=cutvirg(words[2],args,128,equ);
				String res=typeoper(args,n);
				String opcode=words[1]+"_"+res;
				int op=getop(opcode);
				if (op<0)
				{
					add_error("unknown instruction : "+words[1]+" ("+res+")");
					return "";
				}
				bin.add(op);
				offset+=1+opcodeslen[op];
				
				if (res.compareTo("ri")==0)
				{
					checkRegOP(args[0]);
					checkInt(args[1]);
				}
				else if (res.compareTo("o")==0)
				{
				}
				else if (res.compareTo("r")==0)
				{
					checkReg(args[0]);
				}
				else if (res.compareTo("rr")==0)
				{
					checkReg2(args[0],args[1]);
				}
				else if (res.compareTo("w")==0)
				{
					links=checkAbs(args[0],links);
				}
				else if (res.compareTo("rrw")==0)
				{
					checkReg2(args[0],args[1]);
					links=checkAbs(args[2],links);
				}
				else if (res.compareTo("rw")==0)
				{
					checkReg(args[0]);
					links=checkAbs(args[1],links);
				}
				else if (res.compareTo("i")==0)
				{
					checkInt(args[0]);
				}
				else if (res.compareTo("rir")==0)
				{
					checkReg2(args[0],args[2]);
					checkInt(args[1]);
				}
				else if (res.compareTo("ii")==0)
				{
					checkInt(args[0]);
					checkInt(args[1]);
				}
				else if (res.compareTo("iiii")==0)
				{
					checkInt(args[0]);
					checkInt(args[1]);
					checkInt(args[2]);
					checkInt(args[3]);
				}
				if (bin.isempty())
				{
					add_error("args");
					return "";
				}
			}
			
		}
	}
        add_error("end of file");
        return "";
        } catch (Throwable t) {
        }
	add_error("exception at line "+line);
	return "";
}
int checksum(String src)
{
	try{
		byte[] buf=src.getBytes("ISO-8859-1");
		int l=src.length();
		int i=0;
		byte chk=0;
		for(i=0;i<l;i++) chk+=buf[i];
		return (int)chk;
	} catch (Throwable t) {
    		return -1;
	}
}
public String asmonly(String src)
{
	String res=asm(src,0,"");
//	return "<pre>**empty"+error();
	if (res.length()==0) res="<pre>**ERROR\n"+error();
	return res;
}
public String maketrame(String src,int id,int timeout,String path)
{
	try {
	
	String res=asm(src,17,path);
//	return "<pre>**empty"+error();
	if (res.length()==0) return "<pre>**ERROR\n"+error();
	
	res=itobin1(5)+itobin3(res.length()+15)+
		"amber"+itobin4(id)+itobin1(timeout)+res;
        return res+itobin1(255-checksum(res)-checksum("mind"))+"mind";

	
	} catch (Throwable t) {
	t.printStackTrace();
	}
	return "<pre>***EXCEPTION";
}

}

