// Sets the registers with the alu's output according to // the given instruction // Throws ProgramException if destination contains M and A contains // an illegal address. protected void setDestination(short instruction) throws ProgramException { boolean destA = (instruction & 0x0020) > 0; boolean destD = (instruction & 0x0010) > 0; boolean destM = (instruction & 0x0008) > 0; if (destM) { int address = A.get(); if ((address < 0) || (address >= M.getSize())) { throw new ProgramException( "At line " + PC.get() + ": Destination is M but A=" + address + " is an illegal memory address."); } A.setUpdatePointer(true); bus.send(alu, 2, M, address); A.setUpdatePointer(false); } if (destA) { bus.send(alu, 2, A, 0); } if (destD) { bus.send(alu, 2, D, 0); } }
// sends a value from the appropriate stack (at sp-1) to the ram at the given index // and increments sp. private void popToRAM(int stackID, int index) throws ProgramException { short newSP = (short) (getSP() - 1); bus.send((stackID == MAIN_STACK ? stackSegment : workingStackSegment), newSP, ram, index); checkSP(newSP); setSP(newSP); }
// Push a value from the calculator at the given index into the appropriate stack. private void pushFromCalculator(int stackID, int index) throws ProgramException { short sp = getSP(); bus.send(calculator, index, (stackID == MAIN_STACK ? stackSegment : workingStackSegment), sp); checkSP((short) (sp + 1)); setSP((short) (sp + 1)); }
// sends a value from the appropriate stack (at sp-1) to the given segment at the given index // and increments sp. private void popToSegment(int stackID, short segmentCode, int index) throws ProgramException { short newSP = (short) (getSP() - 1); MemorySegment segment = (segmentCode == HVMInstructionSet.STATIC_SEGMENT_CODE) ? staticSegment : segments[segmentCode]; checkSegmentIndex(segment, segmentCode, index); bus.send((stackID == MAIN_STACK ? stackSegment : workingStackSegment), newSP, segment, index); checkSP(newSP); setSP(newSP); }
// sends a value from the given segment at the the given index to the appropriate stack (at sp) // and increments sp. private void pushFromSegment(int stackID, short segmentCode, int index) throws ProgramException { short sp = getSP(); MemorySegment segment = (segmentCode == HVMInstructionSet.STATIC_SEGMENT_CODE) ? staticSegment : segments[segmentCode]; checkSegmentIndex(segment, segmentCode, index); bus.send(segment, index, (stackID == MAIN_STACK ? stackSegment : workingStackSegment), sp); checkSP((short) (sp + 1)); setSP((short) (sp + 1)); }
// computes the exp part of the given instruction. // The result will be at the alu's output. // Throws ProgramException if the calculation involves M and A contains // an illegal address. protected void computeExp(short instruction) throws ProgramException { boolean indirect = (instruction & 0x1000) > 0; boolean zd = (instruction & 0x0800) > 0; boolean nd = (instruction & 0x0400) > 0; boolean zm = (instruction & 0x0200) > 0; boolean nm = (instruction & 0x0100) > 0; boolean f = (instruction & 0x0080) > 0; boolean no = (instruction & 0x0040) > 0; try { alu.setCommand( assemblerTranslator.getExpByCode((short) (instruction & 0xffc0)), zd, nd, zm, nm, f, no); } catch (AssemblerException ae) { } bus.send(D, 0, alu, 0); // sends D to input0 of the alu // sends A or M[A] to input1 of the alu if (indirect) { int address = A.get(); if ((address < 0) || (address >= M.getSize())) { throw new ProgramException( "At line " + PC.get() + ": Expression involves M but A=" + address + " is an illegal memory address."); } A.setUpdatePointer(true); bus.send(M, address, alu, 1); A.setUpdatePointer(false); } else { bus.send(A, 0, alu, 1); } alu.compute(); }
// Sets the program counter (if necessary) according to // the given instruction and the alu's output. // If the program counter was changed, returns true, otherwise false. // Throws ProgramException if the program counter should be changed and A // contains an illegal address. protected boolean checkJump(short instruction) throws ProgramException { boolean jumpNegative = (instruction & 0x0004) > 0; boolean jumpEqual = (instruction & 0x0002) > 0; boolean jumpPositive = (instruction & 0x0001) > 0; boolean changed = false; short exp = alu.getValueAt(2); if (((exp < 0) && jumpNegative) || ((exp == 0) && jumpEqual) || ((exp > 0) && jumpPositive)) { int newPC = A.get(); if ((newPC < 0) || (newPC >= Definitions.ROM_SIZE)) { throw new ProgramException( "At line " + PC.get() + ": Jump requested but A=" + newPC + " is an illegal program address."); } bus.send(A, 0, PC, 0); changed = true; } return changed; }
/** * Executes the current instruction (ROM at pc). Throws ProgramException if the current * instruction is illegal or if it causes an illegal effect (read/write from M when A is an * illegal address or jump when A is an illegal address). */ public void executeInstruction() throws ProgramException { short instruction = rom.getValueAt(PC.get()); boolean pcChanged = false; if ((instruction & 0x8000) == 0) { bus.send(rom, PC.get(), A, 0); } else if ((instruction & 0xe000) == 0xe000) { computeExp(instruction); setDestination(instruction); pcChanged = checkJump(instruction); } else if (instruction != HackAssemblerTranslator.NOP) { throw new ProgramException("At line " + PC.get() + ": Illegal instruction"); } if (!pcChanged) { short newPC = (short) (PC.get() + 1); if ((newPC < 0) || (newPC >= Definitions.ROM_SIZE)) { throw new ProgramException("At line " + PC.get() + ": Can't continue past last line"); } PC.setValueAt(0, newPC, true); } time++; }
/** Returns the value of the function to the top of the stack. */ public void returnFromFunction() throws ProgramException { // make sure that there's somewhere to return to (old local <> 0) if (stackSegment.getValueAt(Definitions.LOCAL_POINTER_ADDRESS) == 0) throw new ProgramException( "Nowhere to return to in " + getCallStack().getTopFunction() + "." + getCurrentInstruction().getIndexInFunction()); // done in order to clear the method stack's contents workingStackSegment.setStartAddress(getSP()); bus.send(ram, Definitions.LOCAL_POINTER_ADDRESS, ram, Definitions.R13_ADDRESS); // R13 = lcl bus.send( stackSegment, stackSegment.getValueAt(Definitions.LOCAL_POINTER_ADDRESS) - 5, ram, Definitions.R14_ADDRESS); // R14 = return address bus.send( stackSegment, getSP() - 1, stackSegment, ram.getValueAt(Definitions.ARG_POINTER_ADDRESS)); // *arg = return value setSP((short) (ram.getValueAt(Definitions.ARG_POINTER_ADDRESS) + 1)); // SP = arg + 1 bus.send( stackSegment, ram.getValueAt(Definitions.R13_ADDRESS) - 1, ram, Definitions.THAT_POINTER_ADDRESS); // that = *(R13 - 1) bus.send( stackSegment, ram.getValueAt(Definitions.R13_ADDRESS) - 2, ram, Definitions.THIS_POINTER_ADDRESS); // this = *(R13 - 2) bus.send( stackSegment, ram.getValueAt(Definitions.R13_ADDRESS) - 3, ram, Definitions.ARG_POINTER_ADDRESS); // arg = *(R13 - 3) bus.send( stackSegment, ram.getValueAt(Definitions.R13_ADDRESS) - 4, ram, Definitions.LOCAL_POINTER_ADDRESS); // lcl = *(R13 - 4) // removes the top function from the call stack callStack.popFunction(); // check whether there is a "calling frame" if (stackFrames.size() > 0) { // retrieve stack frame address of old function int frameAddress = ((Integer) stackFrames.lastElement()).intValue(); stackFrames.removeElementAt(stackFrames.size() - 1); workingStackSegment.setStartAddress(frameAddress); // disable non relevant range of the local segment - enable only the locals // of the function that we returned to. localSegment.setEnabledRange( Math.max(localSegment.getStartAddress(), Definitions.STACK_START_ADDRESS), frameAddress - 1, true); // enable in the arg segment only the number of args that were sent to the function // that we returned to. argSegment.setEnabledRange( argSegment.getStartAddress(), localSegment.getStartAddress() - 6, true); // enable this, that according to their retrieved pointers thisSegment.setEnabledRange( Math.max(thisSegment.getStartAddress(), Definitions.HEAP_START_ADDRESS), Definitions.HEAP_END_ADDRESS, true); thatSegment.setEnabledRange( Math.max(thatSegment.getStartAddress(), Definitions.HEAP_START_ADDRESS), Definitions.SCREEN_END_ADDRESS, true); } /* else { error("Nowhere to return to"); } */ // Allow return if we previously had "function" even with no call - // For the SimpleFunction test short returnAddress = ram.getValueAt(Definitions.R14_ADDRESS); if (returnAddress == VMProgram.BUILTIN_FUNCTION_ADDRESS) { staticSegment.setEnabledRange(0, -1, true); // empty static segment builtInFunctionsRunner.returnToBuiltInFunction(popValue(METHOD_STACK)); } else if (returnAddress >= 0 && returnAddress < program.getSize()) { // sets the static segment range if (stackFrames.size() > 0) { setStaticRange(callStack.getTopFunction()); } else { staticSegment.setStartAddress(Definitions.VAR_START_ADDRESS); staticSegment.setEnabledRange( Definitions.VAR_START_ADDRESS, Definitions.VAR_END_ADDRESS - 1, true); } program.setPC((short) (returnAddress - 1)); // set previousPC correctly program.setPC(returnAddress); // pc = *sp } else { error("Illegal return address"); } }