Skip to content

Commit 204a1d5

Browse files
committed
Allow control bits at the bottom of components
1 parent ef808c1 commit 204a1d5

File tree

11 files changed

+183
-60
lines changed

11 files changed

+183
-60
lines changed

TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Dans les petits plus, mettre en couleur les composants pourrait être intéressa
4343

4444
### DONE
4545

46+
* Control bits for most "controlled" components can be put at the bottom
4647
* Proper mouseover for bezier curves
4748
* Fix drawing of gates with many inputs
4849
* Waypoint positioning when embedded right-click
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{ // JSON5
2+
v: 6,
3+
opts: {name: 'test-draw-controlled-comps', wireStyle: 'hv'},
4+
components: {
5+
tristate0: {type: 'tristate', pos: [230, 85], in: [0, 1], out: 2},
6+
cnot0: {type: 'cnot-array', pos: [135, 390], in: '9-13', out: '14-17'},
7+
tristate5: {type: 'tristate', pos: [365, 105], orient: 's', in: [33, 34], out: 35},
8+
tristate6: {type: 'tristate', pos: [445, 75], orient: 'w', in: [36, 37], out: 38},
9+
tristate7: {type: 'tristate', pos: [580, 85], orient: 'n', in: [39, 40], out: 41},
10+
cnot1: {type: 'cnot-array', pos: [260, 395], orient: 's', in: '42-46', out: '47-50'},
11+
cnot2: {type: 'cnot-array', pos: [395, 390], orient: 'w', in: '51-55', out: '56-59'},
12+
cnot3: {type: 'cnot-array', pos: [520, 395], orient: 'n', in: '60-64', out: '65-68'},
13+
tristate8: {type: 'tristate-array', pos: [145, 680], in: '117-121', out: '122-125'},
14+
tristate9: {type: 'tristate-array', pos: [275, 680], orient: 's', in: '126-130', out: '131-134'},
15+
tristate10: {type: 'tristate-array', pos: [390, 680], orient: 'w', in: '135-139', out: '140-143'},
16+
tristate11: {type: 'tristate-array', pos: [540, 685], orient: 'n', in: '144-148', out: '149-152'},
17+
tristate16: {type: 'tristate', pos: [230, 185], in: [3, 4], out: 5, bottom: true},
18+
tristate1: {type: 'tristate', pos: [340, 205], orient: 's', in: [6, 7], out: 8, bottom: true},
19+
tristate2: {type: 'tristate', pos: [465, 205], orient: 'w', in: [18, 19], out: 20, bottom: true},
20+
tristate3: {type: 'tristate', pos: [575, 205], orient: 'n', in: [21, 22], out: 23, bottom: true},
21+
tristate4: {type: 'tristate-array', pos: [145, 800], in: '24-28', out: '29-32', bottom: true},
22+
tristate12: {type: 'tristate-array', pos: [275, 785], orient: 's', in: '105-109', out: '110-113', bottom: true},
23+
tristate13: {type: 'tristate-array', pos: [450, 805], orient: 'w', in: ['114-116', 153, 154], out: '155-158', bottom: true},
24+
tristate14: {type: 'tristate-array', pos: [555, 785], orient: 'n', in: '159-163', out: '164-167', bottom: true},
25+
in0: {type: 'in', pos: [100, 30], id: 168, name: 'E'},
26+
in1: {type: 'in', pos: [90, 90], id: 171, name: 'I'},
27+
in2: {type: 'in', pos: [55, 320], id: 172, name: 'E'},
28+
in3: {type: 'in', pos: [60, 610], id: 173, name: 'E'},
29+
cnot8: {type: 'cnot-array', pos: [135, 515], in: [169, 170, '174-176'], out: '177-180', bottom: true},
30+
cnot4: {type: 'cnot-array', pos: [260, 505], orient: 's', in: '181-185', out: '186-189', bottom: true},
31+
cnot5: {type: 'cnot-array', pos: [440, 520], orient: 'w', in: '190-194', out: '195-198', bottom: true},
32+
cnot6: {type: 'cnot-array', pos: [535, 505], orient: 'n', in: '199-203', out: '204-207', bottom: true},
33+
mux0: {type: 'mux', pos: [710, 245], in: '437-446', out: [447, 448], from: 8, to: 2},
34+
demux1: {type: 'demux', pos: [715, 660], in: '449-452', out: '453-460', from: 2, to: 8},
35+
mux1: {type: 'mux', pos: [985, 200], orient: 's', in: '461-470', out: [471, 472], from: 8, to: 2},
36+
mux2: {type: 'mux', pos: [1135, 240], orient: 'w', in: '473-482', out: [483, 484], from: 8, to: 2},
37+
mux3: {type: 'mux', pos: [1425, 200], orient: 'n', in: '485-494', out: [495, 496], from: 8, to: 2},
38+
mux8: {type: 'mux', pos: [800, 240], in: '545-554', out: [555, 556], from: 8, to: 2, bottom: true},
39+
mux4: {type: 'mux', pos: [980, 300], orient: 's', in: '557-566', out: [567, 568], from: 8, to: 2, bottom: true},
40+
mux5: {type: 'mux', pos: [1225, 245], orient: 'w', in: '569-578', out: [579, 580], from: 8, to: 2, bottom: true},
41+
mux6: {type: 'mux', pos: [1415, 310], orient: 'n', in: '581-590', out: [591, 592], from: 8, to: 2, bottom: true},
42+
demux2: {type: 'demux', pos: [1035, 610], orient: 's', in: '605-608', out: '609-616', from: 2, to: 8},
43+
demux4: {type: 'demux', pos: [1220, 660], orient: 'w', in: '629-632', out: '633-640', from: 2, to: 8},
44+
demux8: {type: 'demux', pos: [825, 675], in: '677-680', out: '681-688', from: 2, to: 8, bottom: true},
45+
demux0: {type: 'demux', pos: [1030, 710], orient: 's', in: '689-692', out: '693-700', from: 2, to: 8, bottom: true},
46+
demux3: {type: 'demux', pos: [1300, 655], orient: 'w', in: '701-704', out: '705-712', from: 2, to: 8, bottom: true},
47+
demux5: {type: 'demux', pos: [1470, 595], orient: 'n', in: '713-716', out: '717-724', from: 2, to: 8, bottom: true},
48+
demux6: {type: 'demux', pos: [1475, 705], orient: 'n', in: '725-728', out: '729-736', from: 2, to: 8, bottom: true},
49+
label0: {type: 'label', pos: [1070, 495], text: 'Demuxes'},
50+
label1: {type: 'label', pos: [1055, 70], text: 'Muxes'},
51+
label2: {type: 'label', pos: [65, 155], text: 'TristateBuf'},
52+
label3: {type: 'label', pos: [80, 740], text: 'TristateBufArray'},
53+
label4: {type: 'label', pos: [85, 450], text: 'ControlledInvs'},
54+
},
55+
wires: [[168, 1], [168, 34], [168, 37, {via: [[495, 115, 's', 'in0']]}], [168, 40], [168, 4, {via: [[130, 215, 's', 'in0']]}], [168, 7, {via: [[130, 290, 's', 'in0']]}], [168, 19, {via: [[130, 290, 's', 'in0'], [400, 155, 'n', 'in0']]}], [168, 22, {via: [[130, 290, 's', 'in0']]}], [171, 0], [171, 3], [171, 33, {via: [[190, 140, 'e', 'in1'], [305, 65, 'e', 'in1']]}], [171, 6, {via: [[190, 140, 'e', 'in1']]}], [172, 13], [172, 46], [172, 55, {via: [[440, 445, 's', 'in2']]}], [172, 64], [172, 176, {via: [[185, 575, 's', 'in2']]}], [172, 185], [172, 194], [172, 203], [173, 121], [173, 130], [173, 139, {via: [[450, 730, 's', 'in3']]}], [173, 148], [173, 28, {via: [[195, 855, 's', 'in3']]}], [173, 109], [173, 154], [173, 163]]
56+
}

