package lab07;

/**
 * Simulate a Pippin CPU. 
 * 
 * A Pippin CPU consists of:
 * <ul>
 * <li>a reference to a Memory object, 
 * <li>three internal integer registers: an instructionPointer, an accumulator, and a dataMemoryBase,
 * <li>a single flag called "halted",
 * <li>the logic to execute Pippin instructions in an ALU to manipulate the registers, flags, and memory
 * </ul> 
 * @author cs140
 */
public class CPU {
	
	private int accumulator;
	private int instructionPointer;
	private int dataMemoryBase;
	private Memory memory;
	private boolean halted;

	/**
	 * Constructor to initialize CPU
	 * @param memory the memory to be used by the CPU
	 */
	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;
	}
	
	/**
	 * Run the current program by executing instructions until halted
	 */
	public void run() {
		halted=false;
		while(!halted) execute();
	}
	
	/**
	 * Execute a single Pippin Instruction
	 */
	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");
		}
	}
		

	/**
	 * Set the value of the dataMemoryBase register.
	 * @param dataMemoryBase new value
	 */
	public void setDataMemoryBase(int dataMemoryBase) {
		this.dataMemoryBase = dataMemoryBase;
	}
	
	/**
	 * Get the memory object this CPU is using.
	 * @return A reference to the Memory object this CPU is using
	 */
	public Memory getMemory() { return memory; }

	
	
	/**
	 * Get the instructionPointer register value
	 * @return the instructionPointer value
	 */
	public int getInstructionPointer() { return instructionPointer; }

	/**
	 * Set the instructionPointer register.
	 * @param instructionPointer new value
	 */
	public void setInstructionPointer(int instructionPointer) {
		this.instructionPointer = instructionPointer;
	}

	/**
	 * Get the value in the data part of the memory at the specified location.
	 * @param loc index into the data part of the memory (offset from the dataMemoryBase)
	 * @return the value in memory at the specified location
	 */
	public int getData(int loc) {
		return memory.get(loc+dataMemoryBase);
	}
	
	/**
	 * Set the value in the data part of the memory at the specified location.
	 * @param loc index into the data part of the memory (offset from the dataMemoryBase)
	 * @param value new value to write into the specified location in data memory
	 */
	public void setData(int loc,int value) {
		memory.set(loc+dataMemoryBase, value);
	}


	/**
	 * Get the accumulator register value
	 * @return the accumulator value
	 */
	public int getAccumulator() { return accumulator; }

	/**
	 * Update the accumulator register value
	 * @param accumulator the accumulator to set
	 */
	public void setAccumulator(int accumulator) {
		this.accumulator = accumulator;
	}

	/**
	 * Set the halted flag to the specified value.
	 * @param halted new value for the halted flag
	 */
	public void setHalted(boolean halted) { this.halted = halted; }

}
