package lab04;

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 = new Instruction(memory.get(instructionPointer));
		instructionPointer++;
		
		// Fetch operands from instruction / memory
		
		//Note... the following holds for all BUT STO
		int input=0;
		if (inst.getOpcode()!=Instruction.findOpcode("STO")) {
			if (inst.getMode()==Instruction.findMode("IMM")) input=inst.getArgument();
			else if (inst.getMode()==Instruction.findMode("DIR")) input=getData(inst.getArgument());
			else {
				System.out.println("Error... " + inst.getOpName() + " has unrecognized inst.getModeName(): " + inst.getModeName());
				return;
			}
		}
		
		// 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;
			
		default:
			System.out.println("Error... Unrecognized inst.getOpName()erator: " + inst.getOpName() + " no action performed.");
		}
	}
	
	public int getAccumulator() { return 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);
	}

}
