/********************************************************************************
* 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.Assembler.AssemblerException;
import Hack.Assembler.HackAssemblerTranslator;
import Hack.ComputerParts.ComputerPartEvent;
import Hack.ComputerParts.PointedMemory;
import Hack.Controller.HackController;
import Hack.Controller.ProgramException;
import Hack.Events.ProgramEvent;
import Hack.Events.ProgramEventListener;
import Hack.Utilities.Definitions;
import java.util.Vector;
/**
* A Read Only Memory. Has methods for loading a machine language file (.hack) and for
* setting a pointer (a specific address in the ROM for GUI perposes).
*/
public class ROM extends PointedMemory implements ProgramEventListener
{
/**
* Decimal numeric format
*/
public static final int DECIMAL_FORMAT = HackController.DECIMAL_FORMAT;
/**
* Hexadecimal numeric format
*/
public static final int HEXA_FORMAT = HackController.HEXA_FORMAT;;
/**
* Binary numeric format
*/
public static final int BINARY_FORMAT = HackController.BINARY_FORMAT;;
/**
* Assembler format
*/
public static final int ASM_FORMAT = 4;
// listeners to program changes
private Vector listeners;
/**
* Constructs a new ROM with the given ROM GUI.
*/
public ROM(ROMGUI gui) {
super(Definitions.ROM_SIZE, gui);
setNullValue(HackAssemblerTranslator.NOP, true);
listeners = new Vector();
if (hasGUI) {
gui.addProgramListener( (ProgramEventListener)this);
gui.setNumericFormat(ASM_FORMAT); // enable assembler
}
}
/**
* Loads the given program file (HACK or ASM) into the ROM.
*/
public synchronized void loadProgram(String fileName) throws ProgramException {
short[] program = null;
if (displayChanges)
((ROMGUI)gui).showMessage("Loading...");
try {
program = HackAssemblerTranslator.loadProgram(fileName, Definitions.ROM_SIZE,
HackAssemblerTranslator.NOP);
mem = program;
if (displayChanges) {
gui.setContents(mem);
((ROMGUI)gui).setProgram(fileName);
((ROMGUI)gui).hideMessage();
gui.hideHighlight();
}
notifyProgramListeners(ProgramEvent.LOAD, fileName);
} catch (AssemblerException ae) {
if (displayChanges)
((ROMGUI)gui).hideMessage();
throw new ProgramException(ae.getMessage());
}
}
/**
* 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) {
switch (event.getType()) {
case ProgramEvent.LOAD:
ROMLoadProgramTask task = new ROMLoadProgramTask(event.getProgramFileName());
Thread t = new Thread(task);
t.start();
break;
case ProgramEvent.CLEAR:
notifyProgramListeners(ProgramEvent.CLEAR, null);
}
}
/**
* Called when the contents of the memory are changed through the memory gui.
*/
public void valueChanged(ComputerPartEvent event) {
short newValue = event.getValue();
int newAddress = event.getIndex();
clearErrorListeners();
try {
HackAssemblerTranslator.getInstance().codeToText(newValue);
setValueAt(newAddress, newValue, true);
} catch (AssemblerException ae) {
notifyErrorListeners("Illegal instruction");
quietUpdateGUI(newAddress, mem[newAddress]);
}
}
class ROMLoadProgramTask implements Runnable {
private String programName;
public ROMLoadProgramTask(String programName) {
this.programName = programName;
}
public void run() {
clearErrorListeners();
try {
loadProgram(programName);
} catch (ProgramException pe) {
notifyErrorListeners(pe.getMessage());
}
}
}
/**
* Registers the given ProgramEventListener as a listener to this GUI.
*/
public void addProgramListener(ProgramEventListener listener) {
listeners.add(listener);
}
/**
* Un-registers the given ProgramEventListener from being a listener to this GUI.
*/
public void removeProgramListener(ProgramEventListener listener) {
listeners.remove(listener);
}
/**
* Notifies all the ProgramEventListeners on a change in the ROM's program by creating
* a ProgramEvent (with the new event type and program's file name) and sending it using the
* programChanged method to all the listeners.
*/
protected void notifyProgramListeners(byte eventType, String programFileName) {
ProgramEvent event = new ProgramEvent(this, eventType, programFileName);
for (int i = 0; i < listeners.size(); i++) {
((ProgramEventListener)listeners.elementAt(i)).programChanged(event);
}
}
}