/********************************************************************************
* 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 builtInChips;
import Hack.Assembler.AssemblerException;
import Hack.Assembler.HackAssemblerTranslator;
import Hack.CPUEmulator.ROM;
import Hack.ComputerParts.ComputerPartEvent;
import Hack.ComputerParts.ComputerPartEventListener;
import Hack.Events.ProgramEvent;
import Hack.Events.ProgramEventListener;
import Hack.Gates.BuiltInGateWithGUI;
import Hack.Gates.GateException;
import Hack.Gates.GatesManager;
import Hack.Utilities.Definitions;
import SimulatorsGUI.ROMComponent;
import java.awt.Component;
/**
* /* A Read only memory of 32K registers, each 16 bit-wide. The output is the value /* stored at
* the memory location specified by the 15-bit address.
*/
public class ROM32K extends BuiltInGateWithGUI
implements ComputerPartEventListener, ProgramEventListener {
// The gui
private ROMComponent gui;
// The memory array
private short[] values;
/**
* Constructs a new ROM32K.
*/
public ROM32K() {
values = new short[Definitions.ROM_SIZE];
if (GatesManager.getInstance().isChipsGUIEnabled()) {
gui = new ROMComponent();
gui.setContents(values);
gui.setVisibleRows(7);
gui.setLocation(326, 295);
gui.setName("ROM:");
gui.reset();
gui.addListener(this);
gui.addProgramListener(this);
gui.addErrorListener(this);
}
}
@Override
protected void reCompute() {
short address = inputPins[0].get(); // 15 bit address
outputPins[0].set(values[address]);
if (gui != null) {
gui.setPointer(address);
}
}
@Override
public Component getGUIComponent() {
return gui;
}
/**
* Loads the given file into the rom.
*/
protected void loadProgram(String fileName) throws AssemblerException {
short[] program = HackAssemblerTranslator.loadProgram(fileName,
Definitions.ROM_SIZE,
(short) 0);
if (gui != null) {
if (fileName.endsWith(".hack")) {
gui.setNumericFormat(ROM.BINARY_FORMAT);
} else {
gui.setNumericFormat(ROM.ASM_FORMAT);
}
}
values = program;
if (gui != null) {
gui.setProgram(fileName);
gui.setContents(values);
}
reCompute();
evalParent();
}
/**
* Called when the ROM's current program is changed. The event contains the source object, and
* the new program's file name.
*/
@Override
public void programChanged(ProgramEvent event) {
clearErrorListeners();
if (gui != null) {
gui.showMessage("Loading...");
}
try {
loadProgram(event.getProgramFileName());
} catch (AssemblerException pe) {
notifyErrorListeners(pe.getMessage());
}
if (gui != null) {
gui.hideMessage();
}
}
/**
* Called when the contents of the memory are changed through the memory gui.
*/
@Override
public void valueChanged(ComputerPartEvent event) {
short newValue = event.getValue();
int newAddress = event.getIndex();
clearErrorListeners();
try {
HackAssemblerTranslator.getInstance().codeToText(newValue);
updateValue(newAddress, newValue);
} catch (AssemblerException ae) {
notifyErrorListeners("Illegal instruction");
if (gui != null) {
gui.setValueAt(newAddress, values[newAddress]);
}
}
}
// updates the given value
private void updateValue(int address, short value) {
values[address] = value;
if (gui != null) {
gui.setValueAt(address, value);
}
reCompute();
evalParent();
}
@Override
public void guiGainedFocus() {
}
@Override
public short getValueAt(int index) throws GateException {
checkIndex(index);
return values[index];
}
// checks the given index. If illegal throws GateException.
private void checkIndex(int index) throws GateException {
if (index < 0 || index >= values.length) {
throw new GateException("Illegal index");
}
}
@Override
public void setValueAt(int index, short value) throws GateException {
checkIndex(index);
updateValue(index, value);
}
/**
* Executes the given command, given in args[] style. Subclasses may override this method to
* implement commands.
*/
@Override
public void doCommand(String[] command) throws GateException {
if (command[0].toUpperCase().equalsIgnoreCase("LOAD")) {
if (command.length != 2) {
throw new GateException("Illegal number of arguments");
}
if (gui != null) {
gui.showMessage("Loading...");
}
String fileName = GatesManager.getInstance().getWorkingDir() + "/" + command[1];
try {
loadProgram(fileName);
} catch (AssemblerException pe) {
if (gui != null) {
gui.hideMessage();
}
throw new GateException(pe.getMessage());
}
if (gui != null) {
gui.hideMessage();
}
} else {
throw new GateException("This chip doesn't support this command");
}
}
}