import java.util.*;

/**
 * This interface is the standard implementation of intreface PIC, but a lot of other classes implementing
 * it could be written (to have better emulation algorithms for instance).
 * <BR>You can read the source of this class <A HREF="./PIC_std.java.html"> here</A>.
 */
public class PIC_std extends DigitalComponent_std implements PIC {
// Private constants
	protected static final int DestW = 0;
	protected static final int DestF = 1;

// Constructors
	/**
	 * Creates a new PIC_std with a program.
	 * @param prog The program.
	 */
	public PIC_std(CompiledProgram prog) {
		super(18);
		FR = new FileRegisters();
		Fl = new Flash(prog);
		Stack = new int[8];
		notifyVector = new Vector();
	}

	/**
	 * Creates a new PIC_std without any program.
	 */
	public PIC_std() {
		super(18);
		FR = new FileRegisters();
		Fl = new Flash();
		Stack = new int[8];
		notifyVector = new Vector();
	}

	/**
	 * Creates a new PIC_std without any program with an UI.
	 */
	public PIC_std(DigitalComponentUI dui) {
		super(18, dui);
		FR = new FileRegisters();
		Fl = new Flash();
		Stack = new int[8];
		notifyVector = new Vector();
	}

// Public methods
	protected void refreshPins() {
		int i;
		// RAx
		int tris = FR.Read(SystemRegisters.TRISA, 1);
		int port = FR.Read(SystemRegisters.PORTA, 0);
		int APins[] = {17, 18, 1, 2, 3};

		for(i = 0 ; i <= 4 ; i++)
			if((tris & Utils.Puiss2(i)) == 0) {
				if((port & Utils.Puiss2(i)) == 0) {
					if(getPinState(APins[i]))
						setPinState(APins[i], false);
				}
				else {
					if(!getPinState(APins[i]))
						setPinState(APins[i], true);
				}
			}

		//RBx
		tris = FR.Read(SystemRegisters.TRISB, 1);
		port = FR.Read(SystemRegisters.PORTB, 0);
		int BPins[] = {6, 7, 8, 9, 10, 11, 12, 13};

		for(i = 0 ; i <= 7 ; i++)
			if((tris & Utils.Puiss2(i)) == 0) {
				if((port & Utils.Puiss2(i)) == 0) {
					if(getPinState(BPins[i]))
						setPinState(BPins[i], false);
				}
				else {
					if(!getPinState(BPins[i]))
						setPinState(BPins[i], true);
				}
			}
	}

	public boolean connectPin(int LocalPin, DigitalComponent c, int RemotePin, boolean InitialState) {
		int address = -1;
		int bit = 0;

		switch(LocalPin) {
			case 6 : bit = 0; address = SystemRegisters.PORTB; break; // RB0
			case 7 : bit = 1; address = SystemRegisters.PORTB; break; // RB1
			case 8 : bit = 2; address = SystemRegisters.PORTB; break; // RB2
			case 9 : bit = 3; address = SystemRegisters.PORTB; break; // RB3
			case 10 : bit = 4; address = SystemRegisters.PORTB; break; // RB4
			case 11 : bit = 5; address = SystemRegisters.PORTB; break; // RB5
			case 12 : bit = 6; address = SystemRegisters.PORTB; break; // RB6
			case 13 : bit = 7; address = SystemRegisters.PORTB; break; // RB7

			case 17 : bit = 0; address = SystemRegisters.PORTA; break; // RA0
			case 18 : bit = 1; address = SystemRegisters.PORTA; break; // RA1
			case 1 : bit = 2; address = SystemRegisters.PORTA; break; // RA2
			case 2 : bit = 3; address = SystemRegisters.PORTA; break; // RA3
			case 3 : bit = 4; address = SystemRegisters.PORTA; break; // RA4
		}

		if(address != -1) {
			int port = FR.Read(address, 0);

			if(InitialState && ((port & Utils.Puiss2(bit)) == 0))
				FR.Write(address, port | Utils.Puiss2(bit), 0);
			else if(!InitialState && ((port & Utils.Puiss2(bit)) != 0))
				FR.Write(address, port & ~Utils.Puiss2(bit), 0);
		}

		return super.connectPin(LocalPin, c, RemotePin, InitialState);
	}	

