Its possible to edit ram content during operation

This commit is contained in:
hneemann 2016-03-27 14:28:10 +02:00
parent 0a8afe201b
commit db5dfdc7c0
19 changed files with 197 additions and 50 deletions

View File

@ -3,6 +3,7 @@ package de.neemann.digital.core.memory;
import de.neemann.digital.lang.Lang;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
/**
@ -10,22 +11,26 @@ import java.util.Arrays;
*/
public class DataField {
public static final DataField DEFAULT = new DataField(0);
public static final DataField DEFAULT = new DataField(0, 8);
private final int size;
private long[] data;
private final int bits;
public DataField(int size) {
this(new long[size], size);
private transient ArrayList<DataListener> listeners;
public DataField(int size, int bits) {
this(new long[size], size, bits);
}
private DataField(long[] data, int size) {
private DataField(long[] data, int size, int bits) {
this.size = size;
this.data = data;
this.bits = bits;
}
public DataField(DataField dataField, int newSize) {
this(Arrays.copyOf(dataField.data, newSize), newSize);
public DataField(DataField dataField, int newSize, int bits) {
this(Arrays.copyOf(dataField.data, newSize), newSize, bits);
}
public DataField(File file) throws IOException {
@ -53,13 +58,18 @@ public class DataField {
}
size = pos;
}
bits = 16;
}
public void setData(int addr, long value) {
if (addr < size) {
if (addr >= data.length)
data = Arrays.copyOf(data, size);
data[addr] = value;
if (data[addr] != value) {
data[addr] = value;
fireChanged(addr);
}
}
}
@ -80,6 +90,38 @@ public class DataField {
if (pos == data.length)
return this;
else
return new DataField(Arrays.copyOf(data, pos), size);
return new DataField(Arrays.copyOf(data, pos), size, bits);
}
public int getBits() {
return bits;
}
public void addListener(DataListener l) {
if (listeners == null)
listeners = new ArrayList<>();
listeners.add(l);
}
public void removeListener(DataListener l) {
if (listeners == null)
return;
listeners.remove(l);
if (listeners.isEmpty())
listeners = null;
}
public void fireChanged(int addr) {
if (listeners == null)
return;
for (DataListener l : listeners)
l.valueChanged(addr);
}
public interface DataListener {
void valueChanged(int addr);
}
}

View File

@ -11,12 +11,13 @@ import de.neemann.digital.core.element.ElementTypeDescription;
/**
* @author hneemann
*/
public class RAMDualPort extends Node implements Element {
public class RAMDualPort extends Node implements Element, RAMInterface {
public static final ElementTypeDescription DESCRIPTION = new ElementTypeDescription(RAMDualPort.class, "A", "D", "str", "c", "ld")
.addAttribute(AttributeKey.Bits)
.addAttribute(AttributeKey.AddrBits)
.addAttribute(AttributeKey.Label)
.setShortName("RAM");
private final DataField memory;
@ -36,7 +37,7 @@ public class RAMDualPort extends Node implements Element {
bits = attr.get(AttributeKey.Bits);
output = createOutput();
addrBits = attr.get(AttributeKey.AddrBits);
memory = new DataField(1 << addrBits);
memory = new DataField(1 << addrBits, bits);
}
protected ObservableValue createOutput() {
@ -87,4 +88,8 @@ public class RAMDualPort extends Node implements Element {
}
}
@Override
public DataField getMemory() {
return memory;
}
}

View File

@ -0,0 +1,8 @@
package de.neemann.digital.core.memory;
/**
* @author hneemann
*/
public interface RAMInterface {
DataField getMemory();
}

View File

@ -11,16 +11,22 @@ import de.neemann.digital.core.element.ElementTypeDescription;
*/
public class RAMSinglePort extends RAMDualPort {
public static final ElementTypeDescription DESCRIPTION = new ElementTypeDescription(RAMSinglePort.class, "A", "str", "c", "ld")
.addAttribute(AttributeKey.Bits)
.addAttribute(AttributeKey.AddrBits)
.addAttribute(AttributeKey.Label)
.setShortName("RAM");
public RAMSinglePort(ElementAttributes attr) {
super(attr);
}
/**
* If a port is bidirectional an additional input comes with <code>setInputs</code>.
* Using this input you can read back the output port.
*
* @return outputs
*/
@Override
protected ObservableValue createOutput() {
return super.createOutput().setBidirectional(true);
@ -32,7 +38,7 @@ public class RAMSinglePort extends RAMDualPort {
strIn = inputs[1].checkBits(1, this).addObserver(this);
clkIn = inputs[2].checkBits(1, this).addObserver(this);
ldIn = inputs[3].checkBits(1, this).addObserver(this);
dataIn = inputs[4].checkBits(bits, this).addObserver(this);
dataIn = inputs[4].checkBits(bits, this).addObserver(this); // additional input to read the port
}
}

View File

@ -17,6 +17,7 @@ public class ROM extends Node implements Element {
public static final ElementTypeDescription DESCRIPTION = new ElementTypeDescription(ROM.class, "A", "sel")
.addAttribute(AttributeKey.Bits)
.addAttribute(AttributeKey.AddrBits)
.addAttribute(AttributeKey.Label)
.addAttribute(AttributeKey.Data);
private final DataField data;

View File

@ -3,6 +3,7 @@ package de.neemann.digital.draw.elements;
import de.neemann.digital.core.Observer;
import de.neemann.digital.core.element.AttributeKey;
import de.neemann.digital.core.element.AttributeListener;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.shapes.Drawable;
@ -27,6 +28,7 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
private transient IOState ioState;
private transient Interactor interactor;
private transient boolean highLight = false;
private transient Element element;
private Vector pos;
private int rotate;
@ -182,7 +184,7 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
public void clicked(CircuitComponent cc, Point pos) {
if (interactor != null)
interactor.clicked(cc, pos, ioState);
interactor.clicked(cc, pos, ioState, element);
}
@Override
@ -203,4 +205,12 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
else
return elementName;
}
public void setElement(Element element) {
this.element = element;
}
public Element getElement() {
return element;
}
}

View File

@ -16,7 +16,6 @@ import java.util.ArrayList;
public class ModelBuilder {
private final Circuit circuit;
private boolean bindWiresToGui = true;
private boolean disableClock = false;
private boolean enableTrace = false;
private ModelDescription modelDescription;
@ -25,11 +24,6 @@ public class ModelBuilder {
this.circuit = circuit;
}
public ModelBuilder setBindWiresToGui(boolean bindWiresToGui) {
this.bindWiresToGui = bindWiresToGui;
return this;
}
public ModelBuilder setDisableClock(boolean disableClock) {
this.disableClock = disableClock;
return this;
@ -42,7 +36,7 @@ public class ModelBuilder {
public Model build(ElementLibrary library) throws PinException, NodeException {
modelDescription = new ModelDescription(circuit, library);
Model model = modelDescription.createModel(bindWiresToGui);
Model model = modelDescription.createModel();
if (enableTrace) {

View File

@ -49,10 +49,11 @@ public class ModelDescription implements Iterable<ModelEntry> {
else
ioMap = null;
for (VisualElement vp : circuit.getElements()) {
Pins pins = vp.getPins();
ElementTypeDescription elementType = library.getElementType(vp.getElementName());
Element element = elementType.createElement(vp.getElementAttributes());
for (VisualElement ve : circuit.getElements()) {
Pins pins = ve.getPins();
ElementTypeDescription elementType = library.getElementType(ve.getElementName());
Element element = elementType.createElement(ve.getElementAttributes());
ve.setElement(element);
pins.bindOutputsToOutputPins(element.getOutputs());
@ -60,7 +61,7 @@ public class ModelDescription implements Iterable<ModelEntry> {
boolean isNotAIO = true;
if (readAsCustom) {
if (elementType == In.DESCRIPTION || elementType == Out.DESCRIPTION) {
String label = vp.getElementAttributes().get(AttributeKey.Label);
String label = ve.getElementAttributes().get(AttributeKey.Label);
if (label == null || label.length() == 0)
throw new PinException(Lang.get("err_pinWithoutName"));
if (pins.size() != 1)
@ -72,7 +73,7 @@ public class ModelDescription implements Iterable<ModelEntry> {
}
if (isNotAIO)
entries.add(new ModelEntry(element, pins, vp, elementType.getInputNames(vp.getElementAttributes())));
entries.add(new ModelEntry(element, pins, ve, elementType.getInputNames(ve.getElementAttributes())));
for (Pin p : pins)
netList.add(p);
@ -129,14 +130,13 @@ public class ModelDescription implements Iterable<ModelEntry> {
/**
* Creates the model
*
* @param bindWiresToValues
* @return the model
* @throws PinException
* @throws NodeException
*/
public Model createModel(boolean bindWiresToValues) throws PinException, NodeException {
public Model createModel() throws PinException, NodeException {
for (Net n : netList)
n.interconnect(bindWiresToValues);
n.interconnect();
Model m = new Model();

View File

@ -71,7 +71,7 @@ public class Net {
pins.addAll(p);
}
public void interconnect(boolean bindWiresToValues) throws PinException {
public void interconnect() throws PinException {
ArrayList<Pin> inputs = new ArrayList<>();
ArrayList<Pin> outputs = new ArrayList<>();
for (Pin p : pins) {
@ -100,7 +100,7 @@ public class Net {
for (Pin o : outputs) // set also the reader for bidirectional pins
o.setReaderValue(value);
if (bindWiresToValues && wires != null)
if (wires != null)
for (Wire w : wires)
w.setValue(value);
}

View File

@ -2,6 +2,7 @@ package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.Observer;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.draw.elements.Pin;
import de.neemann.digital.draw.elements.Pins;
@ -34,7 +35,7 @@ public class ClockShape implements Shape {
ioState.getOutput(0).addObserver(guiObserver); // necessary to replot wires also if component itself does not depend on state
return new Interactor() {
@Override
public void clicked(CircuitComponent cc, Point pos, IOState ioState) {
public void clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
ObservableValue value = ioState.getOutput(0);
if (value.getBits() == 1) {
value.setValue(1 - value.getValue());

View File

@ -2,6 +2,7 @@ package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.Observer;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.draw.elements.Pin;
import de.neemann.digital.draw.elements.Pins;
@ -38,7 +39,7 @@ public class InputShape implements Shape {
ioState.getOutput(0).addObserver(guiObserver);
return new Interactor() {
@Override
public void clicked(CircuitComponent cc, Point pos, IOState ioState) {
public void clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
ObservableValue value = ioState.getOutput(0);
if (value.getBits() == 1) {
value.setValue(1 - value.getValue());

View File

@ -1,5 +1,6 @@
package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.gui.components.CircuitComponent;
@ -22,5 +23,5 @@ public interface Interactor {
* @param pos the popuplocation on screen
* @param ioState the state of the element
*/
void clicked(CircuitComponent cc, Point pos, IOState ioState);
void clicked(CircuitComponent cc, Point pos, IOState ioState, Element element);
}

View File

@ -0,0 +1,33 @@
package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.Observer;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.core.memory.DataField;
import de.neemann.digital.core.memory.RAMInterface;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.components.DataEditor;
import java.awt.*;
/**
* @author hneemann
*/
public class RAMShape extends GenericShape {
public RAMShape(String name, String[] inputs, OutputPinInfo[] outputs, String label) {
super(name, inputs, outputs, label, true);
}
@Override
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
return new Interactor() {
@Override
public void clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
if (element instanceof RAMInterface) {
DataField dataField = ((RAMInterface) element).getMemory();
new DataEditor(cc, dataField).showDialog();
}
}
};
}
}

View File

@ -12,6 +12,8 @@ import de.neemann.digital.core.element.ElementTypeDescription;
import de.neemann.digital.core.io.Const;
import de.neemann.digital.core.io.In;
import de.neemann.digital.core.io.Out;
import de.neemann.digital.core.memory.RAMDualPort;
import de.neemann.digital.core.memory.RAMSinglePort;
import de.neemann.digital.core.wiring.*;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.lang.Lang;
@ -49,6 +51,8 @@ public final class ShapeFactory {
map.put(Sub.DESCRIPTION.getName(), attr -> new GenericShape("-", Sub.DESCRIPTION.getInputNames(attr), outputInfos(Sub.DESCRIPTION, attr), null, true));
map.put(Mul.DESCRIPTION.getName(), attr -> new GenericShape("*", Mul.DESCRIPTION.getInputNames(attr), outputInfos(Mul.DESCRIPTION, attr), null, true));
map.put(RAMDualPort.DESCRIPTION.getName(), attr -> new RAMShape("RAM", RAMDualPort.DESCRIPTION.getInputNames(attr), outputInfos(RAMDualPort.DESCRIPTION, attr), attr.get(AttributeKey.Label)));
map.put(RAMSinglePort.DESCRIPTION.getName(), attr -> new RAMShape("RAM", RAMSinglePort.DESCRIPTION.getInputNames(attr), outputInfos(RAMSinglePort.DESCRIPTION, attr), attr.get(AttributeKey.Label)));
map.put(In.DESCRIPTION.getName(), attr -> new InputShape(attr.get(AttributeKey.Label)));
map.put(Const.DESCRIPTION.getName(), attr -> new ConstShape(attr.get(AttributeKey.Value)));

View File

@ -56,7 +56,6 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
super(Lang.get("digital"));
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
Circuit cr = new Circuit();
circuitComponent = new CircuitComponent(cr, library);
String name = prefs.get("name", null);
@ -195,7 +194,6 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
public void actionPerformed(ActionEvent e) {
try {
Model model = new ModelBuilder(circuitComponent.getCircuit())
.setBindWiresToGui(false)
.build(library);
SpeedTest speedTest = new SpeedTest(model);
@ -255,7 +253,6 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
circuitComponent.setModeAndReset(CircuitComponent.Mode.running);
mb = new ModelBuilder(circuitComponent.getCircuit())
.setBindWiresToGui(true)
.setDisableClock(!runClock)
.setEnableTrace(traceEnable.isSelected(), Main.this);

View File

@ -6,11 +6,14 @@ import de.neemann.digital.core.memory.DataField;
import de.neemann.digital.lang.Lang;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
/**
@ -20,26 +23,54 @@ public class DataEditor extends JDialog {
private final DataField dataField;
private boolean ok = false;
public DataEditor(JComponent parent, DataField dataField) {
this(parent, dataField, null);
}
public DataEditor(JComponent parent, DataField dataField, ElementAttributes attr) {
super(SwingUtilities.windowForComponent(parent), Lang.get("key_data"), ModalityType.APPLICATION_MODAL);
super(SwingUtilities.windowForComponent(parent), Lang.get("key_data"), attr == null ? ModalityType.MODELESS : ModalityType.APPLICATION_MODAL);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
int bits = attr.getBits();
int size;
if (attr.contains(AttributeKey.AddrBits))
size = 1 << attr.get(AttributeKey.AddrBits);
else
size = 1 << attr.get(AttributeKey.InputCount);
int bits;
boolean register;
if (attr != null) {
bits = attr.getBits();
if (attr.contains(AttributeKey.AddrBits))
size = 1 << attr.get(AttributeKey.AddrBits);
else
size = 1 << attr.get(AttributeKey.InputCount);
this.dataField = new DataField(dataField, size);
this.dataField = new DataField(dataField, size, bits);
register = false;
} else {
this.dataField = dataField;
size = this.dataField.size();
bits = this.dataField.getBits();
register = true;
}
int cols = 16;
if (size <= 16) cols = 1;
else if (size <= 128) cols = 8;
JTable table = new JTable(new MyTableModel(this.dataField, cols));
MyTableModel dm = new MyTableModel(this.dataField, cols);
JTable table = new JTable(dm);
table.setDefaultRenderer(MyLong.class, new MyLongRenderer(bits));
getContentPane().add(new JScrollPane(table));
if (register) {
this.dataField.addListener(dm);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
DataEditor.this.dataField.removeListener(dm);
}
});
}
JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
buttons.add(new JButton(new AbstractAction(Lang.get("ok")) {
@Override
@ -50,7 +81,10 @@ public class DataEditor extends JDialog {
}));
getContentPane().add(buttons, BorderLayout.SOUTH);
setPreferredSize(new Dimension((cols + 1) * 50, getPreferredSize().height));
pack();
setLocationRelativeTo(parent);
}
@ -63,7 +97,7 @@ public class DataEditor extends JDialog {
return ok;
}
private class MyTableModel implements TableModel {
private class MyTableModel implements TableModel, DataField.DataListener {
private final DataField dataField;
private final int cols;
private final int rows;
@ -118,6 +152,11 @@ public class DataEditor extends JDialog {
dataField.setData(rowIndex * cols + (columnIndex - 1), ((MyLong) aValue).getValue());
}
public void fireEvent(TableModelEvent e) {
for (TableModelListener l : listener)
l.tableChanged(e);
}
@Override
public void addTableModelListener(TableModelListener l) {
listener.add(l);
@ -127,6 +166,11 @@ public class DataEditor extends JDialog {
public void removeTableModelListener(TableModelListener l) {
listener.remove(l);
}
@Override
public void valueChanged(int addr) {
fireEvent(new TableModelEvent(this, addr / cols));
}
}

View File

@ -10,7 +10,7 @@ import java.io.StringReader;
public class DataFieldTest extends TestCase {
public void testGetMinimized() throws Exception {
DataField data = new DataField(100);
DataField data = new DataField(100, 8);
data.setData(9, 1);
data = data.getMinimized();
assertEquals(1, data.getData(9));
@ -19,7 +19,7 @@ public class DataFieldTest extends TestCase {
}
public void testGrow() throws Exception {
DataField data = new DataField(100);
DataField data = new DataField(100, 8);
data.setData(9, 1);
data = data.getMinimized();
assertEquals(1, data.getData(9));

View File

@ -18,7 +18,7 @@ public class LUTTest extends TestCase {
ObservableValue c = new ObservableValue("c", 1);
Model model = new Model();
DataField data = new DataField(8);
DataField data = new DataField(8, 8);
data.setData(3, 1);
data.setData(7, 1);
LookUpTable out = model.add(new LookUpTable(

View File

@ -18,7 +18,7 @@ public class ROMTest extends TestCase {
ObservableValue sel = new ObservableValue("sel", 1);
Model model = new Model();
DataField data = new DataField(8);
DataField data = new DataField(8, 8);
data.setData(3, 17);
data.setData(7, 200);
ROM out = model.add(new ROM(