Package Hack.CPUEmulator

Source Code of Hack.CPUEmulator.CPUEmulator

/********************************************************************************
* The contents of this file are subject to the GNU General Public License      *
* (GPL) Version 2 or later (the "License"); you may not use this file except   *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.gnu.org/copyleft/gpl.html                                         *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* without warranty of any kind, either expressed or implied. See the License   *
* for the specific language governing rights and limitations under the         *
* License.                                                                     *
*                                                                              *
* This file was originally developed as part of the software suite that        *
* supports the book "The Elements of Computing Systems" by Nisan and Schocken, *
* MIT Press 2005. If you modify the contents of this file, please document and *
* mark your changes clearly, for the benefit of others.                        *
********************************************************************************/

package Hack.CPUEmulator;

import Hack.ComputerParts.Bus;
import Hack.ComputerParts.ComputerPartErrorEvent;
import Hack.ComputerParts.ComputerPartErrorEventListener;
import Hack.ComputerParts.Register;
import Hack.Controller.*;
import Hack.Events.ProgramEvent;
import Hack.Utilities.Conversions;
import Hack.Utilities.Definitions;

/**
* A CPU Emulator. Emulates machine code (In HACK format).
*
* Recognizes the following variables:
* A - the address register (short)
* D - the data register (short)
* PC - the program counter (short)
* RAM[i] - the contents of the RAM at location i (short)
* ROM[i] - the contents of the ROM at location i (short)
* time - the time that passed since the program started running (long) - READ ONLY
*
* Recognizes the following commands:
* load <HACK file name> - loads the given file into the ROM
* TickTock - advances the clock by one time unit (executes one instruction)
*/
public class CPUEmulator extends HackSimulator implements ComputerPartErrorEventListener {


    // Variables
    private static final String VAR_A = "A";
    private static final String VAR_D = "D";
    private static final String VAR_PC = "PC";
    private static final String VAR_RAM = "RAM";
    private static final String VAR_ROM = "ROM";
    private static final String VAR_TIME = "time";

    // Commands
    private static final String COMMAND_TICKTOCK = "ticktock";
    private static final String COMMAND_ROMLOAD = "load";
    private static final String COMMAND_SETVAR = "set";

    // The simulating cpu
    private CPU cpu;

    // The GUI of the CPUEmulator
    private CPUEmulatorGUI gui;

    // The list of recognized variables.
    private String[] vars;

    // The keyboard
    private Keyboard keyboard;

    // The current animation mode
    private int animationMode;

    /**
     * Constructs a new CPU Emulator with no GUI component.
     */
    public CPUEmulator() {
        RAM ram = new RAM(null, null, null);
        ram.reset();

        ROM rom = new ROM(null);
        rom.reset();

        PointerAddressRegisterAdapter A = new PointerAddressRegisterAdapter(null, ram);
        A.reset();

        Register D = new Register(null);
        D.reset();

        PointerAddressRegisterAdapter PC = new PointerAddressRegisterAdapter(null, rom);
        PC.reset();

        keyboard = new Keyboard(ram, null);
        keyboard.reset();

        ALU alu = new ALU(null);
        alu.reset();

        Bus bus = new Bus(null);
        bus.reset();

        cpu = new CPU(ram, rom, A, D, PC, alu, bus);

        init();
    }

    /**
     * Constructs a new CPU Emulator with the given GUI component.
     */
    public CPUEmulator(CPUEmulatorGUI gui) {
        this.gui = gui;

        RAM ram = new RAM(gui.getRAM(), null, gui.getScreen());
        ram.addErrorListener(this);
        ram.reset();

        ROM rom = new ROM(gui.getROM());
        rom.addErrorListener(this);
        rom.addProgramListener(this); // listens to program file change
        rom.reset();

        PointerAddressRegisterAdapter A = new PointerAddressRegisterAdapter(gui.getA(), ram);
        A.addErrorListener(this);
        A.reset();

        Register D = new Register(gui.getD());
        D.addErrorListener(this);
        D.reset();

        PointerAddressRegisterAdapter PC = new PointerAddressRegisterAdapter(gui.getPC(), rom);
        PC.addErrorListener(this);
        PC.reset();

        keyboard = new Keyboard(ram, gui.getKeyboard());
        keyboard.reset();

        ALU alu = new ALU(gui.getALU());
        alu.reset();

        Bus bus = new Bus(gui.getBus());
        bus.reset();

        cpu = new CPU(ram, rom, A, D, PC, alu, bus);

        init();
    }

    // Initializes the emulator
    private void init() {
        vars = new String[]{VAR_A, VAR_D, VAR_PC, VAR_RAM + "[]", VAR_ROM + "[]", VAR_TIME};
    }

    public String getName() {
        return "CPU Emulator";
    }

