/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.std.memory;
import java.awt.Graphics;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.Attributes;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.InstanceFactory;
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.tools.key.BitWidthConfigurator;
import com.cburch.logisim.tools.key.IntegerConfigurator;
import com.cburch.logisim.tools.key.JoinedConfigurator;
import com.cburch.logisim.util.GraphicsUtil;
public class ShiftRegister extends InstanceFactory {
static final Attribute<Integer> ATTR_LENGTH = Attributes.forIntegerRange("length",
Strings.getter("shiftRegLengthAttr"), 1, 32);
static final Attribute<Boolean> ATTR_LOAD = Attributes.forBoolean("parallel",
Strings.getter("shiftRegParallelAttr"));
private static final int IN = 0;
private static final int SH = 1;
private static final int CK = 2;
private static final int CLR = 3;
private static final int OUT = 4;
private static final int LD = 5;
public ShiftRegister() {
super("Shift Register", Strings.getter("shiftRegisterComponent"));
setAttributes(new Attribute[] {
StdAttr.WIDTH, ATTR_LENGTH, ATTR_LOAD, StdAttr.EDGE_TRIGGER,
StdAttr.LABEL, StdAttr.LABEL_FONT
}, new Object[] {
BitWidth.ONE, Integer.valueOf(8), Boolean.TRUE,
StdAttr.TRIG_RISING, "", StdAttr.DEFAULT_LABEL_FONT
});
setKeyConfigurator(JoinedConfigurator.create(
new IntegerConfigurator(ATTR_LENGTH, 1, 32, 0),
new BitWidthConfigurator(StdAttr.WIDTH)));
setIconName("shiftreg.gif");
setInstanceLogger(ShiftRegisterLogger.class);
setInstancePoker(ShiftRegisterPoker.class);
}
@Override
public Bounds getOffsetBounds(AttributeSet attrs) {
Object parallel = attrs.getValue(ATTR_LOAD);
if (parallel == null || ((Boolean) parallel).booleanValue()) {
int len = attrs.getValue(ATTR_LENGTH).intValue();
return Bounds.create(0, -20, 20 + 10 * len, 40);
} else {
return Bounds.create(0, -20, 30, 40);
}
}
@Override
protected void configureNewInstance(Instance instance) {
configurePorts(instance);
instance.addAttributeListener();
}
@Override
protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) {
if (attr == ATTR_LOAD || attr == ATTR_LENGTH || attr == StdAttr.WIDTH) {
instance.recomputeBounds();
configurePorts(instance);
}
}
private void configurePorts(Instance instance) {
BitWidth widthObj = instance.getAttributeValue(StdAttr.WIDTH);
int width = widthObj.getWidth();
Boolean parallelObj = instance.getAttributeValue(ATTR_LOAD);
Bounds bds = instance.getBounds();
Port[] ps;
if (parallelObj == null || parallelObj.booleanValue()) {
Integer lenObj = instance.getAttributeValue(ATTR_LENGTH);
int len = lenObj == null ? 8 : lenObj.intValue();
ps = new Port[6 + 2 * len];
ps[LD] = new Port(10, -20, Port.INPUT, 1);
ps[LD].setToolTip(Strings.getter("shiftRegLoadTip"));
for (int i = 0; i < len; i++) {
ps[6 + 2 * i] = new Port(20 + 10 * i, -20, Port.INPUT, width);
ps[6 + 2 * i + 1] = new Port(20 + 10 * i, 20, Port.OUTPUT, width);
}
} else {
ps = new Port[5];
}
ps[OUT] = new Port(bds.getWidth(), 0, Port.OUTPUT, width);
ps[SH] = new Port( 0, -10, Port.INPUT, 1);
ps[IN] = new Port( 0, 0, Port.INPUT, width);
ps[CK] = new Port( 0, 10, Port.INPUT, 1);
ps[CLR] = new Port(10, 20, Port.INPUT, 1);
ps[OUT].setToolTip(Strings.getter("shiftRegOutTip"));
ps[SH].setToolTip(Strings.getter("shiftRegShiftTip"));
ps[IN].setToolTip(Strings.getter("shiftRegInTip"));
ps[CK].setToolTip(Strings.getter("shiftRegClockTip"));
ps[CLR].setToolTip(Strings.getter("shiftRegClearTip"));
instance.setPorts(ps);
instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT,
bds.getX() + bds.getWidth() / 2,
bds.getY() + bds.getHeight() / 4,
GraphicsUtil.H_CENTER, GraphicsUtil.V_CENTER);
}
private ShiftRegisterData getData(InstanceState state) {
BitWidth width = state.getAttributeValue(StdAttr.WIDTH);
Integer lenObj = state.getAttributeValue(ATTR_LENGTH);
int length = lenObj == null ? 8 : lenObj.intValue();
ShiftRegisterData data = (ShiftRegisterData) state.getData();
if (data == null) {
data = new ShiftRegisterData(width, length);
state.setData(data);
} else {
data.setDimensions(width, length);
}
return data;
}
@Override
public void propagate(InstanceState state) {
Object triggerType = state.getAttributeValue(StdAttr.EDGE_TRIGGER);
boolean parallel = state.getAttributeValue(ATTR_LOAD).booleanValue();
ShiftRegisterData data = getData(state);
int len = data.getLength();
boolean triggered = data.updateClock(state.getPort(CK), triggerType);
if (state.getPort(CLR) == Value.TRUE) {
data.clear();
} else if (triggered) {
if (parallel && state.getPort(LD) == Value.TRUE) {
data.clear();
for (int i = len - 1; i >= 0; i--) {
data.push(state.getPort(6 + 2 * i));
}
} else if (state.getPort(SH) != Value.FALSE) {
data.push(state.getPort(IN));
}
}
state.setPort(OUT, data.get(0), 4);
if (parallel) {
for (int i = 0; i < len; i++) {
state.setPort(6 + 2 * i + 1, data.get(len - 1 - i), 4);
}
}
}
@Override
public void paintInstance(InstancePainter painter) {
// draw boundary, label
painter.drawBounds();
painter.drawLabel();
// draw state
boolean parallel = painter.getAttributeValue(ATTR_LOAD).booleanValue();
if (parallel) {
BitWidth widObj = painter.getAttributeValue(StdAttr.WIDTH);
int wid = widObj.getWidth();
Integer lenObj = painter.getAttributeValue(ATTR_LENGTH);
int len = lenObj == null ? 8 : lenObj.intValue();
if (painter.getShowState()) {
if (wid <= 4) {
ShiftRegisterData data = getData(painter);
Bounds bds = painter.getBounds();
int x = bds.getX() + 20;
int y = bds.getY();
Object label = painter.getAttributeValue(StdAttr.LABEL);
if (label == null || label.equals("")) {
y += bds.getHeight() / 2;
} else {
y += 3 * bds.getHeight() / 4;
}
Graphics g = painter.getGraphics();
for (int i = 0; i < len; i++) {
String s = data.get(len - 1 - i).toHexString();
GraphicsUtil.drawCenteredText(g, s, x, y);
x += 10;
}
}
} else {
Bounds bds = painter.getBounds();
int x = bds.getX() + bds.getWidth() / 2;
int y = bds.getY();
int h = bds.getHeight();
Graphics g = painter.getGraphics();
Object label = painter.getAttributeValue(StdAttr.LABEL);
if (label == null || label.equals("")) {
String a = Strings.get("shiftRegisterLabel1");
GraphicsUtil.drawCenteredText(g, a, x, y + h / 4);
}
String b = Strings.get("shiftRegisterLabel2", "" + len,
"" + wid);
GraphicsUtil.drawCenteredText(g, b, x, y + 3 * h / 4);
}
}
// draw input and output ports
int ports = painter.getInstance().getPorts().size();
for (int i = 0; i < ports; i++) {
if (i != CK) painter.drawPort(i);
}
painter.drawClock(CK, Direction.EAST);
}
}