/**
   * 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");
    }
  }
 /** Registers the given ErrorEventListener as a listener to this GUI. */
 public void addErrorListener(ErrorEventListener listener) {
   errorEventListeners.addElement(listener);
 }
 /** Highlights the value at the given index. */
 public void highlight(int index) {
   highlightIndex.addElement(new Integer(index));
   repaint();
 }
 /** Registers the given ComputerPartEventListener as a listener to this GUI. */
 public void addListener(ComputerPartEventListener listener) {
   listeners.addElement(listener);
 }