My first computer was an Olivetti Programma 101 owned by my high school, and I spent many hours learning how to program using it. As chance would have it, it was also the first computer my wife worked with as well - though she only got a few hours. At any rate, I wrote this for my wife's birthday. Those of you who have also used this machine will recognize it immediately.
This program is not typical Tcl. Because our current computers have quite different screen resolutions, I needed to make it resizable, at least to
some extent. So I came up with a system to generate a display from a bitmap, and widgets are located by decimal places - .5 would be 50% of the way from the left side, and so on. It also extracts the actual button label from that portion of the image, so I don't need to specify it separately.
This program has been lightly tested, but there are probably still bugs. The worst I've dealt with are actually edit errors that moved widgets to West Succotash, Iowa or someplace because I dinged the table of widget placement data. Hopefully, this version straightens out all of that. It should also be noted there are quite a few new features added to the architecture to make it a little less painful to program, including editing of programs (if you made a mistake in entering the current instruction you could CLEAR and re-enter it, but if you don't notice the error right away and spot it scrolling up while you are entering a different command, well, you
lose and you have to re-enter the whole program all over again). Also, the blue buttons represent various extended functions allowing you to display the registers, edit the labels, and so on, and I also added the "rub out" button and the keyboard buffer display. You just had to remember what digits you keyed in with the actual hardware.
Aside from these additions the layout exactly follows the original except for moving the decimal wheel from the right to the left.
The variable "runfrom" can be set to "." or to "p101.exe" according to whether it is run as a script or as a tclkit. In either case, it needs a companion "images" directory with the following contents:
The program is below, but it needs a variety of images and so forth. I tried to upload those and got complaints about an image format not being text/wikit format. It would be easiest to just upload the p101.vfs as a zip file. If someone can help me circumvent these issues?
CMcC Could you please elaborate on "No .exe, no .zip, and no .gif either since I somehow changed the type of the file, whatever THAT means" ?
The wiki refuses to upload .exe and .zip files. Likely this is not a bug but a security feature, either one may host malware. Okay, re-reading the image upload and doing it the correct way seems to work - although I find the method counter-intuitive in the extreme - but when I uploaded a .ico file it was still assigned a page number and the .htm extension, and when I did "save Link" and renamed it back to .ico, it didn't work. Likely not 8-bit clean somewhere in the loop.
Now, after some thought, I expect this is because it is assuming that the upload file
replaces the original wiki page, and it won't do that. Again, probably a a feature rather than a bug. It leaves me not knowing how to upload an image as such under its own name and type.
Secondly, consider using the
Half Bakery for uploading your code. I think the basic problem here is that you're uploading a package which could be modelled as a collection containing several components, and wiki generally doesn't really lend itself to a hierarchy by inclusion.
I hadn't thought of the Half Bakery as a release mechanism. I'll look into that. <sigh>I don't see how to upload here, either. Uploading a .zip or a .tgz add the archive name preceded by an 'x' link, but it doesn't seem to have actually added it to the repository and pressing the x just seems to delete it.</sigh> I really agree with
uniquename, a simply upload button would require far less explanation or documentation. It reminds me of the whole "how come Tcl barfs when it sees 09?" controversy.
package provide app-p101 1.0
# set runfrom p101.exe
set runfrom .
proc setfonts { brw brh bufw dispch spfnsz brfnsz \
txtfnsz HLfnsz abtfnsz regfnsz lblfnsz \
} {
set ::brheight $brh
set ::brwidth $brw
set ::bufwidth $bufw
set ::dispch $dispch
set ::spfn [ font create -family courier -weight bold -size $spfnsz ]
set ::brfn [ font create -family courier -weight bold -size $brfnsz ]
set ::txtfn [ font create -family courier -weight bold -size $txtfnsz ]
set ::HLfn [ font create -family tahoma -weight bold -size $HLfnsz ]
set ::abtfn [ font create -family tahoma -weight normal -size $abtfnsz ]
set ::regfn [ font create -family tahoma -size $regfnsz ]
set ::lblfn [ font create -family tahoma -weight normal -size $lblfnsz ]
}
proc log { msg } {
if !$::uselog return
if !$::logopen {
if { [ catch {
set ::logfile [ open ./logfile.txt w ]
set ::logopen 1
} error ] != 0 } {
set ::uselog 0
}
}
puts $::logfile $msg
flush $::logfile
}
proc announce { msg } {
tk_messageBox -parent .top -title "Info" \
-icon info -type ok -message $msg
}
proc reset { { override 0 } } {
if !$override {
if {[tk_messageBox -parent . -title "Are you sure?" \
-icon question -type yesno -default no \
-message "Reset P101 Simulator?"] ne "yes"
} {
return
}
}
.top.printarea.text delete 0.0 end
.prog.list.list delete 0 end
.prog.list.list insert end "end" ; renum .prog.list
set ::pc 0
set ::reg ""
set ::confirm 0
set ::entering 0
set ::recording 0
set ::interactive 1
set ::register(A) 0.0
set ::register(B) 0.0
set ::register(b) 0.0
set ::register(C) 0.0
set ::register(c) 0.0
set ::register(D) 0.0
set ::register(d) 0.0
set ::register(E) 0.0
set ::register(e) 0.0
set ::register(F) 0.0
set ::register(f) 0.0
set ::register(R) 0.0
set ::register(M) 0.0
set stack {}
set regstack {}
.top.red configure -bg $::dkred
foreach w $::btnlist { $w configure -state normal }
.top.face delete vwyzlbl
updlbls
}
proc down { reg } {
set ::register(A) $::register($reg)
interactive $::register($reg) $reg \u2193
}
proc up { reg } {
set ::register($reg) $::register(M)
interactive $::register($reg) $reg \u2191
}
proc swap { reg } {
if { $reg eq "/" } {
set A $::register(A)
set ::register(M) [= {$A-entier($A)} ]
interactive $::register(M) / \u2195
return
}
if { $reg eq "A" } {
set ::register(A) [= {abs($::register(A))}]
} else {
set temp $::register(A)
set ::register(A) $::register($reg)
set ::register($reg) $temp
}
interactive $::register($reg) $reg \u2195
}
proc sqrt { reg } {
set ::register(A) [= {sqrt($::register($reg))}]
set ::register(M) $::register($reg)
interactive $::register(A) $reg \u221a
}
proc minus { reg } {
set ::register(A) [= {$::register(A) - $::register($reg)}]
set ::register(M) $::register($reg)
interactive $::register(A) $reg -
}
proc times { reg } {
set ::register(A) [= {$::register(A) * $::register($reg)}]
set ::register(M) $::register($reg)
interactive $::register(A) $reg \u00d7
}
proc plus { reg } {
set ::register(A) [= {$::register(A) + $::register($reg)}]
set ::register(M) $::register($reg)
interactive $::register(A) $reg +
}
proc divide { reg } {
set A [= {entier($::register(A))}]
if {[ string first "." $::register(A) ] == -1 } {
set ::register(A) $::register(A).0
}
if { 0 != [ catch {
set ::register(A) [= {$::register(A) / $::register($reg)}]
set ::register(R) 0
set R [= {entier($::register($reg))}]
set ::register(R) [= {$A % $R}]
set ::register(M) $::register($reg)
interactive $::register(A) $reg \u00f7
} ] } { redlight -1.0 }
}
proc zero { reg } {
set ::register($reg) 0
interactive $::register($reg) ${reg} *
}
proc prreg { reg } {
type $::register($reg) ${reg}\u25c7
}
proc renum { w } {
$w.linecount delete 0 end
set maxj [ $w.list index end ]
for { set j 0 } { $j < $maxj } { incr j } {
$w.linecount insert end $j
}
}
proc insins { instr } {
.prog.list.list insert $::pc "$instr"
type "$::pc: $instr"
renum .prog.list
}
proc delins { } {
if { [ .prog.list.list get $::pc ] eq "end" } return
.prog.list.list delete $::pc $::pc
renum .prog.list
}
proc clear { } {
if $::recording {
delins
return
}
.top.red configure -bg $::dkred
.top.buffer.entry delete 0 end
foreach w $::btnlist { $w configure -state normal }
}
proc recprog { } {
if $::recording {
set ::recording 0
.top.recprog configure -relief raised
type "...end"
return
}
type "Begin..."
set ::recording 1
.top.recprog configure -relief sunken
.top.buffer.entry delete 0 end
}
proc prprog { } {
type "Listing..."
set maxj [ .prog.list.list index end ]
for { set j 0 } { $j < $maxj } { incr j } {
set ins [ .prog.list.list get $j ]
type "$j: $ins"
}
type "...end"
}
proc paperadv { } { type "" }
proc paperclr { } {
.top.printarea.text delete 1.0 end
set ::value ""
}
proc papersav { } {
set tapename [ tk_getSaveFile -defaultextension .txt \
-initialdir . -parent .top -title "Save Paper Tape..." ]
if { $tapename ne "" } {
set f [ open $tapename w ]
fconfigure $f -encoding utf-8
set tape [ .top.printarea.text get 1.0 end ]
puts $f $tape
close $f
}
}
proc page { number } {
if { $::curpage != 0 } {
grid forget .top.about.pg${::curpage}txt
.top.about.pg${::curpage} configure -relief groove
}
set ::curpage $number
grid .top.about.pg${number}txt -row 3 -column 0 -columnspan 6 -rowspan 20 -sticky news
.top.about.pg${::curpage} configure -relief solid
}
proc manual { } {
set savename [ tk_getSaveFile -defaultextension pdf \
-initialfile olpro101.pdf \
-parent .top -title "Save Manual To..." ]
if { $savename ne "" } {
file copy -force $::manpdf $savename
}
}
proc prefs { } {
destroy .top.options
toplevel .top.options
label .top.options.title -text "PROGRAMMA-101\nSIMULATOR OPTIONS"
grid .top.options.title -row 0 -column 0 -columnspan 2
checkbutton .top.options.animation \
-variable ::animation -text "Card Animation"
grid .top.options.animation -row 1 -column 0
button .top.options.close -text "Close Options" -command {
destroy .top.options
}
button .top.options.manual -text "Save Manual" -command manual
grid .top.options.manual -row 2 -column 0
grid .top.options.close -row 3 -column 0 -columnspan 2
}
proc about { } {
set tabs \t
if { $::size == 100 } { set tabs \t\t }
destroy .top.about
toplevel .top.about
label .top.about.title -text "OLIVETTI-UNDERWOOD\nPROGRAMMA 101" \
-font $::HLfn
grid .top.about.title -row 0 -column 0 -columnspan 5
label .top.about.blurb -text "by Larry Smith" -font $::HLfn
grid .top.about.blurb -row 1 -column 0 -columnspan 5
button .top.about.pg1 -command { page 1 } -text "Credits" \
-font $::HLfn -relief groove
grid .top.about.pg1 -row 2 -column 0
button .top.about.pg2 -command { page 2 } -text "Machine" \
-font $::HLfn -relief groove
grid .top.about.pg2 -row 2 -column 1
button .top.about.pg3 -command { page 3 } -text "Simulator" \
-font $::HLfn -relief groove
grid .top.about.pg3 -row 2 -column 2
button .top.about.pg4 -command { page 4 } -text "Basic Ops" \
-font $::HLfn -relief groove
grid .top.about.pg4 -row 2 -column 3
button .top.about.pg5 -command { page 5 } -text "Extensions" \
-font $::HLfn -relief groove
grid .top.about.pg5 -row 2 -column 4
label .top.about.pg1txt -width 66 -bd 5 -relief solid -justify left -font $::abtfn -text "© 2009 by Larry Smith\n\nWritten for Marjie Smith, my wife, for her birthday. The Olivetti\nProgramma 101 was the first real computer I ever used and it was\nthe first Marjie ever used, too. It has great nostalgic value for us.\n\n\n\n\n\n\n"
label .top.about.pg2txt -width 66 -bd 5 -relief solid -justify left -font $::abtfn -text "The Programma 101 was the first machine to be marketed as\n\"microcomputer\". It was built with many transistors worth of\ndiscrete logic, and used a delay line to implement the memory\nregisters. By modern standards it was limited - five registers\n(B, C, D, E and F) which could each be split in two if you could\nlive with half the accuracy). It stored just 48 instructions in\nits program, and began eating up registers F, E, and D if you\nexceeded that number, to a maximum of 120 instructions with\nonly two registers left.\n\n\n"
label .top.about.pg3txt -width 66 -bd 5 -relief solid -justify left -font $::abtfn -text "The simulator emulates the original machine, but it does not\nenforce its' limits. That is to say, you can split the F register\ninto F and f (F-split) but they are not reduced in size, each is\nreally a separate register.\n\nThe program is also not limited to 48 or 120 steps, it can be any\nlength, and the F, E and D registers are never used.\n\n\n\n\n"
label .top.about.pg4txt -width 66 -bd 5 -relief solid -justify left -font $::abtfn -text "\[To A\u2193\]\tdown\tTransfers named register to accumulator.\n\[M\u2191\]\tup\tMoves contents of M (keyboard) register to named reg.\n\[A\u2195\]\tswap\tSwitches contents of named register and accumulator.\n\[\u221a\]\tsqrt\tLoads accumulator with the square root of the reg.\n\[\u2014\]\tminus\tSubtracts named reg from accumulator.\n\[\u00d7\]\ttimes\tMultiplies named reg by accumulator.\n\[+\]\tplus\tAdds named reg to accumulator.\n\[\u00f7\]\tdivide\tDivides accumulator by named reg.\n\[*\]\tzero\tStores 0 in named reg.\n\[\u25c7\]\tprreg\tPrints the named reg.\n\[S\]\tstart\tStarts execution at current program step.\n\[/\]\tsplit\tUsed to address split registers a (A/), b (B/) etc."
label .top.about.pg5txt -width 66 -bd 5 -relief solid -justify left -font $::abtfn -text "Buttons with a blue background are for extensions to the machine.\n\nSave Tape\tSaves contents of display tape to utf-8 text file.\nClear Tape\tClears display tape.\nPush Regs\tSave all regs but A, R & M to internal stack.\nPull Regs\t\tRestore all regs but A, R & M from internal stack.\nProg Library\tSets directory to search for program cards.\nSave Card${tabs}Saves current program to program card.\nShow Labels\tDisplays window to permit editing VXYZ labels.\nShow Regs\tDisplays window showing current contents of regs.\nShow Prog\tDisplays window showing current program.\n"
button .top.about.exit -text "Exit Simulator" \
-command off -border 3 -font $::abtfn
grid .top.about.exit -row 23 -column 1
button .top.about.close -text "Close About..." \
-command { destroy .top.about } -border 3 -font $::abtfn
grid .top.about.close -row 23 -column 3
page 1
center .top.about
}
proc setdec { args } {
set ::tcl_precision $::numdecs
}
proc type { str { suffix "" } } {
if { $suffix eq "" } {
set prstr $str
} else {
set prstr "[format "%[= {$::dispch-4}].[set ::numdecs]f" $str] ${suffix}"
}
.top.printarea.text insert end \n$prstr
.top.printarea.text see end
}
proc blink { } {
if $::greenon return
set ::greenon 1
.top.green configure -bg $::brgreen
set delay 300
if !$::interactive { set delay 100 }
after $delay {
.top.green configure -bg $::dkgreen
set ::greenon 0
}
}
proc off { } { exit;
if {[tk_messageBox -parent . -title "Are you sure?" \
-icon question -type yesno -default no \
-message "Exit P101 Simulator?"] eq "yes"
} {
exit
}
}
proc redlight { code } {
.top.green configure -bg $::dkgreen
.top.red configure -bg $::brred
if $::interactive { type $code E! } else { type $code PC }
set ::interactive 1
foreach w $::btnlist { $w configure -state disabled }
set ::register(A) 0.0
}
proc jump { label } {
set ::interactive 0
set reg [ string index $label 0 ]
set cond 0
if { [ string first $reg "/cdr" ] != -1 } {
set cond 1
}
set pc $::pc
if { !$cond || ($::register(A) > 0) } {
set pc [ lsearch -exact $::program $::jumps($label) ]
}
if { $pc == -1 } {
set ::interactive 1
redlight $::pc
set ::pc 0
return 0
}
set ::pc $pc
return 1
}
proc isjump { label } {
set result 0
if { ($label ne "") && ($label ne " S") } {
set reg [ string index $label 0 ]
set cmd [ string index $label 1 ]
if { ([ string first $cmd "VWYZ" ] != -1 ) &&
([ string first $reg " MCDR/cdr" ] != -1 )
} {
set result 1
}
}
return $result
}
proc start { { label "" } } {
if $::recording { set ::curbtn start ; return }
if { ![info exists ::pc] || ($::pc eq "") } { set ::pc 0 }
set ::interactive 0
set ::program [ .prog.list.list get 0 end ]
if { $label ne "" } { jump $label }
while 1 {
blink
set ins [ lindex $::program $::pc ]
set cmd [ string index $ins end ]
set reg [ string index $ins end-1 ]
incr ::pc
if { $ins eq "end" } {
if [ pullstate ] continue
set ::pc 0
return
} elseif { $ins eq " S" } { return
} elseif { [string first $cmd "VWYZ" ] !=-1 } {
# either label or branch. Labels are ignored
if [ isjump $ins ] {
if ![jump $ins] return
}
} else {
set cmd $::op2cmd($cmd)
set value [ string range $ins 0 end-2 ]
if { $value ne "" } { set ::register(M) $value }
$cmd $reg
if $::interactive return
}
}
}
proc interactive { str reg op } {
if $::interactive { type $str ${reg}$op }
blink
}
proc nextkey { } {
while 1 {
vwait ::curbtn
if { [lsearch $::immediate $::curbtn] != -1 } {
$::curbtn
} else {
set btntype [ string range $::curbtn 0 2 ]
if { ($btntype eq "num") || ($btntype eq "reg") } {
set ::curbtn [ string index $::curbtn end ]
if { $::curbtn eq "!" } { set ::curbtn . }
} elseif { [ string first $::curbtn "vwyz" ] != -1 } {
set ::curbtn [ string toupper $::curbtn ]
} elseif [ info exists ::cmd2op($::curbtn) ] {
set ::curbtn $::cmd2op($::curbtn)
}
return $::curbtn
}
}
}
proc backsp { } {
.top.buffer.entry delete [= {[string length [.top.buffer.entry get]]-1}]
}
proc cmdloop { } {
set delbuffer 1
while 1 {
set reg ""
set btn [nextkey]
if {[string first $btn "0123456789.-" ] != -1} {
if $delbuffer {
.top.buffer.entry delete 0 end
set delbuffer 0
set havedec 0
}
if { $btn eq "." } {
if !$::havedec {
set ::havedec 1
.top.buffer.entry insert end .
}
} elseif { $btn eq "-" } {
if {[ string index $::buffer 0 ] eq "-" } {
.top.buffer.entry delete 0 1
} else {
.top.buffer.entry insert 0 -
}
} else {
.top.buffer.entry insert end $btn
}
continue
}
set value ""
if { $::buffer ne "" } {
if { [ string first "." $::buffer ] != -1 } {
set ::buffer ${::buffer}.0
}
set value $::buffer
if $::interactive { set ::register(M) $value }
}
set delbuffer 1
# value (if any) dealt with. btn should now be op, split or reg
if {[string first $btn "ABCDEFMR/"] != -1 } {
# it's a register name
set reg [ string index $btn end ]
set btn [ nextkey ] ;# look for op or split
if { $btn eq "/" } {
set reg [ string tolower $reg ]
if { $reg eq "m" } { set reg / }
set btn [nextkey] ;# op MUST follow now
}
}
# we get here we have a reg and op is in btn
if { $reg eq "" } { set reg M } ;# default register
if $::recording {
if { [string first $btn "VWXZ" ] != -1 } {
if { $reg eq "M" } { set reg " " }
insins "$value$reg$btn"
} else {
if { $btn eq "S" } { set reg " " }
insins "$value$reg$btn"
}
.top.buffer.entry delete 0 end
incr ::pc
} else {
if [ isjump $reg$btn ] {
start $reg$btn
set ::interactive 1
.top.buffer.entry delete 0 end
.top.buffer.entry insert end $::register(M)
} else {
if { [catch { $::op2cmd($btn) $reg } err ] } {
$btn
}
}
}
}
}
proc loadlistbox { w values } {
set j true
set indx 0
$w.list delete 0 end
$w.linecount delete 0 end
foreach i $values {
$w.list insert end $i
$w.linecount insert end $indx
if {$j} {
set j false
$w.list itemconfigure $indx -background #ffffdd
} else {
set j true
}
incr indx
}
}
proc scrolledlistbox { w width height values cmd { font "" }} {
log "height is $height"
if { $font eq "" } { set font $::txtfn } else { set font $::brfn }
frame $w
listbox $w.list -width $::brwidth -height $::brheight -font $font
listbox $w.linecount -width 4 -height $::brheight -font $font
$w.list configure -yscrollcommand "$w.scrl set"
#$w.linecount configure -yscrollcommand "$w.scrl set"
scrollbar $w.scrl -command "$w.list yview; $w.linecount yview"
pack $w.scrl -side right -fill y
pack $w.linecount -side left -fill y
pack $w.list -side left -fill both -expand 1
loadlistbox $w $values
# bindings
#
# this will obtain the item clicked, and then pass
# the value onto the proc specified in the variable cmd.
eval "bind $w.list <ButtonRelease-1> \{$cmd \[\%\W get \@\%x,\%y\]\}"
# return the widget path
return $w
}
proc scrolledtextarea {w l t r b } {
set width [= {round(($r-$l)*$::dispw)}]
set height [= {round(($b-$t)*$::disph)}]
set x [= {round($l*$::dispw)}]
set y [= {round($t*$::disph)}]
frame $w -width $width -height $height -bd 2 -bg white
place $w -x $x -y $y
scrollbar $w.vscroll -orient vertical -command [ list $w.text yview ]
scrollbar $w.hscroll -orient horizontal -command [ list $w.text xview ]
text $w.text -yscrollcommand [ list $w.vscroll set ] \
-xscrollcommand [ list $w.hscroll set ] \
-font $::txtfn -bg white -width 1 -height 4 -width $::dispch
pack $w.vscroll -side right -fill y
pack $w.hscroll -side top -fill x
pack $w.text -side left
}
proc uptodate {filename {time 0}} {
set filename [file join [pwd] $filename]
set mtime [file mtime $filename]
if {$mtime > $time} {source $filename}
after 1000 [list uptodate $filename $mtime]
} ;#RS
proc reloadlib {} {
set proglist ""
catch { set proglist [glob -directory $::library *.p101] }
.top.cardlist.list delete 0 end
foreach file $proglist {
.top.cardlist.list insert end $file
}
renum .top.cardlist
}
proc proglib {} {
set newlib [ tk_chooseDirectory -initialdir $::library \
-mustexist 1 -parent .top -title "Library Directory" ]
if { $newlib ne "" } { set ::library $newlib }
}
proc savecard {} {
set progname [ tk_getSaveFile -defaultextension p101 \
-initialdir $::library -parent .top -title "Save Program To..." ]
if { $progname ne "" } {
set f [ open $progname w ]
fconfigure $f -encoding utf-8
puts $f "[ .prog.list.list get 0 end ]"
foreach reg { A B b C c D d E e F f R } {
puts $f $::register($reg)
}
foreach lbl { v w y z } {
set text [.vwyz.${lbl}txt get 1.0 end ]
set text [ split $text \n ]
set text [ join $text "\\n" ]
puts $f $text
}
puts $f $::pc
puts $f $::numdecs
close $f
}
animatecard 1
reloadlib
}
proc runcard { args } {
set cardname $::register(M)
set ::register(M) 0.0
pushstate $cardname
}
set ::animating 0
proc animatecard { { reverse 0 } } {
if $::animating return
set ::animating 1
destroy .card
if !$::animation return
set cardw [ image width card ]
set cardh [ image height card ]
toplevel .card
wm overrideredirect .card 1
canvas .card.c -width $cardw -height $cardh
pack .card.c
.card.c create image 0 0 -image card -anchor nw
update
set leftsh [= {round($::dispw*0.6955)}]
set bottomsh [= {round($::disph*0.031 )}]
set left [= {[ winfo rootx .top ] + $leftsh}]
set showlabels 0
if $reverse {
set bottom [= {[winfo rooty .top.cdrdr] + $bottomsh}]
set curh 1
while { $curh < $cardh } {
wm geometry .card ${cardw}x$curh+$left+$bottom
update
incr curh
incr bottom -1
}
} else {
set rooty [winfo rooty .top.cdrdr]
set cardh [winfo height .card]
set bottom [= {$rooty-$cardh+$bottomsh}]
set curh $cardh
while { $curh > 0 } {
wm geometry .card ${cardw}x$curh+$left+$bottom
update
incr curh -1
incr bottom
}
}
set ::animating 0
destroy .card
update
}
proc loadprog { } {
foreach ins $::program {
.prog.list.list insert end $ins
}
}
proc loadcard { cardname } {
set h [winfo height .top]
set w [winfo width .top]
if $::recording {
insins "$cardname @"
return
}
if { $cardname eq "" } return
set f [ open $cardname r ]
fconfigure $f -encoding utf-8
set ::program [ gets $f ]
foreach reg { A B b C c D d E e F f R} {
set ::register($reg) [ gets $f ]
}
foreach lbl { v w y z } {
.vwyz.${lbl}txt delete 1.0 end
eval set lbltxt [ gets $f ]
set lbltxt [ string trim $lbltxt ]
.vwyz.${lbl}txt insert end $lbltxt
}
updlbls
set ::pc [ gets $f ]
set ::numdecs [ gets $f ]
close $f
.prog.list.list delete 0 end
loadprog
renum .prog.list
wm geometry .top ${w}x$h
animatecard
}
proc updlbls { } {
.top.face delete vwxylbls
set y [= {round($::dispw*.72)}]
foreach { lbl offset } { v .720 w .800 y .880 z .960 } {
set x [= {round($offset*$::dispw)}]
set lbltxt [ .vwyz.${lbl}txt get 1.0 end ]
if { $lbltxt ne "" } {
.top.face create text $x $y -fill black -font $::lblfn \
-tags vwxylbls -anchor s -justify center -text $lbltxt
}
}
.top.face raise vwxylbls
}
proc setpc { instr } {
set ::pc [ .prog.list.list curselection ]
}
proc showprog { args } {
if $::progshowing {
wm withdraw .prog
set ::progshowing 0
.top.showprog configure -text "Show\nProg"
} else {
wm deiconify .prog
set ::progshowing 1
.top.showprog configure -text "Hide\nProg"
}
}
proc showlabels { args } {
if $::labelshowing {
set ::labelshowing 0
wm withdraw .vwyz
} else {
set ::labelshowing 1
wm deiconify .vwyz
}
}
proc pushregs { } {
if $::recording { set ::curbtn pushregs ; return }
set state [ list \
$::register(B) $::register(b) $::register(C) $::register(c) \
$::register(D) $::register(d) $::register(E) $::register(e) \
$::register(F) $::register(f) ]
lappend ::regstack $state
}
proc pullregs { } {
if $::recording { set ::curbtn pullregs ; return }
if { $::regstack eq {} } { return 0 }
set state [lindex end $::regstack]
set ::stack [lrange $::regstack 0 end-1]
foreach [list \
::register(B) ::register(b) ::register(C) ::register(c) \
::register(D) ::register(d) ::register(E) ::register(e) \
::register(F) ::register(f) ] \
$state break
return 1
}
# push and pull save everything but registers AM&R, which can
# be used to pass results back to a previous program
proc pushstate { newcard } {
lappend ::stack $::program
lappend ::stack $::pc
pushregs
loadcard $newcard
}
proc pullstate { } {
if { $::stack eq {} } { return 0 }
set ::pc [lindex end $::stack]
set ::stack [lrange $::regstack 0 end-1]
set ::program [lindex end-1 $::stack]
set ::stack [lrange $::regstack 0 end-1]
pullregs
return 1
}
proc showregs { args } {
if !$::regssetup {
foreach reg { A B b C c D d E e F f M R } {
.regs.reglist.linecount insert end $reg
.regs.reglist.list insert end $::register($reg)
trace add variable ::register($reg) write updregs
}
set ::regssetup 1
}
if $::regsshowing {
wm withdraw .regs
set ::regsshowing 0
.top.showregs configure -text "Show\nRegs"
} else {
wm deiconify .regs
set ::regsshowing 1
.top.showregs configure -text "Hide\nRegs"
}
}
proc updregs { args } {
.regs.reglist.list delete 0 end
foreach reg { A B b C c D d E e F f M R } {
.regs.reglist.list insert end $::register($reg)
}
}
proc center { w { width 0 } { height 0 } } {
update
if { $width == 0 } { set width [winfo width $w] }
if { $height == 0 } { set height [winfo height $w] }
set x [= {([winfo vrootwidth $w] - $width ) / 2 }]
set y [= {([winfo vrootheight $w] - $height ) / 2 }]
wm geometry $w ${width}x${height}+${x}+${y}
}
proc showsplash { } {
destroy .splash
toplevel .splash
wm overrideredirect .splash 1
canvas .splash.c
pack .splash.c -side top -fill both -expand 1
image create photo splash -file $::runfrom/images/splash.gif
.splash.c create image 0 0 -image splash -anchor nw
center .splash 600 655
update
after 3000 {
destroy .splash
}
}
proc buildvwyz { } {
destroy .vwyz
toplevel .vwyz
wm protocol .vwyz WM_DELETE_WINDOW showlabels
wm withdraw .vwyz
label .vwyz.title -text "Labels:" -font regfn
grid .vwyz.title -row 0 -column 0 -columnspan 2
label .vwyz.v -text "V:" -font regfn
grid .vwyz.v -row 1 -column 0 -sticky news
label .vwyz.w -text "W:" -font regfn
grid .vwyz.w -row 2 -column 0 -sticky news
label .vwyz.y -text "Y:" -font regfn
grid .vwyz.y -row 3 -column 0 -sticky news
label .vwyz.z -text "Z:" -font regfn
grid .vwyz.z -row 4 -column 0 -sticky news
text .vwyz.vtxt -width 10 -font regfn -height 5
grid .vwyz.vtxt -row 1 -column 1 -sticky news
text .vwyz.wtxt -width 10 -font regfn -height 5
grid .vwyz.wtxt -row 2 -column 1 -sticky news
text .vwyz.ytxt -width 10 -font regfn -height 5
grid .vwyz.ytxt -row 3 -column 1 -sticky news
text .vwyz.ztxt -width 10 -font regfn -height 5
grid .vwyz.ztxt -row 4 -column 1 -sticky news
button .vwyz.updlbls -font regfn -command updlbls \
-text "Update Labels"
grid .vwyz.updlbls -row 5 -column 0 -columnspan 2 -sticky ew
update
}
proc buildprog { } {
destroy .prog
toplevel .prog
wm protocol .prog WM_DELETE_WINDOW showprog
wm withdraw .prog
scrolledlistbox .prog.list 20 10 "" setpc
.prog.list.list insert end end; renum .prog.list
label .prog.label -text "Program:" -anchor w
pack .prog.label .prog.list
update
}
proc buildregs { } {
destroy .regs
toplevel .regs
wm protocol .regs WM_DELETE_WINDOW showregs
wm withdraw .regs
scrolledlistbox .regs.reglist 20 13 "" ""
label .regs.label -text "Registers:" -anchor w
pack .regs.label .regs.reglist
update
}
proc buildgui { args } {
global size dispw disph
destroy .top
toplevel .top
wm protocol .top WM_DELETE_WINDOW { off }
image create photo card -file $::runfrom/images/p101card-${size}%.gif
image create photo p101 -file $::runfrom/images/p101.gif
image create photo cr -file $::runfrom/images/cardreader-${size}%.gif
image create photo 1xparentpx -file $::runfrom/images/1xparentpx.gif
image create photo p101face -file $::runfrom/images/P101-${size}%.gif
image create photo btnup -file $::runfrom/images/P101-${size}%.gif
#image create photo btndn \
-file $::runfrom/images/P101-${size}%-dark.png
set dispw [ image width p101face ]
set disph [ image height p101face ]
switch $size {
50 { setfonts 18 2 32 33 6 6 8 8 8 8 6 }
75 { setfonts 22 4 27 29 10 8 14 14 12 12 8 }
100 { setfonts 20 4 25 28 16 12 20 20 16 16 12 }
}
update
set btns [ list \
.top.about 0.0852 0.0 0.2335 0.1260 \
.top.paperadv 0.01052 0.2087 0.0727 0.3930 \
.top.prefs 0.0096 0.4715 0.0746 0.5556 \
.top.reset 0.0096 0.5840 0.0746 0.6680 \
.top.off 0.00956 0.6965 0.0746 0.7805 \
.top.regF 0.0852 0.4580 0.1703 0.5704 \
.top.regE 0.0852 0.5705 0.1703 0.6829 \
.top.regD 0.0852 0.6830 0.1703 0.7953 \
.top.regC 0.0852 0.7954 0.1703 0.9078 \
.top.showlabels 0.0852 0.9079 0.1703 0.9986 \
.top.up 0.1703 0.4580 0.3388 0.5704 \
.top.regB 0.1703 0.6830 0.3388 0.7953 \
.top.clear 0.1703 0.5705 0.3388 0.6829 \
.top.reg/ 0.1703 0.7954 0.3388 0.9078 \
.top.showregs 0.1703 0.9079 0.2545 0.9986 \
.top.showprog 0.2555 0.9079 0.3388 0.9986 \
.top.num7 0.3426 0.4580 0.4230 0.5704 \
.top.num4 0.3426 0.5705 0.4230 0.6829 \
.top.num1 0.3426 0.6830 0.4230 0.7953 \
.top.num0 0.3426 0.7954 0.4230 0.9078 \
.top.pushregs 0.3426 0.9079 0.4230 0.9986 \
.top.num8 0.4239 0.4580 0.5091 0.5704 \
.top.num5 0.4239 0.5705 0.5091 0.6829 \
.top.num2 0.4239 0.6830 0.5091 0.7953 \
.top.num! 0.4239 0.7954 0.5091 0.9078 \
.top.pullregs 0.4239 0.9097 0.5091 0.9986 \
.top.num9 0.5100 0.4580 0.5943 0.5704 \
.top.num6 0.5100 0.5705 0.5943 0.6929 \
.top.num3 0.5100 0.6830 0.5943 0.7953 \
.top.num- 0.5100 0.7954 0.5943 0.9078 \
.top.proglib 0.5100 0.9097 0.5942 0.9986 \
.top.start 0.5962 0.4580 0.6804 0.9078 \
.top.savecard 0.5962 0.9097 0.6804 0.9986 \
.top.recprog 0.6813 0.35 0.839 0.41 \
.top.down 0.6813 0.4580 0.8402 0.5704 \
.top.minus 0.6813 0.5705 0.7590 0.6929 \
.top.plus 0.6813 0.6830 0.7590 0.7953 \
.top.v 0.6813 0.7954 0.7664 0.8550 \
.top.times 0.7608 0.5705 0.8402 0.6929 \
.top.divide 0.7608 0.6830 0.8402 0.7953 \
.top.w 0.7608 0.7954 0.9262 0.8564 \
.top.prprog 0.8411 0.35 0.999 0.41 \
.top.swap 0.8411 0.4580 0.9196 0.8550 \
.top.regA 0.8411 0.5705 0.9196 0.6929 \
.top.regR 0.8411 0.6830 0.9196 0.7953 \
.top.y 0.8411 0.7954 0.9196 0.8564 \
.top.sqrt 0.9206 0.4580 0.9990 0.8550 \
.top.prreg 0.9206 0.5705 0.9990 0.6929 \
.top.zero 0.9206 0.6830 0.9990 0.7953 \
.top.z 0.9206 0.7954 0.9990 .8580 \
.top.papersav 0.0105 0.1463 0.0727 0.2073 \
.top.paperclr 0.0105 0.3957 0.0727 0.4566 \
.top.backsp 0.5962 0.25 0.6804 0.455 \
]
canvas .top.face -width $dispw -height $disph
.top.face create image 0 0 -image p101face -anchor nw
place .top.face -x 0 -y 0
update
center .top $dispw $disph
set ::btnlist {}
foreach { name l t r b } $btns {
set l [= {round($l*$::dispw)}]
set t [= {round($t*$::disph)}]
set r [= {round($r*$::dispw)}]
set b [= {round($b*$::disph)}]
image create photo face$name
face$name copy btnup -from $l $t $r $b
set cmd [ list set ::curbtn $name ]
set code [ string range [ file extension $name ] 1 end ]
button $name -image face$name -command [list set ::curbtn $code] \
-bd 0 -highlightthickness 0 -activebackground #0000ff
place $name -x $l -y $t
raise $name
if { ($name ne ".top.clear") && ($name ne ".top.reset") } {
lappend ::btnlist $name
}
}
# printarea
foreach { l t r b } { 0.0852 0.127 0.67 0.29 } break
scrolledtextarea .top.printarea $l $t $r $b
# buffer
foreach { x y } { 0.0855 0.390 } break
frame .top.buffer -bd 2
place .top.buffer -x [= {round($x*$dispw)}] -y [= {round($y*$disph)}]
entry .top.buffer.entry -font $::txtfn -textvariable ::buffer -width $::bufwidth
pack .top.buffer.entry -fill both
# cardlist
foreach { x y } { .6813 .125 } break
scrolledlistbox .top.cardlist $::brwidth $::brheight "" loadcard $::brfn
place .top.cardlist -x [= {round($x*$dispw)}] -y [= {round($y*$disph)}]
# card reader
foreach { w h x y } { 0 0 .681 .2735 } break
label .top.cdrdr -image cr -anchor nw -bd 0
place .top.cdrdr -x [= {round($x*$dispw)}] -y [= {round($y*$disph)}]
# green light
foreach { w h x y } { .18 .06 .6825 .01 } break
if { $::size == 50 } { set w .18 }
label .top.green -bg $::dkgreen -anchor center -image 1xparentpx \
-width [= {round($dispw*$w)}] -height [= {round($disph*$h)}] -bd 10 \
-relief raised
place .top.green -x [= {round($x*$dispw)}] -y [= {round($y*$disph)}]
# red light
foreach { w h x y } { .07 .06 .9 .01 } break
label .top.red -bg $::dkred -anchor center -image 1xparentpx \
-width [= {round($dispw*$w)}] -height [= {round($disph*$h)}] -bd 10 \
-relief raised
place .top.red -x [= {round($x*$dispw)}] -y [= {round($y*$disph)}]
# decimal wheel
foreach { w h x y } { 2 1 .018 .8821 } break
spinbox .top.decset -from 0 -to 15 -wrap 1 -font $::spfn \
-width 2 -command setdec -textvariable numdecs
place .top.decset -x [= {round($x*$dispw)}] -y [= {round($y*$disph)}]
if $::needreset {
set ::needreset 0
buildregs
buildvwyz
buildprog
reset 1
} else {
loadprog
}
reloadlib
update
}
proc setsize { {size 0 } } {
if { $size == 0 } {
destroy .size
toplevel .size
label .size.msg -text "Simulator Display Size?"
grid .size.msg -row 0 -column 0 -columnspan 4
button .size.100 -text "100%" -command { setsize 100 }
grid .size.100 -row 1 -column 0
button .size.75 -text "75%" -command { setsize 75 }
grid .size.75 -row 1 -column 1
button .size.50 -text "50%" -command { setsize 50 }
grid .size.50 -row 1 -column 2
button .size.cancel -text "Exit" -command exit
grid .size.cancel -row 1 -column 3
center .size
after 1000 { wm deiconify .size }
} else {
destroy .size
set ::size $size
}
}
# initialize
package require Tk
wm withdraw .
#set runfrom .
set runfrom p101.exe
set manpdf $::runfrom/olpro101.pdf
interp alias {} = {} expr
encoding system utf-8
set dkgreen #179fa2
set brgreen #879fa2
set dkred #880000
set brred #ff0000
set btnlist {}
set program {}
set stack {}
set regstack {}
array set op2cmd {
\u2193 down
\u2191 up
\u2195 swap
\u221a sqrt
\u2014 minus
\u00d7 times
+ plus
\u00f7 divide
* zero
\u25c7 prreg
S start
/ split
@ runcard
( pushregs
) pullregs
}
array set cmd2op {
down \u2193
up \u2191
swap \u2195
sqrt \u221a
minus \u2014
times \u00d7
plus +
divide \u00f7
zero *
prreg \u25c7
start S
split /
runcard @
pushregs (
pullregs )
}
set uselog 1
set logopen 0
set logfile ""
set recording 0
set curpage 0
set animation 1
set interactive 1
set greenon 0
array set jumps {
" V" AV
" W" AW
" Y" AY
" Z" AZ
MV AV
MW AW
MY AY
MZ AZ
CW BW
CY BY
CZ BZ
DV EV
DW EW
DY EY
DZ EZ
RV FV
RW FW
RY FY
RZ FZ
/V aV
/W aW
/Y aY
/Z aZ
cV bV
cW bW
cY bY
cZ bZ
dV eV
dW eW
dY eY
dZ eZ
rV fV
rW fW
rY fY
rZ fZ
}
set immediate [ list recprog prprog prefs reset off clear start \
papersav paperclr showlabels showregs showprog pushregs pullregs \
proglib savecard about backsp ]
set havedec 0
set progshowing 0
set progshowing 0
set labelshowing 0
set regsshowing 0
set regssetup 0
set regsshowing 0
uptodate [info script] [file mtime [info script]]
set numdecs 4; setdec
set curbtn ""
trace add variable size write buildgui
set needreset 1
set library .
showsplash
after 2000
setsize
cmdloop