	public void setPinStateDontNotify(int p, boolean state) {
		super.setPinStateDontNotify(p, state);

		if(_dui != null)
			_dui.refresh(getState());

		int trisa = FR.Read(SystemRegisters.TRISA, 1);
		int trisb = FR.Read(SystemRegisters.TRISB, 1);

		int address = -1;
		int bit = 0;

		switch(p) {
			case 6 : if((trisb & 1) != 0) {bit = 0; address = SystemRegisters.PORTB;} break; // RB0
			case 7 : if((trisb & 2) != 0) {bit = 1; address = SystemRegisters.PORTB;} break; // RB1
			case 8 : if((trisb & 4) != 0) {bit = 2; address = SystemRegisters.PORTB;} break; // RB2
			case 9 : if((trisb & 8) != 0) {bit = 3; address = SystemRegisters.PORTB;} break; // RB3
			case 10 : if((trisb & 16) != 0) {bit = 4; address = SystemRegisters.PORTB;} break; // RB4
			case 11 : if((trisb & 32) != 0) {bit = 5; address = SystemRegisters.PORTB;} break; // RB5
			case 12 : if((trisb & 64) != 0) {bit = 6; address = SystemRegisters.PORTB;} break; // RB6
			case 13 : if((trisb & 128) != 0) {bit = 7; address = SystemRegisters.PORTB;} break; // RB7

			case 17 : if((trisa & 1) != 0) {bit = 0; address = SystemRegisters.PORTA;} break; // RA0
			case 18 : if((trisa & 2) != 0) {bit = 1; address = SystemRegisters.PORTA;} break; // RA1
			case 1 : if((trisa & 4) != 0) {bit = 2; address = SystemRegisters.PORTA;} break; // RA2
			case 2 : if((trisa & 8) != 0) {bit = 3; address = SystemRegisters.PORTA;} break; // RA3
			case 3 : if((trisa & 16) != 0) {bit = 4; address = SystemRegisters.PORTA;} break; // RA4
		}

		int port = FR.Read(address, 0);
		if(address != -1)
			if(state && ((port & Utils.Puiss2(bit)) == 0)) {
				FR.Write(address, port | Utils.Puiss2(bit), 0);
				checkAndPerformInterrupt(address, bit);
			}
			else if(!state && ((port & Utils.Puiss2(bit)) != 0)) {
				FR.Write(address, port & ~Utils.Puiss2(bit), 0);
				checkAndPerformInterrupt(address, bit);
			}
	}

	protected boolean isOutputPin(int p) {
		int trisa = FR.Read(SystemRegisters.TRISA, 1);
		int trisb = FR.Read(SystemRegisters.TRISB, 1);

		switch(p) {
			case 6 : if((trisb & 1) == 0) return true; break;	// RB0
			case 7 : if((trisb & 2) == 0) return true; break;	// RB1
			case 8 : if((trisb & 4) == 0) return true; break;	// RB2
			case 9 : if((trisb & 8) == 0) return true; break;	// RB3
			case 10 : if((trisb & 16) == 0) return true; break;	// RB4
			case 11 : if((trisb & 32) == 0) return true; break;	// RB5
			case 12 : if((trisb & 64) == 0) return true; break;	// RB6
			case 13 : if((trisb & 128) == 0) return true; break;	// RB7

			case 17 : if((trisa & 1) == 0) return true; break;	// RA0
			case 18 : if((trisa & 2) == 0) return true; break;	// RA1
			case 1 : if((trisa & 4) == 0) return true; break;	// RA2
			case 2 : if((trisa & 8) == 0) return true; break;	// RA3
			case 3 : if((trisa & 16) == 0) return true; break;	// RA4
		}
		return false;
	}

	protected void checkAndPerformInterrupt(int address, int bit) {

	}

