4bit adder

This commit is contained in:
sfja 2026-06-11 05:10:35 +02:00
parent e8c8609217
commit 91329edd74
4 changed files with 109 additions and 7 deletions

View File

@ -286,7 +286,7 @@ export class Board {
).length; ).length;
const pinMax = Math.max(inputCount, outputCount); const pinMax = Math.max(inputCount, outputCount);
return new ComponentKind( return new ComponentKind(
v2(60 + name.length * 5, 40 + 10 * pinMax), v2(60 + name.length * 10, 40 + 10 * pinMax),
name, name,
new Array<null>(inputCount).fill(null), new Array<null>(inputCount).fill(null),
new Array<null>(outputCount).fill(null), new Array<null>(outputCount).fill(null),

View File

@ -336,15 +336,19 @@ class Placing implements State {
enter(): void { enter(): void {
this.unsubscribe = this.cx.events.subscribe( this.unsubscribe = this.cx.events.subscribe(
["MouseDownOffset", "MouseMove", "KeyDown"], ["MouseClickOffset", "MouseMove", "KeyDown"],
(ev) => { (ev) => {
switch (ev.tag) { switch (ev.tag) {
case "MouseDownOffset": { case "MouseClickOffset": {
const boardPos = ev.pos; const boardPos = ev.pos;
if (this.cx.board.canPlaceComponent(this.compDef, boardPos)) { if (this.cx.board.canPlaceComponent(this.compDef, boardPos)) {
this.cx.board.placeComponent(this.compDef, boardPos); this.cx.board.placeComponent(this.compDef, boardPos);
this.cx.events.send({ tag: "SaveRequest" }); this.cx.events.send({ tag: "SaveRequest" });
this.cx.transitionTo(new Normal(this.cx)); if (this.cx.keysPressed.has("Shift")) {
this.cx.transitionTo(new Placing(this.cx, this.tool));
} else {
this.cx.transitionTo(new Normal(this.cx));
}
} }
break; break;
} }
@ -362,7 +366,7 @@ class Placing implements State {
}, },
); );
this.cx.addComponentPlacer(v2(0, 0), this.compDef.size); this.cx.addComponentPlacer(v2(-100, 0), this.compDef.size);
} }
leave(): void { leave(): void {

View File

@ -28,12 +28,15 @@ export class Stmt {
return [k.op]; return [k.op];
case "And": case "And":
case "Or": case "Or":
case "Nand":
case "Nor":
return [k.lhs, k.rhs]; return [k.lhs, k.rhs];
case "Call": case "Call":
return [...k.inputs]; return [...k.inputs];
case "Elem": case "Elem":
return [k.src]; return [k.src];
} }
k satisfies never;
} }
replaceSource(oldStmt: Stmt, newStmt: Stmt) { replaceSource(oldStmt: Stmt, newStmt: Stmt) {
@ -52,6 +55,8 @@ export class Stmt {
break; break;
case "And": case "And":
case "Or": case "Or":
case "Nand":
case "Nor":
if (k.lhs === oldStmt) k.lhs = newStmt; if (k.lhs === oldStmt) k.lhs = newStmt;
if (k.rhs === oldStmt) k.rhs = newStmt; if (k.rhs === oldStmt) k.rhs = newStmt;
break; break;
@ -61,6 +66,8 @@ export class Stmt {
case "Elem": case "Elem":
if (k.src === oldStmt) k.src = newStmt; if (k.src === oldStmt) k.src = newStmt;
break; break;
default:
k satisfies never;
} }
} }
@ -84,7 +91,7 @@ export type StmtKind =
| { tag: "GetState"; state: State } | { tag: "GetState"; state: State }
| { tag: "SetState"; state: State; src: Stmt } | { tag: "SetState"; state: State; src: Stmt }
| { tag: "Not"; op: Stmt } | { tag: "Not"; op: Stmt }
| { tag: "And" | "Or"; lhs: Stmt; rhs: Stmt } | { tag: "And" | "Or" | "Nand" | "Nor"; lhs: Stmt; rhs: Stmt }
| { tag: "Call"; comp: Component; inputs: Stmt[] } | { tag: "Call"; comp: Component; inputs: Stmt[] }
| { tag: "Elem"; src: Stmt; i: number }; | { tag: "Elem"; src: Stmt; i: number };
@ -161,6 +168,16 @@ class StmtsMutater {
return this.comp.stmts[Symbol.iterator](); return this.comp.stmts[Symbol.iterator]();
} }
entries(): ArrayIterator<[number, Stmt]> {
return this.comp.stmts.entries();
}
replaceStmt(oldStmt: Stmt, newStmt: Stmt) {
this.comp.stmts = this.comp.stmts.map((stmt) =>
stmt === oldStmt ? newStmt : stmt,
);
}
replaceSource(oldStmt: Stmt, newStmt: Stmt) { replaceSource(oldStmt: Stmt, newStmt: Stmt) {
for (const stmt of this.comp.stmts) { for (const stmt of this.comp.stmts) {
stmt.replaceSource(oldStmt, newStmt); stmt.replaceSource(oldStmt, newStmt);
@ -207,8 +224,14 @@ export class ComponentOptimizer {
private optimizeWithOptions(removeUnusedStates: boolean) { private optimizeWithOptions(removeUnusedStates: boolean) {
const score = () => this.comp.stmts.length * 100 + this.comp.states.length; const score = () => this.comp.stmts.length * 100 + this.comp.states.length;
let iterations = 0;
let scoreBefore: number; let scoreBefore: number;
do { do {
if (iterations > 100) {
console.error("iterations > 100");
break;
}
scoreBefore = score(); scoreBefore = score();
this.eliminateRedundantState(); this.eliminateRedundantState();
@ -220,6 +243,10 @@ export class ComponentOptimizer {
if (removeUnusedStates) { if (removeUnusedStates) {
this.eliminateIndependentSetState(); this.eliminateIndependentSetState();
} }
this.rewriteToNandOrNor();
this.eliminateUnusedStmts();
iterations++;
} while (score() !== scoreBefore); } while (score() !== scoreBefore);
} }
@ -352,6 +379,65 @@ export class ComponentOptimizer {
} }
} }
rewriteToNandOrNor() {
const mut = new StmtsMutater(this.comp, this.replacedStates);
const useCount = new Map<Stmt, number>();
for (const stmt of mut) {
for (const src of stmt.sources()) {
useCount.set(src, (useCount.get(src) ?? 0) + 1);
}
}
for (let i = 0; i < this.comp.stmts.length; ++i) {
const stmt = this.comp.stmts[i];
if (stmt.kind.tag === "Not") {
const src = stmt.kind.op;
if (
(src.kind.tag === "And" || src.kind.tag === "Or") &&
useCount.get(src) === 1
) {
const newStmt = new Stmt({
tag: src.kind.tag === "And" ? "Nand" : "Nor",
lhs: src.kind.lhs,
rhs: src.kind.rhs,
});
mut.insertStmtAt(i, newStmt);
mut.removeStmt(stmt);
mut.replaceSource(stmt, newStmt);
}
}
}
}
eliminateUnusedStmts() {
const mut = new StmtsMutater(this.comp, this.replacedStates);
const useCount = new Map<Stmt, number>();
for (const stmt of mut) {
for (const src of stmt.sources()) {
useCount.set(src, (useCount.get(src) ?? 0) + 1);
}
}
const nonEffectfulStmts = new Set<StmtKind["tag"]>([
"And",
"Or",
"Nand",
"Nor",
"Elem",
"Input",
"GetState",
"Null",
]);
for (const stmt of mut) {
if (!useCount.get(stmt) && nonEffectfulStmts.has(stmt.kind.tag)) {
mut.removeStmt(stmt);
}
}
}
private indexMap(): Map<Stmt, number> { private indexMap(): Map<Stmt, number> {
return new Map(this.comp.stmts.map((stmt, i) => [stmt, i])); return new Map(this.comp.stmts.map((stmt, i) => [stmt, i]));
} }
@ -398,7 +484,7 @@ export class ComponentPrinter {
"\\c(color: #d44949; font-weight: bold)$&\\c", "\\c(color: #d44949; font-weight: bold)$&\\c",
) )
.replaceAll( .replaceAll(
/(?:Null)|(?:Input)|(?:Output)|(?:GetState)|(?:SetState)|(?:Not)|(?:And)|(?:Or)|(?:Call)|(?:Elem)/g, /(?:Null)|(?:Input)|(?:Output)|(?:GetState)|(?:SetState)|(?:Not)|(?:And)|(?:Or)|(?:Nand)|(?:Nor)|(?:Call)|(?:Elem)/g,
"\\c(color: orange)$&\\c", "\\c(color: orange)$&\\c",
); );
@ -449,11 +535,15 @@ export class ComponentPrinter {
return `${stmtId(stmt)} = Not ${stmtId(k.op)}`; return `${stmtId(stmt)} = Not ${stmtId(k.op)}`;
case "And": case "And":
case "Or": case "Or":
case "Nand":
case "Nor":
return `${stmtId(stmt)} = ${k.tag} ${stmtId(k.lhs)}, ${stmtId(k.rhs)}`; return `${stmtId(stmt)} = ${k.tag} ${stmtId(k.lhs)}, ${stmtId(k.rhs)}`;
case "Call": case "Call":
return `${stmtId(stmt)} = Call ${k.comp.label} (${k.inputs.map((s) => stmtId(s)).join(", ")})`; return `${stmtId(stmt)} = Call ${k.comp.label} (${k.inputs.map((s) => stmtId(s)).join(", ")})`;
case "Elem": case "Elem":
return `${stmtId(stmt)} = Elem ${stmtId(k.src)}, ${k.i}`; return `${stmtId(stmt)} = Elem ${stmtId(k.src)}, ${k.i}`;
default:
k satisfies never;
} }
} }
} }

View File

@ -66,6 +66,12 @@ export class Sim {
case "Or": case "Or":
regs[i] = operation((a, b) => a || b, k.lhs, k.rhs); regs[i] = operation((a, b) => a || b, k.lhs, k.rhs);
break; break;
case "Nand":
regs[i] = operation((a, b) => !(a && b), k.lhs, k.rhs);
break;
case "Nor":
regs[i] = operation((a, b) => !(a || b), k.lhs, k.rhs);
break;
case "Call": { case "Call": {
const outputs = new Array<boolean>(k.comp.outputs).fill(false); const outputs = new Array<boolean>(k.comp.outputs).fill(false);
new Sim( new Sim(
@ -85,6 +91,8 @@ export class Sim {
regs[i] = outputs[k.i]; regs[i] = outputs[k.i];
break; break;
} }
default:
k satisfies never;
} }
// console.log("Sim:", i, stmt.kind.tag, inputs, outputs, this.state); // console.log("Sim:", i, stmt.kind.tag, inputs, outputs, this.state);