/** * Calls a function according to the given function number stating that the given number of * arguments have been pushed onto the stack * * <p>If callerIsBuiltIn then the caller is a builtIn function that called this function through * callFunctionFromBuiltIn. If address is -1 then a native function should be looked up and * called. */ public void callFunction( short address, short numberOfArguments, String functionName, boolean callerIsBuiltIn) throws ProgramException { stackFrames.addElement(new Integer(workingStackSegment.getStartAddress())); workingStackSegment.setStartAddress(getSP() + 5); if (callerIsBuiltIn) { pushValue(MAIN_STACK, VMProgram.BUILTIN_FUNCTION_ADDRESS); } else { pushValue(MAIN_STACK, program.getPC()); } pushFromRAM(MAIN_STACK, Definitions.LOCAL_POINTER_ADDRESS); pushFromRAM(MAIN_STACK, Definitions.ARG_POINTER_ADDRESS); pushFromRAM(MAIN_STACK, Definitions.THIS_POINTER_ADDRESS); pushFromRAM(MAIN_STACK, Definitions.THAT_POINTER_ADDRESS); ram.setValueAt( Definitions.ARG_POINTER_ADDRESS, (short) (getSP() - numberOfArguments - 5), false); ram.setValueAt(Definitions.LOCAL_POINTER_ADDRESS, getSP(), false); // enable in the arg segment only the number of args that were sent to the called function. argSegment.setEnabledRange( argSegment.getStartAddress(), argSegment.getStartAddress() + numberOfArguments - 1, true); if (address == VMProgram.BUILTIN_FUNCTION_ADDRESS) { // Perform some actions normally done in the function() method localSegment.setEnabledRange( localSegment.getStartAddress(), localSegment.getStartAddress() - 1, true); // no local variables callStack.pushFunction(functionName + " (built-in)"); staticSegment.setEnabledRange(0, -1, true); // empty static segment // Read parameters from the stack short[] params = new short[numberOfArguments]; for (int i = 0; i < numberOfArguments; ++i) { params[i] = argSegment.getValueAt(i); } // Call the built-in implementation builtInFunctionsRunner.callBuiltInFunction(functionName, params); } else if (address >= 0 || address < program.getSize()) { program.setPC(address); program.setPC(address); // make sure previouspc isn't pc-1 // which might happen if the calling // function called this function in the // last line before the "return" and // was declared just before this function. // In this case encountering the "function" // command will issue an error about // "missing return"... } else { error("Illegal call address"); } }
/** * Calls a function according to the given function name with the given parameters from a built-in * function */ public void callFunctionFromBuiltIn(String functionName, short[] params) throws ProgramException { // Push the arguments onto the stack for (int i = 0; i < params.length; ++i) { pushValue(METHOD_STACK, params[i]); } callFunction(program.getAddress(functionName), (short) params.length, functionName, true); }
/** Sets the static segment range according to the the given function (file) name. */ protected void setStaticRange(String functionName) throws ProgramException { int dotLocation = functionName.indexOf("."); if (dotLocation == -1) throw new ProgramException("Illegal function name: " + functionName); String className = functionName.substring(0, dotLocation); int[] range = program.getStaticRange(className); if (range == null) throw new ProgramException("Function name doesn't match class name: " + functionName); staticSegment.setStartAddress(range[0]); staticSegment.setEnabledRange(range[0], range[1], true); }
/** * Constructs the CPU with given program, RAM, call stack, bus, stack and other memory segments. */ public CPU( VMProgram program, RAM ram, CallStack callStack, Calculator calculator, Bus bus, AbsolutePointedMemorySegment stackSegment, TrimmedAbsoluteMemorySegment workingStackSegment, MemorySegment staticSegment, MemorySegment localSegment, MemorySegment argSegment, MemorySegment thisSegment, MemorySegment thatSegment, MemorySegment tempSegment, File builtInDir) { this.program = program; this.ram = ram; this.callStack = callStack; this.calculator = calculator; this.bus = bus; this.stackSegment = stackSegment; this.workingStackSegment = workingStackSegment; this.staticSegment = staticSegment; this.localSegment = localSegment; this.argSegment = argSegment; this.thisSegment = thisSegment; this.thatSegment = thatSegment; this.tempSegment = tempSegment; segments = new MemorySegment[HVMInstructionSet.NUMBER_OF_ACTUAL_SEGMENTS]; segments[HVMInstructionSet.LOCAL_SEGMENT_CODE] = localSegment; segments[HVMInstructionSet.ARG_SEGMENT_CODE] = argSegment; segments[HVMInstructionSet.THIS_SEGMENT_CODE] = thisSegment; segments[HVMInstructionSet.THAT_SEGMENT_CODE] = thatSegment; segments[HVMInstructionSet.TEMP_SEGMENT_CODE] = tempSegment; stackFrames = new Vector(); if (program.getGUI() != null) { builtInFunctionsRunner = new BuiltInFunctionsRunner(this, builtInDir); } }
/** 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"); } }
/** * Enters an infinite loop requested by a built-in function, de-facto halting the program. * important so that tests and other scripts finish counting (since a built-in infinite loop * doesn't count as steps). also needed because there is no good way to use the stop button to * stop an infinite loop in a built-in jack class. A message containing information may be * provided (can be null). */ public void infiniteLoopFromBuiltIn(String message) { program.setPCToInfiniteLoopForBuiltIns(message); }
/** Pops a value from the stack and goes to the given address if the value is not zero. */ public void ifGoTo(short address) throws ProgramException { if (popAndReturn() != 0) { program.setPC(address); } }
/** Goes to the label at the given address */ public void goTo(short address) { program.setPC(address); }
/** * Executes the current instruction (Program at pc). Returns false if END command, true otherwise. */ public void executeInstruction() throws ProgramException { currentInstruction = program.getNextInstruction(); if (currentInstruction == null) throw new ProgramException("No more instructions to execute"); switch (currentInstruction.getOpCode()) { case HVMInstructionSet.ADD_CODE: add(); break; case HVMInstructionSet.SUBSTRACT_CODE: substract(); break; case HVMInstructionSet.NEGATE_CODE: negate(); break; case HVMInstructionSet.EQUAL_CODE: equal(); break; case HVMInstructionSet.GREATER_THAN_CODE: greaterThan(); break; case HVMInstructionSet.LESS_THAN_CODE: lessThan(); break; case HVMInstructionSet.AND_CODE: and(); break; case HVMInstructionSet.OR_CODE: or(); break; case HVMInstructionSet.NOT_CODE: not(); break; case HVMInstructionSet.PUSH_CODE: push(currentInstruction.getArg0(), currentInstruction.getArg1()); break; case HVMInstructionSet.POP_CODE: pop(currentInstruction.getArg0(), currentInstruction.getArg1()); break; case HVMInstructionSet.GOTO_CODE: goTo(currentInstruction.getArg0()); break; case HVMInstructionSet.IF_GOTO_CODE: ifGoTo(currentInstruction.getArg0()); break; case HVMInstructionSet.FUNCTION_CODE: if (program.getCurrentPC() == program.getPreviousPC() + 1) throw new ProgramException("Missing return in " + callStack.getTopFunction()); function(currentInstruction.getArg0()); break; case HVMInstructionSet.RETURN_CODE: returnFromFunction(); break; case HVMInstructionSet.CALL_CODE: callFunction( currentInstruction.getArg0(), currentInstruction.getArg1(), currentInstruction.getStringArg(), false); break; } }