	public void notifyOneCycle() {
	}
	
	public String getState() {
		String r = "";
		int i;

		for(i = 18 ; i > 9 ; i--)
			r += (getPinState(i) ? "1" : "0");
		r += "\n";
		for(i = 18 ; i > 9 ; i--)
			r += (isOutputPin(i) ? "^" : "v");

		r += "\n\\\n/\n";
		for(i = 1 ; i <= 9 ; i++)
			r += (isOutputPin(i) ? "v" : "^");
		r += "\n";
		for(i = 1 ; i <= 9 ; i++)
			r += (getPinState(i) ? "1" : "0");

		return r;
	}

	public void ReprogramFlash(CompiledProgram prog) {
		Fl.Reprogram(prog);
	}

	public void Run() {
		for(;;)
			ExecInstruction();
	}

	public void Reset() {
		PC = 0;
		W = 0;
		StackPointer = 0;
	}

	public void ExecInstruction() {
		int File = 0, Dest = 0, Literal = 0, Opcode = Fl.Read(PC);
		InstructionInformation instr = InstructionSet.Find(Opcode);

		if(instr == null)
			System.out.println("Illegal opcode");

		if(instr.NbOperandes() > 0) {
			switch(instr.CodeAndDecodeType()) {
				case OpcodeType.FileAndDest		: File = DecodeFile(Opcode); Dest = DecodeDest(Opcode); break;
				case OpcodeType.File			: File = DecodeFile(Opcode); break;
				case OpcodeType.FileAnd3BitsConst	: File = DecodeFile(Opcode); Literal = Decode3BitsConst(Opcode); break;
				case OpcodeType.Literal8Bits		: Literal = DecodeLiteral8Bits(Opcode); break;
				case OpcodeType.Literal11Bits		: Literal = DecodeLiteral11Bits(Opcode); break;
				default					: break;
			}
		}


		int oldPCL = (PC + 1) & 0xFF;
		FR.Write(SystemRegisters.PCL, oldPCL);

		switch(instr.ExecFunction()) {
			case 1  : ExecAddwf(File, Dest); break;
			case 2  : ExecAndwf(File, Dest); break;
			case 3  : ExecClrf(File); break;
			case 4  : ExecClrw(); break;
			case 5  : ExecComf(File, Dest); break;
			case 6  : ExecDecf(File, Dest); break;
			case 7  : ExecDecfsz(File, Dest); break;
			case 8  : ExecIncf(File, Dest); break;
			case 9  : ExecIncfsz(File, Dest); break;
			case 10 : ExecIorwf(File, Dest); break;
			case 11 : ExecMovf(File, Dest); break;
			case 12 : ExecMovwf(File); break;
			case 13 : ExecNop(); break;
			case 14 : ExecRlf(File, Dest); break;
			case 15 : ExecRrf(File, Dest); break;
			case 16 : ExecSubwf(File, Dest); break;
			case 17 : ExecSwapf(File, Dest); break;
			case 18 : ExecXorwf(File, Dest); break;

			case 19 : ExecBcf(File, Literal); break;
			case 20 : ExecBsf(File, Literal); break;
			case 21 : ExecBtfsc(File, Literal); break;
			case 22 : ExecBtfss(File, Literal); break;

			case 23 : ExecAddlw(Literal); break;
			case 24 : ExecAndlw(Literal); break;
			case 25 : ExecCall(Literal); break;
			case 26 : ExecClrwdt(); break;
			case 27 : ExecGoto(Literal); break;
			case 28 : ExecIorlw(Literal); break;
			case 29 : ExecMovlw(Literal); break;
			case 30 : ExecRetfie(); break;
			case 31 : ExecRetlw(Literal); break;
			case 32 : ExecReturn(); break;
			case 33 : ExecSleep(); break;
			case 34 : ExecSublw(Literal); break;
			case 35 : ExecXorlw(Literal); break;
		}

		if(FR.Read(SystemRegisters.PCL) != oldPCL)
			PC = FR.Read(SystemRegisters.PCL) + (FR.Read(SystemRegisters.PCLATH) << 8);
		refreshPins();
		notifyAllOneCycle();
	}

