/** Constructs a new empty program with the given GUI. */ public VMProgram(VMProgramGUI gui) { super(gui != null); this.gui = gui; listeners = new Vector(); staticRange = new Hashtable(); functions = new Hashtable(); if (hasGUI) { gui.addProgramListener(this); gui.addErrorListener(this); } reset(); }
public short getAddress(String functionName) throws ProgramException { Short address = (Short) functions.get(functionName); if (address != null) { return address.shortValue(); } else { String className = functionName.substring(0, functionName.indexOf(".")); if (staticRange.get(className) == null) { // The class is not implemented by a VM file - search for a // built-in implementation later. Display a popup to confirm // this as this is not a feature from the book but a later // addition. if (builtInAccessStatus == BUILTIN_ACCESS_UNDECIDED) { if (hasGUI && gui.confirmBuiltInAccess()) { builtInAccessStatus = BUILTIN_ACCESS_AUTHORIZED; } else { builtInAccessStatus = BUILTIN_ACCESS_DENIED; } } if (builtInAccessStatus == BUILTIN_ACCESS_AUTHORIZED) { return BUILTIN_FUNCTION_ADDRESS; } } // Either: // 1.The class is implemented by a VM file and no implementation // for the function is found - don't override with built-in // - or - // 2.The user did not authorize using built-in implementations. throw new ProgramException( className + ".vm not found " + "or function " + functionName + " not found in " + className + ".vm"); } }
/** * Creates a vm program. If the given file is a dir, creates a program composed of the vm files in * the dir. The vm files are scanned twice: in the first scan a symbol table (that maps function & * label names into addresses) is built. In the second scan, the instructions array is built. * Throws ProgramException if an error occurs while loading the program. */ public void loadProgram(String fileName) throws ProgramException { File file = new File(fileName); if (!file.exists()) throw new ProgramException("cannot find " + fileName); File[] files; if (file.isDirectory()) { files = file.listFiles(new HackFileFilter(".vm")); if (files == null || files.length == 0) throw new ProgramException("No vm files found in " + fileName); } else files = new File[] {file}; if (displayChanges) gui.showMessage("Loading..."); // First scan staticRange.clear(); functions.clear(); builtInAccessStatus = BUILTIN_ACCESS_UNDECIDED; Hashtable symbols = new Hashtable(); nextPC = 0; for (int i = 0; i < files.length; i++) { String name = files[i].getName(); String className = name.substring(0, name.indexOf(".")); // put some dummy into static range - just to tell the function // getAddress in the second pass which classes exist staticRange.put(className, new Boolean(true)); try { updateSymbolTable(files[i], symbols, functions); } catch (ProgramException pe) { if (displayChanges) gui.hideMessage(); throw new ProgramException(name + ": " + pe.getMessage()); } } boolean addCallBuiltInSysInit = false; if ((file.isDirectory() || symbols.get("Main.main") != null) && symbols.get("Sys.init") == null) { // If the program is in multiple files or there's a Main.main // function it is assumed that it should be run by calling Sys.init. // If no Sys.init is found, add an invisible line with a call // to Sys.init to start on - the builtin version will be called. addCallBuiltInSysInit = true; getAddress("Sys.init"); // confirm calling the built-in Sys.init ++nextPC; // A "call Sys.init 0" line will be added } instructions = new VMEmulatorInstruction[nextPC + 4]; // Second scan nextPC = 0; currentStaticIndex = Definitions.VAR_START_ADDRESS; for (int i = 0; i < files.length; i++) { String name = files[i].getName(); String className = name.substring(0, name.indexOf(".")); largestStaticIndex = -1; int[] range = new int[2]; range[0] = currentStaticIndex; try { // functions is not passed as an argument since it is accessed // through getAddress() buildProgram(files[i], symbols); } catch (ProgramException pe) { if (displayChanges) gui.hideMessage(); throw new ProgramException(name + ": " + pe.getMessage()); } currentStaticIndex += largestStaticIndex + 1; range[1] = currentStaticIndex - 1; staticRange.put(className, range); } instructionsLength = visibleInstructionsLength = nextPC; if (builtInAccessStatus == BUILTIN_ACCESS_AUTHORIZED) { // Add some "invisible" code in the end to make everything work instructionsLength += 4; if (addCallBuiltInSysInit) { instructionsLength += 1; } short indexInInvisibleCode = 0; // Add a jump to the end (noone should get here since // both calls to built-in functions indicate that // that this is a function-based program and not a script // a-la proj7, but just to be on the safe side...). instructions[nextPC] = new VMEmulatorInstruction( HVMInstructionSet.GOTO_CODE, (short) instructionsLength, indexInInvisibleCode); instructions[nextPC].setStringArg("afterInvisibleCode"); nextPC++; // Add a small infinite loop for built-in // methods to call (for example when Sys.halt is // called it must call a non-built-in infinite loop // because otherwise the current script would not // finish running - a problem for the OS tests. instructions[nextPC] = new VMEmulatorInstruction(HVMInstructionSet.LABEL_CODE, (short) -1); instructions[nextPC].setStringArg("infiniteLoopForBuiltIns"); nextPC++; infiniteLoopForBuiltInsAddress = nextPC; instructions[nextPC] = new VMEmulatorInstruction(HVMInstructionSet.GOTO_CODE, nextPC, ++indexInInvisibleCode); instructions[nextPC].setStringArg("infiniteLoopForBuiltIns"); nextPC++; if (addCallBuiltInSysInit) { // Add a call to the built-in Sys.init instructions[nextPC] = new VMEmulatorInstruction( HVMInstructionSet.CALL_CODE, getAddress("Sys.init"), (short) 0, ++indexInInvisibleCode); instructions[nextPC].setStringArg("Sys.init"); startAddress = nextPC; nextPC++; } // Add the label that the first invisible code line jumps to instructions[nextPC] = new VMEmulatorInstruction(HVMInstructionSet.LABEL_CODE, (short) -1); instructions[nextPC].setStringArg("afterInvisibleCode"); nextPC++; } if (!addCallBuiltInSysInit) { Short sysInitAddress = (Short) symbols.get("Sys.init"); if (sysInitAddress == null) // Single file, no Sys.init - start at 0 startAddress = 0; else // Implemented Sys.init - start there startAddress = sysInitAddress.shortValue(); } if (displayChanges) gui.hideMessage(); nextPC = startAddress; setGUIContents(); notifyProgramListeners(ProgramEvent.LOAD, fileName); }
public void refreshGUI() { if (displayChanges) { gui.setContents(instructions, visibleInstructionsLength); gui.setCurrentInstruction(nextPC); } }
// Sets the GUI's current instruction index private void setGUIPC() { if (displayChanges) gui.setCurrentInstruction(nextPC); }
// Sets the gui's contents (if a gui exists) private void setGUIContents() { if (displayChanges) { gui.setContents(instructions, visibleInstructionsLength); gui.setCurrentInstruction(nextPC); } }
/** * Sets the program counter to a specially created infinite loop in the end of the programs for * access by built-in functions, 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 setPCToInfiniteLoopForBuiltIns(String message) { if (hasGUI) { gui.notify(message); } setPC(infiniteLoopForBuiltInsAddress); }