package lab05;

public class CPU {
	
	private int accumulator;
	private int instructionPointer;
	private int dataMemoryBase;
	private Memory memory;
	private boolean halted;

	public CPU(Memory memory) {
		accumulator=0; // Not required, but included for clarity
		dataMemoryBase=0; 
		instructionPointer=2; // Start programs at memory location 2
		this.memory=memory;
		halted=true;
	}
	
	public void run() {
		halted=false;
		while(!halted) execute();
	}
	
	public void execute() {
		
		if (halted) return; 
		
		// Fetch instruction from memory
		Instruction inst = Instruction.factory(memory.get(instructionPointer));
		instructionPointer++;
		
		if (inst.isValid()) inst.execute(this);
		else { 
			halted=true;
			System.out.println("Invalid instruction... execution halted");
		}
		
		/*------------------------------------------------------------------
		 * I have included the previous implementation of the CPU execute
		 * method for reference purposes.  All of the logic that was in 
		 * this method (and is now commented) belongs in the individual
		 * execute methods in the InstructionXXX classes.
		 * 
		// Execute instruction		
		switch(inst.getOpcode()) {
		
		case(0): //NOP
			break;
		
		case(1): //LOD
			accumulator=input;
			Trace.message(inst + " acc=" + accumulator);
			break;
			
		case(2):	//STO
			if (inst.getModeName().equals("DIR")) {
				setData(inst.getArgument(),accumulator);
				Trace.message(inst + " data[" + inst.getArgument() + "]=" + accumulator);
			} 
			else System.out.println("Error... " + inst.getOpName() + " has unrecognized inst.getModeName(): " + inst.getModeName());
			break;
			
		case(3): //ADD
			accumulator = accumulator + input;
			Trace.message(inst + " acc=" + accumulator);
			break;
			
		case(4): //SUB
			accumulator = accumulator - input;
			Trace.message(inst + " acc=" + accumulator);
			break;
			
		case(5): //MUL
			accumulator = accumulator * input;
			Trace.message(inst + " acc=" + accumulator);
			break;
			
		case(6): //DIV
			if (input==0) 
				System.out.println("Attempt to divide by zero ignored");
			else {
				accumulator = accumulator / input;
				Trace.message(inst + " acc=" + accumulator);
			}
			break;
			
		case(7): //HLT
			halted=true;
			Trace.message(inst + " Program halted");
			break;
		 
		 */
	}
	
	public int getAccumulator() { return accumulator; }
	public void setAccumulator(int accumulator) { this.accumulator = accumulator; }

	public void setDataMemoryBase(int dataMemoryBase) {
		this.dataMemoryBase = dataMemoryBase;
	}
	
	public Memory getMemory() { return memory; }

	public void setInstructionPointer(int instructionPointer) {
		this.instructionPointer = instructionPointer;
	}

	public int getData(int loc) {
		return memory.get(loc+dataMemoryBase);
	}
	
	public void setData(int loc,int value) {
		memory.set(loc+dataMemoryBase, value);
	}

	public void setHalted(boolean halted) { this.halted = halted; }

}