	protected void notifyAllOneCycle() {
		int i, n = notifyVector.size();

		for(i = 0 ; i < n ; i++)
			((DigitalComponent)notifyVector.elementAt(i)).notifyOneCycle();
	}

	public void addComponentToNotify(DigitalComponent c) {
		notifyVector.addElement(c);
	}

// Methods to execute the instructions	
	protected void ExecAddwf(int File, int Dest) {
		int res = W + FR.Read(File);

		SetStatusC(res & 0xFF00);
		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		AffectStatusZ(res);
		PC++;
	}

	protected void ExecAndwf(int File, int Dest) {
		int res = W & FR.Read(File);

		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		AffectStatusZ(res);
		PC++;
	}

	protected void ExecClrf(int File) {
		FR.Write(File, 0);
		AffectStatusZ(0);
		PC++;
	}

	protected void ExecClrw() {
		W = 0;
		AffectStatusZ(0);
		PC++;
	}

	protected void ExecComf(int File, int Dest) {
		int res = (~FR.Read(File)) & 0xFF;

		if(Dest == DestW)
			W = res;
		else

			FR.Write(File, res);
		AffectStatusZ(res);
		PC++;
	}

	protected void ExecDecf(int File, int Dest) {
		int res = FR.Read(File) - 1;

		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		AffectStatusZ(res);
		PC++;
	}

	protected void ExecDecfsz(int File, int Dest) {
		int res = FR.Read(File) - 1;

		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		if(res == 0)	// Skip if 0
			PC++;

		PC++;
	}

	protected void ExecIncf(int File, int Dest) {
		int res = FR.Read(File) + 1;

		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		AffectStatusZ(res);
		PC++;
	}

	protected void ExecIncfsz(int File, int Dest) {
		int res = FR.Read(File) + 1;

		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		if(res == 0)	// Skip if 0
			PC++;

		PC++;
	}

	protected void ExecIorwf(int File, int Dest) {
		int res = W | FR.Read(File);

		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		AffectStatusZ(res);
		PC++;
	}

	protected void ExecMovf(int File, int Dest) {
		int res = FR.Read(File);

		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		AffectStatusZ(res);
		PC++;
	}

	protected void ExecMovwf(int File) {
		FR.Write(File, W);

		PC++;
	}

	protected void ExecNop() {
		PC++;
	}

	protected void ExecRlf(int File, int Dest) {
		int res = FR.Read(File) << 1;
		res += FR.Read(SystemRegisters.STATUS) & Utils.Puiss2(SystemRegisters.C);

		SetStatusC(res & 0xFF00);
		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		PC++;
	}

	protected void ExecRrf(int File, int Dest) {
		int res = FR.Read(File);
		SetStatusC(res & 1);
		res >>= 1;
		res += ((FR.Read(SystemRegisters.STATUS) & Utils.Puiss2(SystemRegisters.C))) << 7;

		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		PC++;
	}

	protected void ExecSubwf(int File, int Dest) {
		int res = FR.Read(File) - W;

		SetStatusC(res & 0xFF00);
		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		AffectStatusZ(res);
		PC++;
	}

	protected void ExecSwapf(int File, int Dest) {
		int res = FR.Read(File);
		res = ((res << 4) & 0xF0) + (res >> 4);

		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		PC++;
	}

	protected void ExecXorwf(int File, int Dest) {
		int res = W ^ FR.Read(File);

		res &= 0xFF;
		if(Dest == DestW)
			W = res;
		else
			FR.Write(File, res);

		AffectStatusZ(res);
		PC++;
	}


	protected void ExecBcf(int File, int Literal) {
		FR.Write(File, FR.Read(File) & ~Utils.Puiss2(Literal));

		PC++;
	}

	protected void ExecBsf(int File, int Literal) {
		FR.Write(File, FR.Read(File) | Utils.Puiss2(Literal));

		PC++;
	}