    /**
     * Returns the value of the given variable.
     * Throws VariableException if the variable is not legal.
     */
    public String getValue(String varName) throws VariableException {
        if (varName.equals(VAR_A))
            return String.valueOf(cpu.getA().get());
        else if (varName.equals(VAR_D))
            return String.valueOf(cpu.getD().get());
        else if (varName.equals(VAR_PC))
            return String.valueOf(cpu.getPC().get());
        else if (varName.equals(VAR_TIME))
            return String.valueOf(cpu.getTime());
        else if (varName.startsWith(VAR_RAM + "[")) {
            short index = getRamIndex(varName);
            return String.valueOf(cpu.getRAM().getValueAt(index));
        }
        else if (varName.startsWith(VAR_ROM + "[")) {
            short index = getRomIndex(varName);
            return String.valueOf(cpu.getROM().getValueAt(index));
        }
        else
            throw new VariableException("Unknown variable", varName);
    }

    /**
     * Sets the given variable with the given value.
     * Throws VariableException if the variable name or value are not legal.
     */
    public void setValue(String varName, String value) throws VariableException {
        int numValue;

        try {
            value = Conversions.toDecimalForm(value);

            if (varName.equals(VAR_A)) {
                numValue = Integer.parseInt(value);
                check_ram_address(varName, numValue);
                cpu.getA().store((short)numValue);
            }
            else if (varName.equals(VAR_D)) {
                numValue = Integer.parseInt(value);
                check_value(varName, numValue);
                cpu.getD().store((short)numValue);
            }
            else if (varName.equals(VAR_PC)) {
                numValue = Integer.parseInt(value);
                check_rom_address(varName, numValue);
                cpu.getPC().store((short)numValue);
            }
            else if (varName.equals(VAR_TIME))
                throw new VariableException("Read Only variable", varName);
            else if (varName.startsWith(VAR_RAM + "[")) {
                short index = getRamIndex(varName);
                numValue = Integer.parseInt(value);
                check_value(varName, numValue);
                cpu.getRAM().setValueAt(index, (short)numValue, false);
            }
            else if (varName.startsWith(VAR_ROM + "[")) {
                short index = getRomIndex(varName);
                numValue = Integer.parseInt(value);
                check_value(varName, numValue);
                cpu.getROM().setValueAt(index, (short)numValue, false);
            }
            else
                throw new VariableException("Unknown variable", varName);
        } catch (NumberFormatException nfe) {
            throw new VariableException("'" + value + "' is not a legal value for variable",
                                        varName);
        }
    }

    /**
     * Executes the given simulator command (given in args[] style).
     * Throws CommandException if the command is not legal.
     * Throws ProgramException if an error occurs in the program.
     */
    public void doCommand(String[] command)
     throws CommandException, ProgramException, VariableException {
        if (command.length == 0)
            throw new CommandException("Empty command", command);

        // hide gui highlights
        if (animationMode != HackController.NO_DISPLAY_CHANGES)
            hideHighlightes();

        // execute the appropriate command
        if (command[0].equals(COMMAND_TICKTOCK)) {
            if (command.length != 1)
                throw new CommandException("Illegal number of arguments to command", command);

            cpu.executeInstruction();
        }
        else if (command[0].equals(COMMAND_SETVAR)) {
            if (command.length != 3)
                throw new CommandException("Illegal number of arguments to command", command);
            setValue(command[1], command[2]);
        }
        else if (command[0].equals(COMMAND_ROMLOAD)) {
            if (command.length != 2)
                throw new CommandException("Illegal number of arguments to command", command);

            String fileName = getWorkingDir().getAbsolutePath() + "/" + command[1];
            cpu.getROM().loadProgram(fileName);
            int oldAnimationMode = animationMode;
            setAnimationMode(HackController.DISPLAY_CHANGES);
            cpu.initProgram();
            setAnimationMode(oldAnimationMode);
        }
        else
            throw new CommandException("Unknown simulator command", command);
    }

    // Hides all highlights in GUIs.
    private void hideHighlightes() {
        cpu.getRAM().hideHighlight();
        cpu.getROM().hideHighlight();
        cpu.getA().hideHighlight();
        cpu.getD().hideHighlight();
        cpu.getPC().hideHighlight();
        cpu.getALU().hideHighlight();
    }

    /**
     * Restarts the CPUEmulator - program will be restarted.
     */
    public void restart() {
        cpu.initProgram();
    }

    public void setAnimationMode(int newAnimationMode) {
        if (gui != null) {
            // enter NO_DISPLAY_CHANGES
            if (newAnimationMode == HackController.NO_DISPLAY_CHANGES &&
                    animationMode != HackController.NO_DISPLAY_CHANGES) {
                cpu.getRAM().disableUserInput();
                cpu.getROM().disableUserInput();
                cpu.getA().disableUserInput();
                cpu.getD().disableUserInput();
                cpu.getPC().disableUserInput();

                ScreenGUI screen = gui.getScreen();
                if (screen != null)
                    screen.startAnimation();
            }

            // exit NO_DISPLAY_CHANGES
            if (newAnimationMode != HackController.NO_DISPLAY_CHANGES &&
                    animationMode == HackController.NO_DISPLAY_CHANGES) {
                cpu.getRAM().enableUserInput();
                cpu.getROM().enableUserInput();
                cpu.getA().enableUserInput();
                cpu.getD().enableUserInput();
                cpu.getPC().enableUserInput();

                ScreenGUI screen = gui.getScreen();
                if (screen != null)
                    screen.stopAnimation();
            }
        }

        animationMode = newAnimationMode;

        boolean animate = (animationMode == HackController.ANIMATION);
        cpu.getBus().setAnimate(animate);
        cpu.getRAM().setAnimate(animate);
        cpu.getROM().setAnimate(animate);
        cpu.getA().setAnimate(animate);
        cpu.getD().setAnimate(animate);
        cpu.getPC().setAnimate(animate);
        cpu.getALU().setAnimate(animate);

        boolean displayChanges = (animationMode != HackController.NO_DISPLAY_CHANGES);
        cpu.getRAM().setDisplayChanges(displayChanges);
        cpu.getROM().setDisplayChanges(displayChanges);
        cpu.getA().setDisplayChanges(displayChanges);
        cpu.getD().setDisplayChanges(displayChanges);
        cpu.getPC().setDisplayChanges(displayChanges);
        cpu.getALU().setDisplayChanges(displayChanges);
    }

