/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.logisim.std.memory;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.WeakHashMap;
import javax.swing.JLabel;
import com.bfh.logisim.designrulecheck.CorrectLabel;
import com.cburch.logisim.circuit.CircuitState;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.gui.hex.HexFile;
import com.cburch.logisim.gui.hex.HexFrame;
import com.cburch.logisim.gui.main.Frame;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.instance.InstanceState;
import com.cburch.logisim.instance.Port;
import com.cburch.logisim.instance.StdAttr;
import com.cburch.logisim.proj.Project;
import com.cburch.logisim.util.GraphicsUtil;
public class Rom extends Mem {
static class ContentsAttribute extends Attribute<MemContents> {
public ContentsAttribute() {
super("contents", Strings.getter("romContentsAttr"));
}
@Override
public java.awt.Component getCellEditor(Window source, MemContents value) {
if (source instanceof Frame) {
Project proj = ((Frame) source).getProject();
RomAttributes.register(value, proj);
}
ContentsCell ret = new ContentsCell(source, value);
ret.mouseClicked(null);
return ret;
}
@Override
public MemContents parse(String value) {
int lineBreak = value.indexOf('\n');
String first = lineBreak < 0 ? value : value
.substring(0, lineBreak);
String rest = lineBreak < 0 ? "" : value.substring(lineBreak + 1);
StringTokenizer toks = new StringTokenizer(first);
try {
String header = toks.nextToken();
if (!header.equals("addr/data:"))
return null;
int addr = Integer.parseInt(toks.nextToken());
int data = Integer.parseInt(toks.nextToken());
MemContents ret = MemContents.create(addr, data);
HexFile.open(ret, new StringReader(rest));
return ret;
} catch (IOException e) {
return null;
} catch (NumberFormatException e) {
return null;
} catch (NoSuchElementException e) {
return null;
}
}
@Override
public String toDisplayString(MemContents value) {
return Strings.get("romContentsValue");
}
@Override
public String toStandardString(MemContents state) {
int addr = state.getLogLength();
int data = state.getWidth();
StringWriter ret = new StringWriter();
ret.write("addr/data: " + addr + " " + data + "\n");
try {
HexFile.save(ret, state);
} catch (IOException e) {
}
return ret.toString();
}
}
@SuppressWarnings("serial")
private static class ContentsCell extends JLabel implements MouseListener {
Window source;
MemContents contents;
ContentsCell(Window source, MemContents contents) {
super(Strings.get("romContentsValue"));
this.source = source;
this.contents = contents;
addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
if (contents == null)
return;
Project proj = source instanceof Frame ? ((Frame) source)
.getProject() : null;
HexFrame frame = RomAttributes.getHexFrame(contents, proj);
frame.setVisible(true);
frame.toFront();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
}
public static Attribute<MemContents> CONTENTS_ATTR = new ContentsAttribute();
// The following is so that instance's MemListeners aren't freed by the
// garbage collector until the instance itself is ready to be freed.
private WeakHashMap<Instance, MemListener> memListeners;
public Rom() {
super("ROM", Strings.getter("romComponent"), 0);
setIconName("rom.gif");
memListeners = new WeakHashMap<Instance, MemListener>();
}
@Override
protected void configureNewInstance(Instance instance) {
super.configureNewInstance(instance);
MemContents contents = getMemContents(instance);
MemListener listener = new MemListener(instance);
memListeners.put(instance, listener);
contents.addHexModelListener(listener);
instance.addAttributeListener();
}
@Override
void configurePorts(Instance instance) {
Port[] ps = new Port[MEM_INPUTS];
ps[ADDR] = new Port(0, 10, Port.INPUT, ADDR_ATTR);
ps[ADDR].setToolTip(Strings.getter("memAddrTip"));
int ypos = (instance.getAttributeValue(Mem.DATA_ATTR).getWidth() == 1) ? getControlHeight(instance
.getAttributeSet()) + 10 : getControlHeight(instance
.getAttributeSet());
ps[DATA] = new Port(SymbolWidth + 40, ypos, Port.OUTPUT, DATA_ATTR);
ps[DATA].setToolTip(Strings.getter("memDataTip"));
instance.setPorts(ps);
}
@Override
public AttributeSet createAttributeSet() {
return new RomAttributes();
}
private void DrawControlBlock(InstancePainter painter, int xpos, int ypos) {
Graphics g = painter.getGraphics();
GraphicsUtil.switchToWidth(g, 2);
AttributeSet attrs = painter.getAttributeSet();
g.drawLine(xpos + 20, ypos, xpos + 20 + SymbolWidth, ypos);
g.drawLine(xpos + 20, ypos, xpos + 20, ypos + getControlHeight(attrs)
- 10);
g.drawLine(xpos + 20 + SymbolWidth, ypos, xpos + 20 + SymbolWidth, ypos
+ getControlHeight(attrs) - 10);
g.drawLine(xpos + 20, ypos + getControlHeight(attrs) - 10, xpos + 30,
ypos + getControlHeight(attrs) - 10);
g.drawLine(xpos + 20 + SymbolWidth - 10, ypos + getControlHeight(attrs)
- 10, xpos + 20 + SymbolWidth, ypos + getControlHeight(attrs)
- 10);
g.drawLine(xpos + 30, ypos + getControlHeight(attrs) - 10, xpos + 30,
ypos + getControlHeight(attrs));
g.drawLine(xpos + 20 + SymbolWidth - 10, ypos + getControlHeight(attrs)
- 10, xpos + 20 + SymbolWidth - 10, ypos
+ getControlHeight(attrs));
GraphicsUtil.drawCenteredText(
g,
"ROM "
+ GetSizeLabel(painter.getAttributeValue(Mem.ADDR_ATTR)
.getWidth())
+ " x "
+ Integer.toString(painter.getAttributeValue(
Mem.DATA_ATTR).getWidth()), xpos
+ (SymbolWidth / 2) + 20, ypos + 5);
GraphicsUtil.switchToWidth(g, 1);
DrawAddress(painter, xpos, ypos + 10,
painter.getAttributeValue(Mem.ADDR_ATTR).getWidth());
}
private void DrawDataBlock(InstancePainter painter, int xpos, int ypos,
int bit, int NrOfBits) {
int realypos = ypos + getControlHeight(painter.getAttributeSet()) + bit
* 20;
int realxpos = xpos + 20;
boolean FirstBlock = bit == 0;
boolean LastBlock = bit == (NrOfBits - 1);
Graphics g = painter.getGraphics();
Font font = g.getFont();
GraphicsUtil.switchToWidth(g, 2);
g.drawRect(realxpos, realypos, SymbolWidth, 20);
GraphicsUtil.drawText(g, "A", realxpos + SymbolWidth - 3,
realypos + 10, GraphicsUtil.H_RIGHT, GraphicsUtil.V_CENTER);
if (FirstBlock && LastBlock) {
GraphicsUtil.switchToWidth(g, 3);
g.drawLine(realxpos + SymbolWidth + 1, realypos + 10, realxpos
+ SymbolWidth + 20, realypos + 10);
painter.drawPort(DATA);
return;
}
g.drawLine(realxpos + SymbolWidth, realypos + 10, realxpos
+ SymbolWidth + 10, realypos + 10);
g.drawLine(realxpos + SymbolWidth + 10, realypos + 10, realxpos
+ SymbolWidth + 15, realypos + 5);
g.setFont(font.deriveFont(7.0f));
GraphicsUtil
.drawText(g, Integer.toString(bit), realxpos + SymbolWidth + 3,
realypos + 7, GraphicsUtil.H_LEFT,
GraphicsUtil.V_BASELINE);
g.setFont(font);
GraphicsUtil.switchToWidth(g, 5);
if (FirstBlock) {
g.drawLine(realxpos + SymbolWidth + 15, realypos + 5, realxpos
+ SymbolWidth + 15, realypos + 20);
g.drawLine(realxpos + SymbolWidth + 15, realypos + 5, realxpos
+ SymbolWidth + 20, realypos);
painter.drawPort(DATA);
} else if (LastBlock) {
g.drawLine(realxpos + SymbolWidth + 15, realypos, realxpos
+ SymbolWidth + 15, realypos + 10);
} else
g.drawLine(realxpos + SymbolWidth + 15, realypos, realxpos
+ SymbolWidth + 15, realypos + 20);
GraphicsUtil.switchToWidth(g, 1);
}
public int getControlHeight(AttributeSet attrs) {
return 60;
}
@Override
public String getHDLName(AttributeSet attrs) {
StringBuffer CompleteName = new StringBuffer();
CompleteName.append(CorrectLabel.getCorrectLabel(attrs
.getValue(StdAttr.LABEL)));
if (CompleteName.length() == 0)
CompleteName.append("ROM");
return CompleteName.toString();
}
@Override
HexFrame getHexFrame(Project proj, Instance instance, CircuitState state) {
return RomAttributes.getHexFrame(getMemContents(instance), proj);
}
// TODO - maybe delete this method?
MemContents getMemContents(Instance instance) {
return instance.getAttributeValue(CONTENTS_ATTR);
}
@Override
public Bounds getOffsetBounds(AttributeSet attrs) {
int len = attrs.getValue(Mem.DATA_ATTR).getWidth();
return Bounds.create(0, 0, SymbolWidth + 40, getControlHeight(attrs)
+ 20 * len);
}
@Override
MemState getState(Instance instance, CircuitState state) {
MemState ret = (MemState) instance.getData(state);
if (ret == null) {
MemContents contents = getMemContents(instance);
ret = new MemState(contents);
instance.setData(state, ret);
}
return ret;
}
@Override
MemState getState(InstanceState state) {
MemState ret = (MemState) state.getData();
if (ret == null) {
MemContents contents = getMemContents(state.getInstance());
ret = new MemState(contents);
state.setData(ret);
}
return ret;
}
@Override
public boolean HDLSupportedComponent(String HDLIdentifier,
AttributeSet attrs) {
if (MyHDLGenerator == null)
MyHDLGenerator = new RomHDLGeneratorFactory();
return MyHDLGenerator.HDLTargetSupported(HDLIdentifier, attrs);
}
@Override
protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) {
if (attr == Mem.DATA_ATTR) {
instance.recomputeBounds();
configurePorts(instance);
}
}
@Override
public void paintInstance(InstancePainter painter) {
Graphics g = painter.getGraphics();
Bounds bds = painter.getBounds();
String Label = painter.getAttributeValue(StdAttr.LABEL);
if (Label != null) {
Font font = g.getFont();
g.setFont(painter.getAttributeValue(StdAttr.LABEL_FONT));
GraphicsUtil.drawCenteredText(g, Label, bds.getX() + bds.getWidth()
/ 2, bds.getY() - g.getFont().getSize());
g.setFont(font);
}
int xpos = bds.getX();
int ypos = bds.getY();
int NrOfBits = painter.getAttributeValue(Mem.DATA_ATTR).getWidth();
/* draw control */
DrawControlBlock(painter, xpos, ypos);
/* draw body */
for (int i = 0; i < NrOfBits; i++) {
DrawDataBlock(painter, xpos, ypos, i, NrOfBits);
}
/* Draw contents */
if (painter.getShowState()) {
MemState state = getState(painter);
state.paint(painter.getGraphics(), bds.getX() + 20, bds.getY(),
false, getControlHeight(painter.getAttributeSet()));
}
}
@Override
public void propagate(InstanceState state) {
MemState myState = getState(state);
BitWidth dataBits = state.getAttributeValue(DATA_ATTR);
Value addrValue = state.getPortValue(ADDR);
int addr = addrValue.toIntValue();
if (!addrValue.isFullyDefined() || addr < 0)
return;
if (addr != myState.getCurrent()) {
myState.setCurrent(addr);
myState.scrollToShow(addr);
}
int val = myState.getContents().get(addr);
state.setPort(DATA, Value.createKnown(dataBits, val), DELAY);
}
@Override
public boolean RequiresNonZeroLabel() {
return true;
}
}