	protected void ExecBtfsc(int File, int Literal) {
		if((FR.Read(File) & Utils.Puiss2(Literal)) == 0)
			PC++;

		PC++;
	}

	protected void ExecBtfss(int File, int Literal) {
		if((FR.Read(File) & Utils.Puiss2(Literal)) != 0)
			PC++;

		PC++;
	}


	protected void ExecAddlw(int Literal) {
		int res = W + Literal;

		SetStatusC(res & 0xFF00);
		res &= 0xFF;
		W = res;

		AffectStatusZ(res);

		PC++;
	}

	protected void ExecAndlw(int Literal) {
		int res = W & Literal;

		res &= 0xFF;
		W = res;

		AffectStatusZ(res);

		PC++;
	}

	protected void ExecCall(int Literal) {
		Stack[StackPointer] = PC + 1;
		IncrementStackPointer();
		PC = Literal;
	}

	protected void ExecClrwdt() {
		PC++;
	}

	protected void ExecGoto(int Literal) {
		PC = Literal;
	}

	protected void ExecIorlw(int Literal) {
		int res = W | Literal;

		res &= 0xFF;
		W = res;

		AffectStatusZ(res);

		PC++;
	}

	protected void ExecMovlw(int Literal) {
		W = Literal;

		PC++;
	}

	protected void ExecRetfie() {
		DecrementStackPointer();
		PC = Stack[StackPointer];
	}

	protected void ExecRetlw(int Literal) {
		DecrementStackPointer();
		PC = Stack[StackPointer];
		W = Literal;
	}

	protected void ExecReturn() {
		DecrementStackPointer();
		PC = Stack[StackPointer];
	}

	protected void ExecSleep() {
		PC += 1;
	}

	protected void ExecSublw(int Literal) {
		int res = Literal - W;

		SetStatusC(res & 0xFF00);
		res &= 0xFF;
		W = res;

		AffectStatusZ(res);

		PC++;
	}

	protected void ExecXorlw(int Literal) {
		int res = W ^ Literal;

		res &= 0xFF;
		W = res;

		AffectStatusZ(res);

		PC++;
	}

// Methods to update STATUS register
	protected void AffectStatusZ(int r) {
		if(r == 0)
			FR.Write(SystemRegisters.STATUS, FR.Read(SystemRegisters.STATUS) | Utils.Puiss2(SystemRegisters.Z));
		else
			FR.Write(SystemRegisters.STATUS, FR.Read(SystemRegisters.STATUS) & ~Utils.Puiss2(SystemRegisters.Z));
	}

	protected void SetStatusC(int r) {
		if(r == 0)
			FR.Write(SystemRegisters.STATUS, FR.Read(SystemRegisters.STATUS) & ~Utils.Puiss2(SystemRegisters.C));
		else
			FR.Write(SystemRegisters.STATUS, FR.Read(SystemRegisters.STATUS) | Utils.Puiss2(SystemRegisters.C));
	}

// Methods to decode the operands of the instruction
	protected static int DecodeFile(int Opcode) {
		return Opcode & 0x7F;
	}

	protected static int DecodeDest(int Opcode) {
		return ((Opcode & 0x80) == 0) ? DestW : DestF;
	}

	protected static int Decode3BitsConst(int Opcode) {
		return (Opcode & 0x380) >> 7;
	}

	protected static int DecodeLiteral8Bits(int Opcode) {
		return Opcode & 0xFF;
	}

	protected static int DecodeLiteral11Bits(int Opcode) {
		return Opcode & 0x7FF;
	}

// StackPointer methods

	protected void IncrementStackPointer() {
		StackPointer++;
		StackPointer &= 7;
	}

	protected void DecrementStackPointer() {
		StackPointer--;
		StackPointer &= 7;
	}

// Private variables
	protected FileRegisters FR;
	protected Flash Fl;
	protected int W = 0, PC = 0;
	protected int StackPointer = 0, Stack[];
	protected Vector notifyVector;
}