if 0 {[Richard Suchenwirth] 2004-05-02 -- In this weekend fun project I played with simulation of digital hardware, namely "full-adders". While these are not spectacular in effect (they just put 1 and 1 together), they are essential for integer additions. In a 32-bit machine, you'd have 32 of such full-adders in parallel, but in this plaything I limited the "word-width" to three, as the principle should be clearer so. A full-adder has three input lines: the two "bits" to add, and a carry from the previous full-adder (drawn at right); and two outputs, namely the one-bit result of the addition, and its own carry. The following plaything contains * three adders, named a, b, and c, which are wired to * input switches (checkboxes) which you can turn on or off, and * "lamps" (the round things on the bottom), which turn bright or dark depending on whether they get "power". To make things even more pedagogical, the wires also change color to red when they carry power. When a checkbox is clicked, communication between the components goes via [trace]s: the checkbox notifies its wire, which changes color accordingly and sets the variable at the end of the wire. When an input to a full-adder changes, its outputs are recomputed, triggering the out wires, which again change color and update their other end. Testing made me think it all works as it should. Innocent as they look here, full-adders are in fact built up from two half-adders (two lines in, two lines out) and an inverter which could be built from two [NAND] gates, while half-adders themselves can be built up from five NAND gates ("a" above needs only be a half-adder, as no carry can come to him). A NAND gate, finally, can be implemented from two transistors. So to build this plaything in real discrete hardware, you'd have to wire the equivalent of 53 transistors together... My patience would fail long before that - good that we can simulate wiring in Tcl :) } proc main argv { set w [canvas .c -width 160 -height 160] pack $w foreach x {40 80 120} name {p3 p2 p1} {input $name $w $x 20} foreach x {60 100 140} name {q3 q2 q1} {input $name $w $x 50} adder a $w 130 80 adder b $w 90 100 adder c $w 50 120 foreach x {20 60 100 140} name {r4 r3 r2 r1} { lamp $name $w $x 150 } #-- wires from inputs to adders: wire $w p1 a1 wire $w q1 a2 wire $w p2 b1 wire $w q2 b2 wire $w p3 c1 wire $w q3 c2 #-- carry lines between adders: wire $w a4 b3 wire $w b4 c3 #-- to lamps: wire $w a5 r1 wire $w b5 r2 wire $w c5 r3 wire $w c4 r4 } # Hardware components: proc input {name w x y} { checkbutton $w.$name -variable $name -onvalue 1 -offvalue 0 $w create window $x $y -window $w.$name set ::g($name) [list $x $y] } proc adder {name w x y} { global g ${name}1 ${name}2 ${name}3 ${name}4 ${name}5 $w create rect [- $x 12] [- $y 7] [+ $x 12] [+ $y 7] $w create text $x $y -text $name set g(${name}1) [list [- $x 10] [- $y 7]] set g(${name}2) [list [+ $x 10] [- $y 7]] set g(${name}3) [list [+ $x 12] $y] set g(${name}4) [list [- $x 10] [+ $y 7]] set g(${name}5) [list [+ $x 10] [+ $y 7]] foreach i {1 2 3 4 5} {set ::$name$i 0} foreach i {1 2 3} {trace var $name$i w "add $w $name;#"} } proc add {w name} { global ${name}1 ${name}2 ${name}3 ${name}4 ${name}5 set sum [expr [set ${name}1]+[set ${name}2]+[set ${name}3]] set ${name}4 [expr $sum>1] ;# carry set ${name}5 [expr $sum%2] ;# lsb } proc lamp {name w x y} { global $name $w create oval [- $x 6] [- $y 6] [+ $x 6] [+ $y 6] -fill black -tag $name set ::g($name) [list $x $y] set $name 0 trace var $name w "toggleLamp $w $name ;#" } proc toggleLamp {w name} { global $name $w itemconfig $name -fill [expr {[set $name]? "yellow": "black"}] } proc wire {w from to} { global g $w create line [concat $g($from) $g($to)] -tag $from trace var ::$from w "toggleWire $w $from $to;#" } proc toggleWire {w from to} { global $from $to $w itemconfig $from -fill [expr {[set $from]? "red" : "black"}] set $to [set $from] } # Shortcut math: foreach op {+ -} {proc $op {a b} "expr \$a $op \$b"} main $argv bind . {exec wish $argv0 &; exit} ---- [Arts and crafts of Tcl-Tk programming]