    public void setNumericFormat(int formatCode) {
        cpu.getRAM().setNumericFormat(formatCode);
        cpu.getA().setNumericFormat(formatCode);
        cpu.getD().setNumericFormat(formatCode);
        cpu.getPC().setNumericFormat(formatCode);
        cpu.getALU().setNumericFormat(formatCode);
    }

    public void setAnimationSpeed(int speedUnit) {
        cpu.getBus().setAnimationSpeed(speedUnit);
    }

    public int getInitialAnimationMode() {
        return HackController.DISPLAY_CHANGES;
    }

    public int getInitialNumericFormat() {
        return HackController.DECIMAL_FORMAT;
    }

    public void refresh() {
        cpu.getBus().refreshGUI();
        cpu.getRAM().refreshGUI();
        cpu.getROM().refreshGUI();
        cpu.getA().refreshGUI();
        cpu.getD().refreshGUI();
        cpu.getPC().refreshGUI();
        cpu.getALU().refreshGUI();

        ScreenGUI screen = gui.getScreen();
        if (screen != null)
            screen.refresh();

    }

    public void prepareFastForward() {
        gui.requestFocus();
        keyboard.requestFocus();
    }

    public void prepareGUI() {
    }

    public String[] getVariables() {
        return vars;
    }

    protected HackSimulatorGUI getGUI() {
        return gui;
    }

    /**
     * Called when the ROM's current program is changed.
     * The event contains the source object, event type and the new program's file name (if any).
     */
    public void programChanged(ProgramEvent event) {
        super.programChanged(event);

        if (event.getType() == ProgramEvent.LOAD) {
            int oldAnimationMode = animationMode;
            setAnimationMode(HackController.DISPLAY_CHANGES);

            refresh();
            notifyListeners(ControllerEvent.ENABLE_MOVEMENT, null);
            cpu.initProgram();

            setAnimationMode(oldAnimationMode);
        }
    }

    /**
     * Called when an error occured in a computer part.
     * The event contains the source computer part and the error message.
     */
    public void computerPartErrorOccured(ComputerPartErrorEvent event) {
        displayMessage(event.getErrorMessage(), true);
    }

    // receives a variable name of the form xxx[i] and returns the numeric
    // value of i, which is an address in the RAM.
    // Throws VariableException if i is not a legal address in the RAM.
    private static short getRamIndex(String varName) throws VariableException {
        if (varName.indexOf("]") == -1)
            throw new VariableException("Missing ']'", varName);

        String indexStr = varName.substring(varName.indexOf("[") + 1, varName.indexOf("]"));
        int index = Integer.parseInt(indexStr);
        if (index < 0 || index >= Definitions.RAM_SIZE)
            throw new VariableException("Illegal variable index", varName);

        return (short)index;
    }

    // receives a variable name of the form xxx[i] and returns the numeric
    // value of i, which is an address in the ROM.
    // Throws VariableException if i is not a legal address in the ROM.
    private static short getRomIndex(String varName) throws VariableException {
        if (varName.indexOf("]") == -1)
            throw new VariableException("Missing ']'", varName);

        String indexStr = varName.substring(varName.indexOf("[") + 1, varName.indexOf("]"));
        int index = Integer.parseInt(indexStr);
        if (index < 0 || index >= Definitions.ROM_SIZE)
            throw new VariableException("Illegal variable index", varName);

        return (short)index;
    }

    // Checks that the given value is a legal 16-bit value
    private void check_value(String varName, int value) throws VariableException {
        if (value < -32768 || value >= 32768)
            throw new VariableException(value +
                " is an illegal value for variable", varName);
    }

    // Checks that the given value is a legal 16-bit address
    private void check_ram_address(String varName, int value) throws VariableException {
        if (value < 0 || value >= Definitions.RAM_SIZE)
            throw new VariableException(value +
                " is an illegal value for", varName);
    }

    // Checks that the given value is a legal 16-bit address
    private void check_rom_address(String varName, int value) throws VariableException {
        if (value < 0 || value >= Definitions.ROM_SIZE)
            throw new VariableException(value +
                " is an illegal value for", varName);
    }
}
TOP

Related Classes of Hack.CPUEmulator.CPUEmulator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.