/******************************************************************************* * 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.gray; import com.cburch.logisim.data.Attribute; import com.cburch.logisim.data.BitWidth; import com.cburch.logisim.data.Bounds; import com.cburch.logisim.data.Value; 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; /** * This component takes a multibit input and outputs the value that follows it * in Gray Code. For instance, given input 0100 the output is 1100. */ class GrayIncrementer extends InstanceFactory { /* * Note that there are no instance variables. There is only one instance of * this class created, which manages all instances of the component. Any * information associated with individual instances should be handled * through attributes. For GrayIncrementer, each instance has a "bit width" * that it works with, and so we'll have an attribute. */ /** * Computes the next gray value in the sequence after prev. This static * method just does some bit twiddling; it doesn't have much to do with * Logisim except that it manipulates Value and BitWidth objects. */ static Value nextGray(Value prev) { BitWidth bits = prev.getBitWidth(); if (!prev.isFullyDefined()) return Value.createError(bits); int x = prev.toIntValue(); int ct = (x >> 16) ^ x; // compute parity of x ct = (ct >> 8) ^ ct; ct = (ct >> 4) ^ ct; ct = (ct >> 2) ^ ct; ct = (ct >> 1) ^ ct; if ((ct & 1) == 0) { // if parity is even, flip 1's bit x = x ^ 1; } else { // else flip bit just above last 1 int y = x ^ (x & (x - 1)); // first compute the last 1 y = (y << 1) & bits.getMask(); x = (y == 0 ? 0 : x ^ y); } return Value.createKnown(bits, x); } /** * The constructor configures the factory. */ GrayIncrementer() { super("Gray Code Incrementer"); /* * This is how we can set up the attributes for GrayIncrementers. In * this case, there is just one attribute - the width - whose default is * 4. The StdAttr class defines several commonly occurring attributes, * including one for "bit width." It's best to use those StdAttr * attributes when appropriate: A user can then select several * components (even from differing factories) with the same attribute * and modify them all at once. */ setAttributes(new Attribute[] { StdAttr.WIDTH }, new Object[] { BitWidth.create(4) }); /* * The "offset bounds" is the location of the bounding rectangle * relative to the mouse location. Here, we're choosing the component to * be 30x30, and we're anchoring it relative to its primary output (as * is typical for Logisim), which happens to be in the center of the * east edge. Thus, the top left corner of the bounding box is 30 pixels * west and 15 pixels north of the mouse location. */ setOffsetBounds(Bounds.create(-30, -15, 30, 30)); /* * The ports are locations where wires can be connected to this * component. Each port object says where to find the port relative to * the component's anchor location, then whether the port is an * input/output/both, and finally the expected bit width for the port. * The bit width can be a constant (like 1) or an attribute (as here). */ setPorts(new Port[] { new Port(-30, 0, Port.INPUT, StdAttr.WIDTH), new Port(0, 0, Port.OUTPUT, StdAttr.WIDTH), }); } /** * Says how an individual instance should appear on the canvas. */ @Override public void paintInstance(InstancePainter painter) { // As it happens, InstancePainter contains several convenience methods // for drawing, and we'll use those here. Frequently, you'd want to // retrieve its Graphics object (painter.getGraphics) so you can draw // directly onto the canvas. painter.drawRectangle(painter.getBounds(), "G+1"); painter.drawPorts(); } /** * Computes the current output for this component. This method is invoked * any time any of the inputs change their values; it may also be invoked in * other circumstances, even if there is no reason to expect it to change * anything. */ @Override public void propagate(InstanceState state) { // First we retrieve the value being fed into the input. Note that in // the setPorts invocation above, the component's input was included at // index 0 in the parameter array, so we use 0 as the parameter below. Value in = state.getPortValue(0); // Now compute the output. We've farmed this out to a helper method, // since the same logic is needed for the library's other components. Value out = nextGray(in); // Finally we propagate the output into the circuit. The first parameter // is 1 because in our list of ports (configured by invocation of // setPorts above) the output is at index 1. The second parameter is the // value we want to send on that port. And the last parameter is its // "delay" - the number of steps it will take for the output to update // after its input. state.setPort(1, out, out.getWidth() + 1); } }