simulator/src/ComponentMenu.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ const componentsMenu: Array<Section> = [{
107107
items: [
108108
Gate1Def.button({ type: "not" }, "not"),
109109
Gate1Def.button({ type: "buf" }, "buf", { visible: withButton }),
110-
TristateBufferDef.button("tri", { compat: "tri", visible: withButton }),
110+
TristateBufferDef.button({ bottom: false }, "tri", { compat: "tri", visible: withButton }),
111111

112112
GateNDef.button({ type: "and", bits: 2 }, "and"),
113113
GateNDef.button({ type: "or", bits: 2 }, "or"),
@@ -134,9 +134,9 @@ const componentsMenu: Array<Section> = [{
134134
GateNDef.button({ type: "nor", bits: 4 }, "nor4", { compat: "nor4", visible: ifShowOnly }),
135135
GateNDef.button({ type: "xnor", bits: 4 }, "xnor4", { compat: "xnor4", visible: ifShowOnly }),
136136

137-
ControlledInverterDef.button({ bits: 4 }, "ControlledInverter", { compat: "switched-inverter", visible: withButton }),
137+
ControlledInverterDef.button({ bits: 4, bottom: false }, "ControlledInverter", { compat: "switched-inverter", visible: withButton }),
138138
GateArrayDef.button({ type: "and", bits: 4 }, "GateArray", { compat: "quad-gate", visible: withButton }),
139-
TristateBufferArrayDef.button({ bits: 4 }, "TristateBufferArray", { visible: withButton }),
139+
TristateBufferArrayDef.button({ bits: 4, bottom: false }, "TristateBufferArray", { visible: withButton }),
140140

141141
],
142142
}, {

simulator/src/LogicEditor.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,6 +1952,20 @@ export class LogicEditor extends HTMLElement implements DrawableParent {
19521952

19531953
private doDrawWithContext(g: GraphicsRendering, width: number, height: number, baseTransform: DOMMatrixReadOnly, contentTransform: DOMMatrixReadOnly, skipBorder: boolean, transparentBackground: boolean, __redrawMask: boolean) {
19541954

1955+
// Draw order:
1956+
// * Clear
1957+
// * Highlight rectangles
1958+
// * Grid
1959+
// * Guidelines
1960+
// * Border
1961+
// * Components - background
1962+
// * Wires
1963+
// * Components - normal
1964+
// * Anchors
1965+
// * Components - overlays
1966+
// * Refs
1967+
// * Selection rect
1968+
19551969
// TODO handle redrawMask
19561970
// if (redrawMask) {
19571971
// console.log("would redraw mask")

simulator/src/UIEventManager.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,13 @@ export class UIEventManager {
203203
}
204204

205205
public updateMouseOver([x, y]: [number, number], pullingWire: boolean, settingAnchor: boolean) {
206+
207+
// Mouseover search order:
208+
// * Components - overlays
209+
// * Components - normal, and nodes, sometimes
210+
// * Wires, sometimes
211+
// * Components - background
212+
206213
const findMouseOver: () => Drawable | null = () => {
207214
// easy optimization: maybe we're still over the
208215
// same component as before, so quickly check this

simulator/src/components/ControlledInverter.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,40 @@ import { COLOR_COMPONENT_BORDER, COLOR_UNKNOWN, circle, colorForLogicValue } fro
33
import { div, mods, tooltipContent } from "../htmlgen"
44
import { S } from "../strings"
55
import { ArrayFillWith, LogicValue, Unknown, isHighImpedance, isUnknown, typeOrUndefined } from "../utils"
6-
import { ParametrizedComponentBase, Repr, ResolvedParams, defineParametrizedComponent, groupVertical, param } from "./Component"
6+
import { ParametrizedComponentBase, Repr, ResolvedParams, defineParametrizedComponent, groupVertical, param, paramBool } from "./Component"
77
import { DrawContext, DrawableParent, GraphicsRendering, MenuItems } from "./Drawable"
88

99

1010
export const ControlledInverterDef =
1111
defineParametrizedComponent("cnot-array", true, true, {
12-
variantName: ({ bits }) => `cnot-array-${bits}`,
12+
variantName: ({ bits, bottom }) => `cnot-array-${bits}${bottom ? "b" : ""}`,
1313
idPrefix: "cnot",
1414
button: { imgWidth: 50 },
1515
repr: {
1616
bits: typeOrUndefined(t.number),
17+
bottom: typeOrUndefined(t.boolean),
1718
},
1819
valueDefaults: {},
1920
params: {
2021
bits: param(4, [2, 4, 8, 16]),
22+
bottom: paramBool(),
2123
},
22-
validateParams: ({ bits }) => ({
24+
validateParams: ({ bits, bottom }) => ({
2325
numBits: bits,
26+
controlPinsAtBottom: bottom,
2427
}),
2528

2629
size: ({ numBits }) => ({
2730
gridWidth: 4,
2831
gridHeight: 8 + Math.max(0, numBits - 8),
2932
}),
30-
makeNodes: ({ numBits, gridHeight }) => ({
33+
makeNodes: ({ numBits, controlPinsAtBottom, gridHeight }) => ({
3134
ins: {
3235
In: groupVertical("w", -3, 0, numBits),
33-
S: [0, -(gridHeight / 2 + 1), "n"],
36+
S: [0,
37+
-(gridHeight / 2 + 1) * (controlPinsAtBottom ? -1 : 1),
38+
controlPinsAtBottom ? "s" : "n",
39+
],
3440
},
3541
outs: {
3642
Out: groupVertical("e", +3, 0, numBits),
@@ -47,16 +53,19 @@ export type ControlledInverterParams = ResolvedParams<typeof ControlledInverterD
4753
export class ControlledInverter extends ParametrizedComponentBase<ControlledInverterRepr> {
4854

4955
public readonly numBits: number
56+
public readonly controlPinsAtBottom: boolean
5057

5158
public constructor(parent: DrawableParent, params: ControlledInverterParams, saved?: ControlledInverterRepr) {
5259
super(parent, ControlledInverterDef.with(params), saved)
5360
this.numBits = params.numBits
61+
this.controlPinsAtBottom = params.controlPinsAtBottom
5462
}
5563

5664
public toJSON() {
5765
return {
5866
...this.toJSONBase(),
5967
bits: this.numBits === ControlledInverterDef.aults.bits ? undefined : this.numBits,
68+
bottom: this.controlPinsAtBottom === ControlledInverterDef.aults.bottom ? undefined : this.controlPinsAtBottom,
6069
}
6170
}
6271

@@ -89,14 +98,19 @@ export class ControlledInverter extends ParametrizedComponentBase<ControlledInve
8998
protected override doDraw(g: GraphicsRendering, ctx: DrawContext) {
9099
this.doDrawDefault(g, ctx, {
91100
skipLabels: true,
92-
drawInside: ({ top, left, right }) => {
101+
drawInside: ({ top, bottom, left, right }) => {
93102
const invert = this.inputs.S.value
94103

95104
g.lineWidth = 2
96105
g.strokeStyle = colorForLogicValue(invert)
97106
g.beginPath()
98-
g.moveTo(this.posX, top)
99-
g.lineTo(this.posX, this.posY - 4)
107+
if (this.controlPinsAtBottom) {
108+
g.moveTo(this.posX, bottom)
109+
g.lineTo(this.posX, this.posY + 4)
110+
} else {
111+
g.moveTo(this.posX, top)
112+
g.lineTo(this.posX, this.posY - 4)
113+
}
100114
g.stroke()
101115

102116
g.strokeStyle = invert === true ? COLOR_COMPONENT_BORDER : COLOR_UNKNOWN
@@ -114,8 +128,10 @@ export class ControlledInverter extends ParametrizedComponentBase<ControlledInve
114128
}
115129

116130
protected override makeComponentSpecificContextMenuItems(): MenuItems {
131+
const s = S.Components.Generic.contextMenu
117132
return [
118-
this.makeChangeParamsContextMenuItem("inputs", S.Components.Generic.contextMenu.ParamNumBits, this.numBits, "bits"),
133+
this.makeChangeParamsContextMenuItem("inputs", s.ParamNumBits, this.numBits, "bits"),
134+
this.makeChangeBooleanParamsContextMenuItem(s.ParamControlBitAtBottom, this.controlPinsAtBottom, "bottom"),
119135
...this.makeForceOutputsContextMenuItem(true),
120136
]
121137
}

simulator/src/components/Demux.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ export class Demux extends ParametrizedComponentBase<DemuxRepr> {
242242
this.makeChangeParamsContextMenuItem("inputs", s.ParamNumFrom, this.numFrom, "from"),
243243
this.makeChangeParamsContextMenuItem("outputs", s.ParamNumTo, this.numTo, "to", [2, 4, 8, 16].map(x => x * this.numFrom)),
244244
["mid", MenuData.sep()],
245-
this.makeChangeBooleanParamsContextMenuItem(s.ParamControlAtBottom, this.controlPinsAtBottom, "bottom"),
245+
this.makeChangeBooleanParamsContextMenuItem(this.numSel === 1 ? S.Components.Generic.contextMenu.ParamControlBitAtBottom : S.Components.Generic.contextMenu.ParamControlBitsAtBottom, this.controlPinsAtBottom, "bottom"),
246246
["mid", toggleShowWiringItem],
247247
["mid", toggleUseHighZItem],
248248
...this.makeForceOutputsContextMenuItem(true),

simulator/src/components/Mux.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ export class Mux extends ParametrizedComponentBase<MuxRepr> {
209209
this.makeChangeParamsContextMenuItem("outputs", s.ParamNumTo, this.numTo, "to"),
210210
this.makeChangeParamsContextMenuItem("inputs", s.ParamNumFrom, this.numFrom, "from", [2, 4, 8, 16].map(x => x * this.numTo)),
211211
["mid", MenuData.sep()],
212-
this.makeChangeBooleanParamsContextMenuItem(s.ParamControlAtBottom, this.controlPinsAtBottom, "bottom"),
212+
this.makeChangeBooleanParamsContextMenuItem(this.numSel === 1 ? S.Components.Generic.contextMenu.ParamControlBitAtBottom : S.Components.Generic.contextMenu.ParamControlBitsAtBottom, this.controlPinsAtBottom, "bottom"),
213213
["mid", toggleShowWiringItem],
214214
...this.makeForceOutputsContextMenuItem(true),
215215
]
Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
1-
import { COLOR_BACKGROUND, COLOR_COMPONENT_BORDER, drawWireLineToComponent, GRID_STEP } from "../drawutils"
1+
import * as t from "io-ts"
2+
import { COLOR_BACKGROUND, COLOR_COMPONENT_BORDER, COLOR_MOUSE_OVER, drawWireLineToComponent, GRID_STEP } from "../drawutils"
23
import { div, mods, tooltipContent } from "../htmlgen"
34
import { S } from "../strings"
4-
import { HighImpedance, isHighImpedance, isUnknown, LogicValue, Unknown } from "../utils"
5-
import { ComponentBase, defineComponent, Repr } from "./Component"
6-
import { DrawableParent, DrawContext, GraphicsRendering } from "./Drawable"
5+
import { HighImpedance, isHighImpedance, isUnknown, LogicValue, typeOrUndefined, Unknown } from "../utils"
6+
import { defineParametrizedComponent, paramBool, ParametrizedComponentBase, Repr, ResolvedParams } from "./Component"
7+
import { DrawableParent, DrawContext, GraphicsRendering, MenuItems } from "./Drawable"
78

89
export const TristateBufferDef =
9-
defineComponent("tristate", {
10+
defineParametrizedComponent("tristate", true, true, {
11+
variantName: ({ bottom }) => `tristate${bottom ? "b" : ""}`,
1012
idPrefix: "tristate",
1113
button: { imgWidth: 50 },
14+
repr: {
15+
bottom: typeOrUndefined(t.boolean),
16+
},
1217
valueDefaults: {},
13-
size: { gridWidth: 7, gridHeight: 4 },
14-
makeNodes: () => ({
18+
params: {
19+
bottom: paramBool(),
20+
},
21+
validateParams: ({ bottom }) => {
22+
return { controlPinsAtBottom: bottom }
23+
},
24+
size: () => ({ gridWidth: 4, gridHeight: 4 }),
25+
makeNodes: ({ controlPinsAtBottom }) => ({
1526
ins: {
1627
In: [-4, 0, "w", { leadLength: 20 }],
17-
E: [0, -3, "n", "E (Enable)", { leadLength: 20 }],
28+
E: [0,
29+
controlPinsAtBottom ? 3 : -3,
30+
controlPinsAtBottom ? "s" : "n",
31+
"E (Enable)", { leadLength: 20 },
32+
],
1833
},
1934
outs: {
2035
Out: [+4, 0, "e", { leadLength: 20 }],
@@ -24,15 +39,23 @@ export const TristateBufferDef =
2439
})
2540

2641
type TristateBufferRepr = Repr<typeof TristateBufferDef>
42+
type TristateBufferParams = ResolvedParams<typeof TristateBufferDef>
2743

28-
export class TristateBuffer extends ComponentBase<TristateBufferRepr> {
44+
export class TristateBuffer extends ParametrizedComponentBase<TristateBufferRepr> {
2945

30-
public constructor(parent: DrawableParent, saved?: TristateBufferRepr) {
31-
super(parent, TristateBufferDef, saved)
46+
public readonly controlPinsAtBottom: boolean
47+
48+
public constructor(parent: DrawableParent, params: TristateBufferParams, saved?: TristateBufferRepr) {
49+
super(parent, TristateBufferDef.with(params), saved)
50+
51+
this.controlPinsAtBottom = params.controlPinsAtBottom
3252
}
3353

3454
public toJSON() {
35-
return this.toJSONBase()
55+
return {
56+
... this.toJSONBase(),
57+
bottom: this.controlPinsAtBottom === TristateBufferDef.aults.bottom ? undefined : this.controlPinsAtBottom,
58+
}
3659
}
3760

3861
public override makeTooltip() {
@@ -62,37 +85,16 @@ export class TristateBuffer extends ComponentBase<TristateBufferRepr> {
6285

6386
protected override doDraw(g: GraphicsRendering, ctx: DrawContext) {
6487

65-
const width = this.unrotatedWidth
66-
const height = this.unrotatedHeight
67-
const left = this.posX - width / 2
68-
// const right = left + width
69-
const top = this.posY - height / 2
70-
const bottom = top + height
71-
72-
if (ctx.isMouseOver) {
73-
const frameWidth = 2
74-
const frameMargin = 2
75-
g.lineWidth = frameWidth
76-
g.strokeStyle = ctx.borderColor
77-
g.beginPath()
78-
g.rect(
79-
left - frameWidth - frameMargin,
80-
top - frameWidth - frameMargin,
81-
width + 2 * (frameWidth + frameMargin),
82-
height + 2 * (frameWidth + frameMargin)
83-
)
84-
g.stroke()
85-
}
86-
8788
drawWireLineToComponent(g, this.inputs.In)
8889
drawWireLineToComponent(g, this.inputs.E)
8990
drawWireLineToComponent(g, this.outputs.Out)
9091

92+
const { top, bottom } = this.bounds()
9193
const gateWidth = (2 * Math.max(2, this.inputs._all.length)) * GRID_STEP
9294
const gateLeft = this.posX - gateWidth / 2
9395
const gateRight = this.posX + gateWidth / 2
9496
g.fillStyle = COLOR_BACKGROUND
95-
g.strokeStyle = COLOR_COMPONENT_BORDER
97+
g.strokeStyle = ctx.isMouseOver ? COLOR_MOUSE_OVER : COLOR_COMPONENT_BORDER
9698
g.lineWidth = 3
9799

98100
g.beginPath()
@@ -103,5 +105,11 @@ export class TristateBuffer extends ComponentBase<TristateBufferRepr> {
103105
g.stroke()
104106
}
105107

108+
protected override makeComponentSpecificContextMenuItems(): MenuItems {
109+
return [
110+
this.makeChangeBooleanParamsContextMenuItem(S.Components.Generic.contextMenu.ParamControlBitAtBottom, this.controlPinsAtBottom, "bottom"),
111+
]
112+
}
113+
106114
}
107115
TristateBufferDef.impl = TristateBuffer

0 commit comments

Comments
 (0)