מונה קוד אפור פשוט

לעתים קרובות אנו רוצים רכיבים שאינם שילוב בלעדי באופיים - כלומר, אנו רוצים שהרכיב יעשה זאת יש קצת זיכרון. ישנה עדינות חשובה בהגדרת רכיבים כאלה: אתה לא יכול לקבל את הרכיב עצמו מאחסן את המצב, מכיוון שרכיב בודד יכול להופיע פעמים רבות באותו מעגל. זה לא יכול להופיע ישירות בתוך מעגל מספר פעמים, אך הוא יכול להופיע מספר פעמים אם הוא מופיע במעגל משנה בשימוש מספר פעמים.

הפתרון הוא ליצור מחלקה חדשה לייצוג המצב הנוכחי של האובייקט, ולשייך מופעים של זה עם הרכיב דרך מצב מעגל האב. בדוגמה זו, המיישמת קצה מופעל מונה קוד גריי 4 סיביות, אנו מגדירים מחלקה CounterData כדי לייצג את מצב המונה, ב תוספת למחלקת המשנה InstanceFactory כפי שהוצג קודם לכן. ה-CounterData האובייקט זוכר הן את הערך הנוכחי של המונה, כמו גם את קלט השעון האחרון שנראה (כדי לזהות עלייה קצוות).

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);
        }
    }
}

Next: מונה קוד אפור.