Contador simples em código de Gray

Muitas vezes queremos componentes que não são exclusivamente combinacionais em sua natureza - ou seja, queremos que o componente tenha alguma memória. Há uma sutileza importante na definição de tais componentes: você não poderá ter o componente armazenando por si mesmo o estado, porque um componente individual poderá aparecer várias vezes no mesmo circuito. Ele poderá até não aparecer diretamente dentro de um circuito, mas poderá ser contado mais de uma vez se existir em um subcircuito que seja usado várias vezes.

A solução será criar uma nova classe para representar o estado atual do objeto e, associar instâncias a esse componente através do estado do circuito do qual for herdado. Neste exemplo, que implementa um contador em código de Gray de 4 bits sensível à borda, definimos uma classe CounterData para representar o estado do contador, além da subclasse InstanceFactory como ilustrado anteriormente. Um objeto CounterData se lembrará tanto do valor atual do contador, bem como da entrada de clock visto pela última vez (para detectar a borda de subida).

CounterData

package com.cburch.gray;

import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.InstanceData;
import com.cburch.logisim.instance.InstanceState;

/** Represents the state of a counter. */
class CounterData implements InstanceData, Cloneable {
    /** Retrieves the state associated with this counter in the circuit state,
     * generating the state if necessary.
     */
    public static CounterData get(InstanceState state, BitWidth width) {
        CounterData ret = (CounterData) state.getData();
        if(ret == null) {
            // If it doesn't yet exist, then we'll set it up with our default
            // values and put it into the circuit state so it can be retrieved
            // in future propagations.
            ret = new CounterData(null, Value.createKnown(width, 0));
            state.setData(ret);
        } else if(!ret.value.getBitWidth().equals(width)) {
            ret.value = ret.value.extendWidth(width.getWidth(), Value.FALSE);
        }
        return ret;
    }

    /** The last clock input value observed. */
    private Value lastClock;

    /** The current value emitted by the counter. */
    private Value value;

    /** Constructs a state with the given values. */
    public CounterData(Value lastClock, Value value) {
        this.lastClock = lastClock;
        this.value = value;
    }

    /** Returns a copy of this object. */
    public Object clone() {
        // We can just use what super.clone() returns: The only instance variables are
        // Value objects, which are immutable, so we don't care that both the copy
        // and the copied refer to the same Value objects. If we had mutable instance
        // variables, then of course we would need to clone them.
        try { return super.clone(); }
        catch(CloneNotSupportedException e) { return null; }
    }

    /** Updates the last clock observed, returning true if triggered. */
    public boolean updateClock(Value value) {
        Value old = lastClock;
        lastClock = value;
        return old == Value.FALSE && value == Value.TRUE;
    }

    /** Returns the current value emitted by the counter. */
    public Value getValue() {
        return value;
    }

    /** Updates the current value emitted by the counter. */
    public void setValue(Value value) {
        this.value = value;
    }
}

SimpleCounter

package com.cburch.gray;

import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
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.util.GraphicsUtil;
import com.cburch.logisim.util.StringUtil;

/** Manufactures a simple counter that iterates over the 4-bit Gray Code. This
 * example illustrates how a component can maintain its own internal state. All
 * of the code relevant to state, though, appears in CounterData class. */
class SimpleGrayCounter extends InstanceFactory {
    private static final BitWidth BIT_WIDTH = BitWidth.create(4);

    // Again, notice how we don't have any instance variables related to an
    // individual instance's state. We can't put that here, because only one
    // SimpleGrayCounter object is ever created, and its job is to manage all
    // instances that appear in any circuits.

    public SimpleGrayCounter() {
        super("Gray Counter (Simple)");
        setOffsetBounds(Bounds.create(-30, -15, 30, 30));
        setPorts(new Port[] {
                new Port(-30, 0, Port.INPUT, 1),
                new Port(  0, 0, Port.OUTPUT, BIT_WIDTH.getWidth()),
        });
    }

    public void propagate(InstanceState state) {
        // Here I retrieve the state associated with this component via a helper
        // method. In this case, the state is in a CounterData object, which is
        // also where the helper method is defined. This helper method will end
        // up creating a CounterData object if one doesn't already exist.
        CounterData cur = CounterData.get(state, BIT_WIDTH);

        boolean trigger = cur.updateClock(state.getPort(0));
        if(trigger) cur.setValue(GrayIncrementer.nextGray(cur.getValue()));
        state.setPort(1, cur.getValue(), 9);

        // (You might be tempted to determine the counter's current value
        // via state.getPort(1). This is erroneous, though, because another
        // component may be pushing a value onto the same point, which would
        // "corrupt" the value found there. We really do need to store the
        // current value in the instance.)
    }

    public void paintInstance(InstancePainter painter) {
        painter.drawBounds();
        painter.drawClock(0, Direction.EAST); // draw a triangle on port 0
        painter.drawPort(1); // draw port 1 as just a dot

        // Display the current counter value centered within the rectangle.
        // However, if the context says not to show state (as when generating
        // printer output), then skip this.
        if(painter.getShowState()) {
            CounterData state = CounterData.get(painter, BIT_WIDTH);
            Bounds bds = painter.getBounds();
            GraphicsUtil.drawCenteredText(painter.getGraphics(),
                    StringUtil.toHexString(BIT_WIDTH.getWidth(), state.getValue().toIntValue()),
                    bds.getX() + bds.getWidth() / 2,
                    bds.getY() + bds.getHeight() / 2);
        }
    }
}

Próximo: Contador em código de Gray.