Index: AUTHORS.md ================================================================== --- AUTHORS.md +++ AUTHORS.md @@ -66,11 +66,11 @@ many releases since December 2016 include an update to the latest version of upstream SIMH. His contributions are made to the project [as `tony`][thcomm]. * **[Jonathan Trites](mailto:tritesnikov@gmail.com)** wrote the - initial version of the script now called `tools/mkos8.in`, which + initial version of the script now called `libexec/mkos8`, which builds the OS/8 disk images from source tapes. * **[Bill Cattey](mailto:bill.cattey@gmail.com)** is the project lead and primary developer of the system that builds the OS/8 RK05 disk images from source tapes. He greatly extended the `mkos8` script, Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -45,10 +45,26 @@ -I @srcdir@/src -I @srcdir@/src/PDP8 -I src SIM = bin/pidp8i-sim BINS = bin/palbart $(SIM) bin/pidp8i-test bin/ptp2txt libexec/scanswitch +MKOS8 = @srcdir@/libexec/mkos8 +MKOS8_LIB = lib/mkos8 +MKOS8_DIRS = $(MKOS8_LIB)/dirs.py +MKOS8_DIN = @srcdir@/$(MKOS8_DIRS).in +MKOS8_PY = \ + $(MKOS8_LIB)/__init__.py \ + $(MKOS8_LIB)/argparser/__init__.py + # The dirs.py one level up is generated by autosetup, so it is + # purposely not included here or in MKOS8_SRCS. It needs to be + # treated specially in the out-of-tree build case, and we don't want + # the OS/8 RK05 media to be rebuilt just because a *.in elsewhere + # was touched. +MKOS8_PYSRC= $(addprefix @srcdir@/,$(MKOS8_PY)) +MKOS8_BINS = $(MKOS8_PYSRC:.py=.pyc) +MKOS8_SRCS = $(MKOS8) $(MKOS8_DIN) $(MKOS8_PYSRC) + BUILDDIRS = bin libexec obj/PDP8 INSTDIRS = bin etc libexec share/boot share/media share/man/man1 OBJS = \ @@ -117,13 +133,15 @@ @srcdir@/boot/4.script.in \ @srcdir@/boot/6.script.in \ @srcdir@/boot/7.script.in \ @srcdir@/etc/pidp8i-init.in \ @srcdir@/etc/sudoers.in \ + @srcdir@/media/os8/init.tx.in \ @srcdir@/src/gpio-common.c.in \ @srcdir@/src/PDP8/pidp8i.c.in \ - @srcdir@/tools/simh-update.in + @srcdir@/tools/simh-update.in \ + $(MKOS8_DIN) PRECIOUS_INFILES = \ @srcdir@/Makefile.in \ @srcdir@/examples/Makefile.in \ @srcdir@/src/Makefile.in \ @srcdir@/src/PDP8/Makefile.in @@ -130,23 +148,29 @@ OUTFILES := $(subst @srcdir@/,,$(INFILES)) OUTFILES := $(subst .in,,$(OUTFILES)) PRECIOUS_OUTFILES := $(subst @srcdir@/,,$(PRECIOUS_INFILES)) PRECIOUS_OUTFILES := $(subst .in,,$(PRECIOUS_OUTFILES)) +OS8_BIN_RK05 = bin/os8v3d-bin.rk05 +OS8_SRC_RK05 = bin/os8v3d-src.rk05 +OS8_RK05S = $(OS8_BIN_RK05) $(OS8_SRC_RK05) + CLTXT = /boot/cmdline.txt .PHONY: tags .PRECIOUS: $(PRECIOUS_OUTFILES) -all: $(OUTFILES) $(PRECIOUS_OUTFILES) $(BUILDDIRS) $(BINS) $(BOOTSCRIPTS) $(LISTINGS) $(ASM_PTS) $(EX_PTS) +all: $(OUTFILES) $(PRECIOUS_OUTFILES) $(BUILDDIRS) $(BINS) $(BOOTSCRIPTS) $(LISTINGS) $(ASM_PTS) $(EX_PTS) $(OS8_RK05S) @chmod 755 bin/pidp8i clean: @rm -f $(BINS) $(BOOTSCRIPTS) $(ASM_PTS) $(EX_PTS) $(LISTINGS) $(OBJS) $(OUTFILES) \ + $(OS8_RK05S) bin/*.save \ tags \ obj/*.d \ obj/*.o \ + obj/*.log \ obj/PDP8/*.d \ @srcdir@/examples/*.err @-rmdir -p $(BUILDDIRS) 2> /dev/null || true distclean: clean @@ -154,11 +178,11 @@ config.log \ autosetup/jimsh0 \ src/config.h ctags tags: - @ctags -R @srcdir@ + @ctags -R @srcdir@ @srcdir@/libexec/mkos8 ifeq (@HAVE_PROG_CSCOPE@, 1) @cscope -bR -s@srcdir@ endif install: all @@ -225,33 +249,75 @@ @# Install palbart stuff @INSTALL@ -m 755 bin/palbart @prefix@/bin @INSTALL@ -m 644 @srcdir@/palbart/palbart.1 @prefix@/share/man/man1 + @# Install mkos8 and its dependencies + @INSTALL@ -m 775 -g @INSTGRP@ $(MKOS8) @prefix@/libexec + @( cd @srcdir@ ; \ + for f in $(MKOS8_PY) ; do \ + @INSTALL@ -m 644 -g @INSTGRP@ -D @srcdir@/$$f @prefix@/$$f ; \ + @INSTALL@ -m 644 -g @INSTGRP@ -D @srcdir@/$${f}c @prefix@/$${f}c ; \ + done \ + ) + @sed -e 's#^build =.*$$#build = "@ABSPREFIX@"#' \ + -e 's#^src =.*$$#src = "@ABSPREFIX@"#' \ + < $(MKOS8_DIRS) > @prefix@/$(MKOS8_DIRS) + @chgrp @INSTGRP@ @prefix@/$(MKOS8_DIRS) + mediainstall: @echo "[Re]installing OS and program media..." @cd @srcdir@ ; \ find media \( \ -name \*.bin -o \ -name \*.dsk -o \ -name \*.rk05 -o \ -name \*.tu56 \ - \) -exec @INSTALL@ -D -m 664 -g @INSTGRP@ {} @ABSPREFIX@/share/{} \; - @INSTALL@ -m 664 -g @INSTGRP@ boot/*.script @BOOTDIR@ + \) -exec @INSTALL@ -D -m 664 -o @INSTUSR@ -g @INSTGRP@ {} @ABSPREFIX@/share/{} \; + @INSTALL@ -m 644 -o @INSTUSR@ -g @INSTGRP@ bin/os8v3d-*.rk05 @ABSPREFIX@/share/media/os8 + @INSTALL@ -m 664 -o @INSTUSR@ -g @INSTGRP@ boot/*.script @BOOTDIR@ reconfig: @AUTOREMAKE@ release: all @srcdir@/tools/mkrel -run: +run: $(OS8_BIN_RK05) install $(SIM) @srcdir@/boot/0.script simh-update simh-update-f: @@srcdir@/tools/simh-update $(subst simh-update,,$@) + +# Build the OS/8 binary media needed by 0.script +OS8_BIN_SRCS = $(MKOS8_SRCS) \ + @srcdir@/media/os8/al-*-ba-*.tu56 \ + @srcdir@/media/os8/subsys/*.tu56 +$(OS8_BIN_RK05): $(OS8_BIN_SRCS) + @# We depend on the simulator to exist in order to build the OS/8 bin + @# media, but we don't care how new it is, so we test for it in shell + @# code here rather than declare it as a make dependency so that we + @# don't rebuild the OS/8 media every time the simulator is relinked. + @# In other words, any version of simulator will suffice. + @# + @# This does mean we're betting the simulator won't change so wildly + @# that we do somehow have to rebuild the bin media as a result, but + @# that's a pretty safe bet. If by some wild chance that ever does + @# occur, there's an easy fix: "make clean all". + @test -x $(SIM) || $(MAKE) $(SIM) + $(MKOS8)@MKOS8_OPTS@ bin + +# Also build an OS/8 source disk, as a convenience to avoid the +# need to mount up the 7 source tapes in succession. +OS8_SRC_SRCS = $(MKOS8_SRCS) \ + @srcdir@/media/os8/al-*-sa-*.tu56 +$(OS8_SRC_RK05): $(OS8_SRC_SRCS) $(OS8_BIN_RK05) + @# Same justification for these shell code tests as above. + @test -x $(SIM) || $(MAKE) $(SIM) + @test -e $(OS8_BIN_RK05) || $(MAKE) $(OS8_BIN_RK05) + $(MKOS8)@MKOS8_OPTS@ src # Rule for compiling *.c to *.o and autogenerating dependency info. # Explained at http://scottmcpeak.com/autodepend/autodepend.html # # Reflect any changes here into near-duplicate below! Index: README.md ================================================================== --- README.md +++ README.md @@ -26,10 +26,11 @@ * A working C compiler and other standard Linux build tools, such as `make(1)`. On Debian type systems — including Raspbian — you can install such tools with `sudo apt install build-essential` + ## Configuring, Building and Installing This software distribution builds and installs in the same way as most other Linux/Unix software these days. The short-and-sweet is: @@ -39,16 +40,18 @@ step fails, try redoing the `configure` step. Sometimes changes made to the source code invalidate prior `make` dependencies, which are implicitly repaired by the `configure` script. + ### Configure Script Options You can change a few things about the way the software is built and installed by giving options to the `configure` script: + #### --prefix Perhaps the most widely useful `configure` script option is `--prefix`, which lets you override the default installation directory, `/opt/pidp8i`. You could make it install the software under your home @@ -59,10 +62,76 @@ Although this is installing to a directory your user has write access to, you still need to install via `sudo` because the installation process does other things that do require `root` access. + +#### --lowercase + +The American Standards Association (predecessor to ANSI) delivered the +second major version of the ASCII character encoding standard the same +year the first PDP-8 came out, 1965. The big new addition? Lowercase. + +That bit of history means that when the PDP-8 was new, lowercase was a +fancy new feature in the computing industry. That, plus the memory +savings you get from storing [stripped ASCII][sa] as two 6-bit +characters per 12-bit PDP-8 word means that most PDP-8 software did not +expect to receive lowercase ASCII text, particularly the older software. + +The PDP-8 lived long enough to see lowercase ASCII input become common +in the computing industry. + +As a result, PDP-8 software reacts in many strange and wonderful ways +when you give it lowercase input. Some software copes nicely, others +crashes, and some software just sits there dumbly waiting for you to +type something! + +This configuration option lets you control how you want your simulated +PDP-8/I to react to lowercase input: + +* **auto** — The default is for the software to attempt to "do the + right thing." The simulator is configured to send lowercase input to + the PDP-8 software running on it. Where we have the skill, will, + need, and time for it, we have [patched][tty] some of the software + we distribute that otherwise would not do the right thing with + lowercase input to make it do so. + + This is *not* the option you want if you are a purist. + +* **upper** — This option tells the PDP-8 simulator to turn lowercase + input into upper case. This is the behavior we used for all versions + of the PiDP-8/I software up through v2017.04.04. Essentially, it + tells the software that you want it to behave as through you've got + it connected to a Teletype Model 33 ASR. + + The advantage of this mode is that you will have no problems running + PDP-8 software that does not understand lowercase ASCII text. + + The disadvantage is obvious: you won't be able to input lowercase + ASCII text. The SIMH option we enable in this mode is + bidirectional, so that if you run a program that does emit lowercase + ASCII text — such as Rick Murphy's version of Adventure — it will be + uppercased, just like an ASR-33 would do. + + Another trap here is that the C programming language requires + lowercase text, so you will get a warning if you leave the default + option **--enable-os8-cc8** set. Pass **--disable-os8-cc8** when + enabling **upper** mode. + +* **none** — This passes 7-bit ASCII text through to the software + running on the simulator unchanged, and no patches are applied to + the PDP-8 software we distribute. + + This is the option for historical purists. If you run into trouble + getting the software to work as you expect when built in this mode, + try enabling CAPS LOCK. + +[sa]: http://homepage.cs.uiowa.edu/~jones/pdp8/faqs/#charsets +[tty]: https://tangentsoft.com/pidp8i/wiki?name=OS/8+Console+TTY+Setup + + + #### --no-lamp-simulator If you build the software on a multi-core host, the PDP-8/I simulator is normally built with the [incandescent lamp simulator][ils] feature, which drives the LEDs in a way that mimics the incandescent lamps used @@ -79,10 +148,11 @@ method not only uses less CPU, which may be helpful if you're trying to run a lot of background tasks on your Pi 2 or Pi 3, it can also be helpful when the CPU is [heavily throttled][thro]. + #### --serial-mod If you have done [Oscar's serial mod][sm1] to your PiDP-8/I PCB and the Raspberry Pi you have connected to it, add `--serial-mod` to the `configure` command above. @@ -94,10 +164,11 @@ If you give this flag and your PCBs are *not* modified, most of the hardware will work correctly, but several lights and switches will not work correctly. + #### --alt-serial-mod This flag is for an [alternative serial mod by James L-W][sm2]. It doesn't require mods to the Pi, and the mods to the PiDP-8/I board are different from Oscar's. This flag changes the GPIO code to work with @@ -113,16 +184,124 @@ See [`README-throttle.md`][thro] for the values this option takes. If you don't give this option, the simulator runs as fast as possible, more or less. + + +#### --disable-os8-\* + +Several default components of the OS/8 RK05 disk image used by boot +options IF=0 and IF=7 can be left out to save space and build time: + +* **--disable-os8-advent** — Leave out the Adventure game. + +* **--disable-os8-ba** - Leave out the BASIC games and demos which + come from DEC's book "101 BASIC Computer Games." These are normally + installed to `RKB0:` as `*.BA`, thus the option's name. + + (We considered naming it `--disable-os8-basic-games-and-demos`, but + that's too long, and it can't be `--disable-os8-basic` because that + implies that it is the OS/8 BASIC subsystem that is being left out, + which is not even currently an option.) + +* **--disable-os8-chess** — Leave out John Comeau's CHECKMO-II chess + implementation. + +* **--disable-os8-cc8** - Leave out Ian Schofield's native OS/8 CC8 + compiler normally installed to `RKA0:` + +* **--disable-os8-crt** — Suppress the [console rubout behavior][tty] + enabled while building the OS/8 binary RK05 disk image. You + probably only want to do this if you have attached a real teletype + to your PiDP-8/I, and thus do not want video terminal style rubout + processing. + +* **--disable-os8-k12** - Leave out the Kermit-12 implementation + normally installed to `RKA0:` + +* **--disable-os8-uwfocal** - Leave out the U/W FOCAL V4E programming + environment normally installed to `RKA0:`. + + Note that the default installation only installs `UWF16K.SV`, not + the rest of the files on `media/os8/subsys/uwfocal*.tu56`. There is + much more to explore here, but we cannot include it in the default + installation set because that would overrun OS/8's limitation on the + number of files on a volume. + +* **--disable-os8-focal** - Do not install FOCAL on the OS/8 system + disk at all. This option acts as an alias for + `--disable-os8-uwfocal`, and it overrides `--enable-os8-focal69`, + discussed below. + + + +#### --enable-os8-\* + +There are a few file sets not normally installed to the OS/8 RK05 disk +image used by boot options IF=0 and IF=7. You can install them with the +following options: + +* **--enable-os8-music** — The `*.MU` files and the player program for + it are not normally installed to the built OS/8 binary RK05 disk + image because the Raspberry Pi reportedly does not emit eufficient + RFI at AM radio frequencies when running these programs to cause + audible music on a typical AM radio, the very point of these demos. + Until a way is found around this problem — what, low RFI is a + *problem* now? — this option will default to "off". + +* **--enable-os8-vtedit** — This option installs a default-run macro + pack called VTEDIT which causes the OS/8 version of TECO to run in + full-screen mode and to react to [several special keyboard + commands](/wiki?name=Using+VTEDIT) not normally recognized by TEDO. + + This feature is currently disabled because it is not yet fully + tested by the person in charge of the OS/8 disk building process. + + It may remain disabled after that because it changes the behavior of + the `TECO` command in OS/8, which violates the expectations of + people expecting a historically accurate TECO experience. On the + other hand, people don't go to a ren fair and expect to experience + the historical ubiquity of typhoid fever either, so we might change + our mind on this. + +* **--enable-os8-focal69** — Because the default installation includes + U/W FOCAL, we have chosen to leave FOCAL 69 out by default to save + space on the O/S 8 system disk. You can give this option to install + this implementation alongside U/W FOCAL, or you can couple this + option with `--disable-os8-uwfocal` to reverse our choice of which + FOCAL implementation to install by default. + + You should know that the reason we made this choice is that the + version of FOCAL 69 we are currently shipping is fairly minimal: we + believe we are shipping the original DEC version of FOCAL 69 plus a + few carefully-selected overlays. There are many more overlays and + patches available on the Internet for FOCAL 69, but we have not had + time to sort through these and make choices of which ones to ship or + how to manage which ones get installed. Thus our choice: we want to + provide the most functional version of FOCAL by default, and within + the limitations of the time we have chosen to spend on this, that is + U/W FOCAL today. + + (See our [U/W FOCAL manual supplement][suppd] for a list of + differences between these versions of FOCAL, which implicitly + explains why we chose it.) + + It is possible that we will eventually add enough patches and + overlays to FOCAL 69 that it will become more powerful than U/W + FOCAL, so we might then choose to switch the defaults, but that is + just speculation at the time of this writing. + +[suppd]: https://tangentsoft.com/pidp8i/doc/clean-os8-packs/doc/uwfocal-manual-supp.md#diffs + #### --help Run `./configure --help` for more information on your options here. + ## Overwriting the Local Simulator Setup When you run `sudo make install` step on a system that already has an existing installation, it purposely does not overwrite two classes of files: @@ -171,10 +350,11 @@ Beware that this is potentially destructive! If you've made changes to your PDP-8 operating systems or have saved files to your OS system disks, this option will overwrite those changes! + ## Testing You can test your PiDP-8/I LED and switch functions with these commands: $ sudo systemctl stop pidp8i @@ -192,10 +372,11 @@ $ sudo systemctl start pidp8i See [its documentation][test] for more details. + ## Using the Software For the most part, this software distribution works like the upstream [2015.12.15 distribution][usd]. Its [documentation][prj] therefore describes this software too, for the most part. Index: auto.def ================================================================== --- auto.def +++ auto.def @@ -33,35 +33,139 @@ use cc use cc-lib options { - debug-mode => "create a debug build (default is release)" - no-lamp-simulator => "use simple LED driver instead of incandescent lamp simulator" - serial-mod => "use GPIO drive scheme suitable for Oscar Vermeulen's serial mod method" - alt-serial-mod => "use GPIO drive scheme suitable for James L-W's serial mod method" - throttle: => "override the throttle values in the boot scripts" + alt-serial-mod => "use GPIO drive scheme suitable for James L-W's serial mod method" + debug-mode => "create a debug build (default is release)" + lowercase: => "select how lowercase input is to be handled" + no-lamp-simulator => "use simple LED driver instead of incandescent lamp simulator" + os8-advent=1 => "add the game of ADVENT to built OS/8 RK05 image" + os8-ba=1 => "leave *.BA BASIC games and demos off built OS/8 RK05 disk image" + os8-cc8=1 => "leave the native OS/8 CC8 compiler off the built OS/8 RK05 disk image" + os8-crt=1 => "set CRT-style rubout processing in built OS/8 KR05 image" + os8-chess=1 => "leave the CHECKMO-II game of chess off built OS/8 RK05 image" + os8-dcp=1 => "install DCP Disassembler executables to built OS/8 RK05 image" + os8-focal=1 => "leave FOCAL 69 and U/W FOCAL off the built OS/8 RK05 image" + os8-focal69 => "add FOCAL 69 to the built OS/8 RK05 image" + os8-init=1 => "suppress the OS/8 INIT message on boot: start with a bare . prompt" + os8-k12=1 => "leave 12-bit Kermit off built OS/8 RK05 disk image" + os8-music => "add *.MU files to built OS/8 RK05 disk image" + os8-uwfocal=1 => "leave U/W FOCAL (only) off the built OS/8 RK05 image" + os8-vtedit => "add the TECO VTEDIT setup to built OS/8 RK05 image" + serial-mod => "use GPIO drive scheme suitable for Oscar Vermeulen's serial mod method" + throttle: => "override the throttle values in the boot scripts" } if {[opt-bool alt-serial-mod]} { - msg-result "GPIO drive adjusted for James L-W's serial mods to the PiDP-8/I PCB." - define PCB_SERIAL_MOD_JLW - define PCB_SERIAL_MOD_ANY + msg-result "GPIO drive adjusted for James L-W's serial mods to the PiDP-8/I PCB." + define PCB_SERIAL_MOD_JLW + define PCB_SERIAL_MOD_ANY } if {[opt-bool serial-mod]} { - msg-result "GPIO drive adjusted for O. Vermeulen's serial mods to the Pi & PiDP-8/I PCBs." - define PCB_SERIAL_MOD_OV - define PCB_SERIAL_MOD_ANY + msg-result "GPIO drive adjusted for O. Vermeulen's serial mods to the Pi & PiDP-8/I PCBs." + define PCB_SERIAL_MOD_OV + define PCB_SERIAL_MOD_ANY } if {[opt-bool debug-mode]} { - msg-result "Creating a debuggable build." - define BUILDMODE {-O0 -g} + msg-result "Creating a debuggable build." + define BUILDMODE {-O0 -g} +} else { + msg-result "Creating a release build." + define BUILDMODE {-O2} +} + +set mkos8_opts "" + +set lv [opt-val lowercase] +if {$lv == ""} { set lv "auto" } +if {$lv == "auto"} { + define SIMH_PASS_LOWERCASE +} elseif {$lv == "pass"} { + define SIMH_PASS_LOWERCASE + append mkos8_opts " --disable-lcmod" +} elseif {$lv == "upper"} { + append mkos8_opts " --disable-lcmod" +} else { + user-error "Legal values for --lowercase are {auto,pass,upper}." +} +msg-result "Lowercase handling set to '$lv' mode." + +if {![opt-bool os8-advent]} { + msg-result "Will not add game of ADVENT to the OS/8 RK05 disk image." + append mkos8_opts " --disable-advent" +} +if {![opt-bool os8-ba]} { + msg-result "Will not add BASIC games and demos to the OS/8 RK05 disk image." + append mkos8_opts " --disable-ba" +} +if {![opt-bool os8-cc8]} { + msg-result "Will not add CC8 to the OS/8 RK05 disk image." + append mkos8_opts " --disable-cc8" +} +if {![opt-bool os8-chess]} { + msg-result "Will not add game of CHESS to the OS/8 RK05 disk image." + append mkos8_opts " --disable-chess" +} +if {![opt-bool os8-crt]} { + msg-result "Will not add BASIC games and demos to the OS/8 RK05 disk image." + append mkos8_opts " --disable-crt" +} +if {![opt-bool os8-dcp]} { + msg-result "Will not add DCP disassembler to the OS/8 RK05 disk image." + append mkos8_opts " --disable-dcp" +} + +if {![opt-bool os8-focal]} { + msg-result "Will not add FOCAL 69 or U/W FOCAL to the OS/8 RK05 disk image." + append mkos8_opts " --disable-focal" +} elseif {[opt-bool os8-focal69] && ![opt-bool os8-uwfocal]} { + msg-result "Adding FOCAL 69 to the OS/8 RK05 disk image instead of U/W FOCAL." + append mkos8_opts " --enable-focal69 --disable-uwfocal" +} elseif {[opt-bool os8-focal69] && [opt-bool os8-uwfocal]} { + msg-result "Adding both FOCAL 69 and U/W FOCAL to the OS/8 RK05 disk image." + append mkos8_opts " --enable-focal69" ;# default --enable-uwfocal +} else { + msg-result "Adding U/W FOCAL to the OS/8 RK05 disk image instead of FOCAL 69." +} + +if {![opt-bool os8-init]} { + msg-result "Suppressing the INIT message on OS/8 boot." + append mkos8_opts " --disable-init" +} +if {![opt-bool os8-k12]} { + msg-result "Will not add Kermit-12 to the OS/8 RK05 disk image." + append mkos8_opts " --disable-k12" +} +if {[opt-bool os8-music]} { + msg-result "Will add music files to the OS/8 RK05 disk image." + append mkos8_opts " --enable-music" } else { - msg-result "Creating a release build." - define BUILDMODE {-O2} + msg-result "Will not add music files to the OS/8 RK05 disk image." +} +if {[opt-bool os8-vtedit]} { + msg-result "Will add VTEDIT setup to the OS/8 RK05 disk image." + append mkos8_opts " --enable-vtedit" +} +define MKOS8_OPTS $mkos8_opts + +# Force a rebuild of the OS/8 media if the option set changed. +if {![file exists "obj"]} { file mkdir "obj" } +set ofile "obj/mkos8.opts" +set old_mkos8_opts "IMPOSSIBLE" +if {[file exists $ofile]} { + set fp [open $ofile r] + gets $fp old_mkos8_opts + close $fp +} +if {$old_mkos8_opts != $mkos8_opts} { + file delete -force bin/os8v3d-bin.rk05 + set fp [open $ofile w] + puts $fp $mkos8_opts + close $fp } # High-level definitions set builddir [get-define builddir] set srcdir [get-define srcdir] @@ -69,66 +173,66 @@ # Translate --throttle value to a SIMH command set tv [opt-val throttle] set tvsl [string length $tv] if {($tvsl == 0 && $cores > 1) || $tv == "none"} { - define SET_THROTTLE {set nothrottle} - set tv "unlimited" + define SET_THROTTLE {set nothrottle} + set tv "unlimited" } else { - # Rewrite symbolic values with values SIMH can understand. See - # README-throttle.md for the justification of these values. - if {$tv == "single-core" || $tvsl == 0} { - # It's a single-core Pi board, so just tell SIMH to take half + # Rewrite symbolic values with values SIMH can understand. See + # README-throttle.md for the justification of these values. + if {$tv == "single-core" || $tvsl == 0} { + # It's a single-core Pi board, so just tell SIMH to take half # the host CPU power, leaving the rest left for background # tasks. We can't use an IPS value here for several reasons. # See README-throttle.md for details. set tv "50%" - } elseif {$tv == "pdp8e"} { - set tv "416k" - } elseif {$tv == "pdp8i" || $tv == "pdp8a"} { - set tv "333k" - } elseif {$tv == "pdp8l" || $tv == "pdp8"} { - set tv "313k" - } elseif {$tv == "ha6120"} { - set tv "182k" - } elseif {$tv == "im6100a"} { - set tv "200k" - } elseif {$tv == "im6100"} { - set tv "100k" - } elseif {$tv == "im6100c"} { - set tv "83k" - } elseif {$tv == "pdp8s"} { - set tv "63k" - } elseif {$tv == "human"} { - set tv "1/100" - } elseif {$tv == "trace"} { - set tv "1/1000" - } - # else, assume --throttle was given a legal SIMH throttle value + } elseif {$tv == "pdp8e"} { + set tv "416k" + } elseif {$tv == "pdp8i" || $tv == "pdp8a"} { + set tv "333k" + } elseif {$tv == "pdp8l" || $tv == "pdp8"} { + set tv "313k" + } elseif {$tv == "ha6120"} { + set tv "182k" + } elseif {$tv == "im6100a"} { + set tv "200k" + } elseif {$tv == "im6100"} { + set tv "100k" + } elseif {$tv == "im6100c"} { + set tv "83k" + } elseif {$tv == "pdp8s"} { + set tv "63k" + } elseif {$tv == "human"} { + set tv "1/100" + } elseif {$tv == "trace"} { + set tv "1/1000" + } + # else, assume --throttle was given a legal SIMH throttle value if {[string first "/" $tv] > -1} { # Assume the ratio given will push us below 1 kIPS, where ILS # fails badly because of the simulator's sleeping behavior, so # disable the ILS feature if we were going to build it. set cores 1 } - define SET_THROTTLE "set throttle $tv" + define SET_THROTTLE "set throttle $tv" } msg-result "Simulator CPU throttle set to $tv" # Swap the incandescent lamp simulator feature out for the original LED # driving method on single-core hosts. The user can force this on # multi-core hosts via --no-lamp-simulator. if {($cores < 2) || [opt-bool no-lamp-simulator]} { - msg-result "Driving PiDP-8/I front panel LEDs using low-CPU-usage method." - define LED_DRIVER_MODULE n - define ILS_MODE 0 + msg-result "Driving PiDP-8/I front panel LEDs using low-CPU-usage method." + define LED_DRIVER_MODULE n + define ILS_MODE 0 } else { - msg-result "Driving PiDP-8/I front panel LEDs using incandescent lamp simulator." - define LED_DRIVER_MODULE i - define ILS_MODE 1 + msg-result "Driving PiDP-8/I front panel LEDs using incandescent lamp simulator." + define LED_DRIVER_MODULE i + define ILS_MODE 1 } # Check for headers, functions, etc. whose absence we can work around cc-check-includes time.h cc-check-function-in-lib clock_gettime rt @@ -136,31 +240,31 @@ cc-check-functions sched_yield # Ensure we have the libncurses development files installed here, else # pidp8i-test won't build. if {![cc-check-includes curses.h]} { - user-error "Could not find curses.h. Try installing libncurses-dev." + user-error "Could not find curses.h. Try installing libncurses-dev." } elseif {![cc-check-function-in-lib initscr ncurses]} { - user-error "Could not find initscr() in libncurses." + user-error "Could not find initscr() in libncurses." } # We need to find an install(1) type program that supports -D. The # Raspberry Pi OSes typically used with the PiDB-8/I board do have this, # but this package also runs on non-Linux OSes (e.g. for testing on a # desktop Mac) so make sure we've got a suitable implementation. The # ginstall name is typical on non-Linux systems where GNU Coreutils was # installed alongside the core OS utilities. if {[cc-check-progs ginstall]} { - define INSTALL ginstall + define INSTALL ginstall } elseif {[cc-check-progs install]} { - if {[catch {exec install -D -d . >& /dev/null} result] == 0} { - define INSTALL install - } else { - user-error "install(1) does not support -D; install GNU Coreutils." - } + if {[catch {exec install -D -d . >& /dev/null} result] == 0} { + define INSTALL install + } else { + user-error "install(1) does not support -D; install GNU Coreutils." + } } else { - user-error "No install(1) type program found; install GNU Coreutils." + user-error "No install(1) type program found; install GNU Coreutils." } msg-result "Found GNU install(1) program as [get-define INSTALL]." # If we have cscope here, we'll use it in the "tags" target define HAVE_PROG_CSCOPE [cc-check-progs cscope] @@ -174,12 +278,12 @@ # we want to give that group write privileges to some files when they're # installed, and we want them to own the screen(1) session. set instgrp [exec id -grn] set instusr [exec id -urn] if {$instusr == "root"} { - msg-result "Error: This software will not install and run as root." - user-error "Reconfigure without sudo!" + msg-result "Error: This software will not install and run as root." + user-error "Reconfigure without sudo!" } define INSTGRP $instgrp define INSTUSR $instusr msg-result "Install group for user-writeable files will be $instgrp." msg-result "Owner of screen(1) session will be $instusr." @@ -190,18 +294,18 @@ # then will yell about if you try to use them. The test checks for # an -f sub-option that Clang doesn't currently support even enough # to fool autosetup. cc-check-standards c99 if {![opt-bool debug-mode]} { - cc-check-flags -fipa-cp-clone - cc-check-flags -fno-strict-overflow - cc-check-flags -fpredictive-commoning - if ([get-define HAVE_CFLAG_FIPA_CP_CLONE]) { - cc-check-flags -fgcse-after-reload - cc-check-flags -finline-functions - cc-check-flags -fno-unsafe-loop-optimizations - } + cc-check-flags -fipa-cp-clone + cc-check-flags -fno-strict-overflow + cc-check-flags -fpredictive-commoning + if ([get-define HAVE_CFLAG_FIPA_CP_CLONE]) { + cc-check-flags -fgcse-after-reload + cc-check-flags -finline-functions + cc-check-flags -fno-unsafe-loop-optimizations + } } # Embed this software's Fossil-based version string into gpio-common.c. # Fail hard if we can't get this version string because all supported # configurations require Fossil and work from a Fossil checkout. Don't @@ -224,24 +328,59 @@ user-error "$tool failed: $path does not exist." } } define VERSION $version +# Get host, user, and date info for use by media/os8/init.tx. +set host [info hostname] +set user $::env(USER) +define BUILDUSER "$user@$host" +define BUILDDATE [clock format [clock seconds] -format "%Y.%m.%d %T %Z"] + +# The mkos8 script requires Python and some non-core modules. +set status [catch {exec python -c exit} result] +if {$status != 0} { + user-error "Python does not appear to be installed here. It is required." +} +msg-result "Python is installed here." +set status [catch {exec python -c "import pexpect" 2> /dev/null} result] +if {$status != 0} { + set msg "The Python pexpect module is not installed here. Fix with\n" + append msg "\n sudo pip install pexpect\n" + append msg "\nOR:\n" + append msg "\n sudo easy_install pexpect\n" + append msg "\nOR:\n" + append msg "\n sudo apt install python-pexpect\n" + user-error $msg +} +msg-result "Python module pexpect is installed here." +set status [catch {exec python -c "import pkg_resources" 2> /dev/null} result] +if {$status != 0} { + set msg "The Python pkg_resources module is not installed here. Fix with\n" + append msg "\n sudo pip install pkg_resources\n" + append msg "\nOR:\n" + append msg "\n sudo easy_install pkg_resources\n" + append msg "\nOR:\n" + append msg "\n sudo apt install python-pkg-resources\n" + user-error $msg +} +msg-result "Python module pkg_resources is installed here." + # Build Deeper Thought if we find it here if {[file exists "[get-define srcdir]/src/deeper.c"]} { set ls [string toupper "[get-define LED_DRIVER_MODULE]ls"] - msg-result "Found Deeper Thought; building it against $ls GPIO module" + msg-result "Found Deeper Thought; building it against $ls GPIO module" define BUILD_DEEPER_THOUGHT 1 } # Write outputs. # # NOTE: If you change the list of files here, change INFILES in # Makefile.in, too. make-config-header src/config.h \ - -auto {ENABLE_* HAVE_* PACKAGE_* SIZEOF_*} \ - -bare {ILS_MODE PCB_*} + -auto {ENABLE_* HAVE_* PACKAGE_* SIZEOF_*} \ + -bare {ILS_MODE PCB_*} make-template Makefile.in make-template bin/pidp8i.in make-template boot/0.script.in make-template boot/2.script.in make-template boot/3.script.in @@ -249,11 +388,13 @@ make-template boot/6.script.in make-template boot/7.script.in make-template etc/pidp8i-init.in make-template etc/sudoers.in make-template examples/Makefile.in +make-template lib/mkos8/dirs.py.in +make-template media/os8/init.tx.in make-template src/Makefile.in make-template src/gpio-common.c.in make-template src/PDP8/Makefile.in make-template src/PDP8/pidp8i.c.in make-template tools/simh-update.in exec chmod +x "$builddir/tools/simh-update" Index: boot/0.script.in ================================================================== --- boot/0.script.in +++ boot/0.script.in @@ -1,7 +1,7 @@ ; This script initializes a populated OS/8 environment on an -; RK05 cartridge disk pack. That's 10 whole megabytes, so big +; RK05 cartridge disk pack. That's 2.5 whole megabytes, so big ; the OS requires that you split it into two partitions in order ; to address the whole disk! Thus the "RKB0:" references you ; will find in tutorials, as that refers to the second half ("B") ; of the first ("0") RK05 cartridge disk. The default location ; the OS uses is formally called "RKA0:", alias "SYS:". @@ -25,7 +25,21 @@ echo Loading OS/8 from the RK05 cartridge disk... set cpu 32k set cpu noidle set df disabled @SET_THROTTLE@ -att rk0 @MEDIADIR@/os8/os8.rk05 + +@if SIMH_PASS_LOWERCASE +; The software was configured with either --lowercase=auto or =pass, +; so send all text input to the simulator as 7-bit ASCII, including +; lowercase. Lowercase output from the simulator will be sent to the +; console unchanged, as will non-printing chars. +set tti 7b +@else +; The software was configured with --lowercase=upper, meaning the user +; wants lowercase text to be forced to uppercase. This is bidirectional, +; affecting both input to the simulated PDP-8 and output from it. +set tti ksr +@endif + +att rk0 @MEDIADIR@/os8/os8v3d-bin.rk05 boot rk0 ADDED doc/dcp_wu.md Index: doc/dcp_wu.md ================================================================== --- /dev/null +++ doc/dcp_wu.md @@ -0,0 +1,480 @@ +# DCP Disassembler for PDP-8 + +This document is based on the file DCP.WU found with DCP binaries. + +| Author | A.E. Brouwer, Math. Center, Amsterdam | +| Date | 73-10-03 | +| Version Number | DCP AB-V21 | +| Last Update | 74-11-12 | +| Environment | OS/8 operating system | +| Memory Requirements | 16K, Optional 24K mode | + + +## DCP (Preliminary Description) + +DCP (sometimes called `deass`) is a program to deassemble (or disassemble) +a PAL program given in binary or in core image format as 1st input file. +Information about the program and meaningful tags can be given +in a second input file. A well readable listing with meaningful +tags but without comment can be obtained in a few passes +(typically four). The first time no information is supplied; while +reading the output one recognizes certain parts as messages +("NO ROOM FOR OUTPUT") or numeric tables (6030,7634,7766,7777) +or simple subroutines (TTYOUT, PUSH, PRINT). + +Putting these things in an information file and then running +dcp again gives you a much nicer output the second time. +Now you may embark on the program itself and obtain after a small +number of passes (depending on the complexity of the program and +your laziness.) A source that might have been the original one except +for its lack of comment. At this moment you could profitably use +the CTRL/E feature of MCEDIT to provide the whole source of comment. +(For example, we obtained a source of a fortran compiler in three +days after five passes.) + +Below we will describe the OS/8 version of the program + +## Assembly Instructions + +(Alas, we do not yet have source.) + + .R PAL8 + *102,DCP_SBIN,DCPZ/L$ + .SAVE SYS DCP + +## Operating Instructions + + .R DCP + *OUTPUTDSK:DCPLS.TM + If command closed with altmode then exit to OS/8 monitor + else call command decoder again. + +2. If no output given but an output file is required because + chaining to CREF.SV is requested then DSK:DCPLS.TM is used. + +3. If no input given then use output filename with extensions + .SV and .SM (if present.) + E.G. + + *DEASS< + + is equivalent to + + *DEASS3777 the command has to be closed with altmode instead of return.) + +## Translation Is Done One Field at A Time + +Therefore, binaries that load into extended memory must be +disassembled with /(F) for all memory fields used. + +This causes some flaws in the output: + + CIF 10 + JMS I (200 + +is translated as: + + CIF 10 + JMS I (START + +If LOC 200 in the current field is labeled START. +Note that assembling the produced source gives the +correct binary.) + +## Input Format + +Each input section starts with $X (where X is a letter indicating +the type of the section) and ends with $ . + +$\ indicates the end of all input (when not within a secion). +Between the sections comment not containing $ may be inserted. + +### Section Types + +| $A | Translate as 6bit ASCII (TEXT "STRING") +| $D | Dont translate +| $I | Translate as instruction (overriding other specs) +| $L | Translate as identifier rather than as instruction +| $N | Translate octal +| $S | Subroutine with args +| $T | Symbol definitions +| $Z | Special coding +| $ | End of input + +### Content of Section + +1. Sections $X where X is A,D,I,L or N. + + Contents: Lines of the form: + + MMMM-NNNN + + or + + NNNN + + Where NNNN and MMMM are octal addresses. + + e.g. the section: + + $N + 1717-1730 + 1750 + $ + + specifies that the locations 1717-1730 and 1750 are + to be translated as octal numbers. + +2. Sections $S. + + Contents: Lines of the form: + + SSSS:XXXXX + + Where SSSS is a subroutine address and XXXXX specifies + the kind of arguments the subroutine has. + + e.g. the section: + + $S + 1000:NL + $ + + indicates that each call to the subroutine at LOC 1000 has two + arguments of type octal and label respectively. + +3. Sections $T. + + Contents: Lines of the form: + + TAG=NNNN + + or + + TAG + + Meaning: If no octal value of a tag is specified then its value is + taken as one more than the value of the previous tag. + +4. Section $Z. + + This is an ad hoc construct to enable the translation of + symbol tables like those of PAL8 and CREF. + + e.g. + + $Z=52;0=240;1=301;40=260 + NNNN-MMMM:(UUUL) + $ + + indicates that the range NNNN-MMMM is a table of four-word entries + three words in a special format and one label. + + The special format is as follows: + + The value is divided by 52 giving a quotient and a remainder. + Both are converted into a character as follows: 0 gives a space, + 1-37 give letters A-_, and 40-51 give digits 0-9. + + The coding here is not foolproof yet: A strange command might + give strange output instead of an error message. + + In later versions this command will be generalized, so we don't + describe it in full here. + +## Error Messages + +These are very poor (because of lack of space): HLTNNNN, +where NNNN indicates the address of the routine in DCP that +detected the error. + +Errors are almost always violations of the input format. + +A complete list will appear in the final report. + +| Name | DCP (ERROR TABLE) +| Author | A.E. Brouwer +| Date | 75-02-13 + +As noted: The error messages of DCP look like 'HLT....' +where .... stands for the octal address of the routine +that detected the error. + +(Of course giving intelligible messages is highly desirable +but lack of space prevented this. Some future version of DCP +will chain to a file `DECPERR.SV` containing the messages.) + +Below the error numbers are given for DCP AB-V21. +[Note: These numbers may change slightly each time that +DCP is assembled anew.] + +### DCP16 Error Table + +| Number | ERROR +| ------ | -------------------------------------------------------------| +| 0000 | PREMATURE END OF .BN INPUT | +| 0230 | CLOSE ERROR | +| 0301 | LOOKUP FOR SYS:CREV.SV FAILED | +| 1414 | OUTPUT ERROR OR NO ROOM FOR OUTPUT | +| 1451 | INPUT ERROR (INFO FILE) | +| 1522 | NO CARRIAGE RETURN WHERE EXPECTED IN THE INFO FILE | +| 1755 | UPPER BOUND IN BOUND PAIR LESS THAN LOWER BOUND | +| 2031 | ASCII STRING CONTAINED A SIXBIT ZERO, BUT NOT AT THE END | +| | (I.E. A WORD 00XX). (THIS MIGHT HAVE BEEN AN @, | +| | BUT IS USUALLY AN ERROR.) | +| 2046 | ASCII STRING WITHOUT TRAILING ZERO | +| 2061 | DCP COULD NOT FIND A SUITABLE DELIMITER FOR THE ASCII STRING | +| | IN THE RANGE "" TO "? | +| 2125 | IMPOSSIBLE | +| 2214 | TEXT BUFFER OVERFLOW (TOO MANY OR TOO LONG IDENTIFIERS). | +| 2234 | NO IDENTIFIER WHERE EXPECTED (IN A $T SECTION). | +| 2666 | ZERO SUBROUTINE ADDRESS SPECIFIED IN A $S SECTION | +| 2705 | S-BUFFER OVERFLOW (TOO MANY SUBROUTINES WITH ARGS.) | +| 2761 | UNKNOWN TYPE LETTER IN SPECIFICATION OF SUBROUTINE ARGS | +| 3006 | $Z NO FOLLOWED BY = | +| 3011 | $Z= NOT FOLLOWED BY A NONZERO NUMBER | +| 3022 | NO CARRIAGE RETURN OR SEMICOLON WHERE EXPECTED IN $Z HEADER | +| 3030 | NO = WHERE EXPECTED IN $Z HEADER LINE | +| 3041 | ZERO LOWER BOUND IN BOUND PAIR IN $Z SECTION | +| 3064 | Z-BUFFER OVERFLOW | +| 3117 | PREMATURELY EXHAUSTED Z-FORMAT | +| 3135 | UNKNOWN Z-FORMAT SYMBOL | +| 3470 | T-BUFFER OVERFLOW | +| 3723 | NO VALUE ASSIGNED TO FIRST TAG IN $T SECTION | +| 4213 | NO INPUT AND NO OUTPUT AND NO DSK:DCPLS.TM TO DELETE | +| 4245 | HANDLER FETCH ERROR | +| 4341 | LOOKUP FOR INPUTFILE FAILED | +| 4442 | OUTPUT OPEN ERROR | +| 4456 | NO 16K MEMORY AVAILABLE | +| 4470 | CHECKSUM OR FORMAT ERROR IN BINARY INPUT FILE | +| 4613 | FORMAT ERROR IN CORE CONTROL BLOCK OF .SV INPUT FILE | +| 4647 | ERROR READING CORE CONTROL BLOCK OF .SV INPUT | +| 4723 | ERROR READING .SV INPUT FILE | + +## DCP24 + +DCP Version 24 is a 24K version of DCP. + +| Name | DCP-AB-WW-V24 +| Author | W.F. Wakker, Math. Center, Amsterdam +| Date | 76-03-25 + +### The Following Extensions Are Made + +- DCP24 Translates EAE instructions in both A and B mode + + (For mode switching, see below.) + + Example: + + 1200 DAD;1234 + + is translated as if the info-file contains the following info: + + $I + 1200 + $ + $L + 1201 + $ + $N + 1234+ + $ + +- In the info-file one can give : NNNN+ which has the same + effect as NNNN-MMMM where MMMM=NNNN+1. + +- Several buffers have been enlarged. + +- The output is paginated and has a heading on each page. + (The page number is in octal ....) + +- Error messages are unfortunately as poor as before (See DCP24 + error table). + +- New sections, now possible in the info-file are: + + | $B | TRANSLATE AS 8-BIT ASCII | + | $C | GIVE COMMENT | + | $E | FORCE EAE MODE A | + | $F | FORCE EAE MODE B | + | $M | TRANSLATE NEGATIVE | + +- Section $B + + $B + NNNN-MMMM + $ + + Causes the location NNNN-MMMM to be translated as + 8-bit ASCII, E.G. 0301 is translated as "A. + Values less then 241 are translated as octal numbers. + +- Sections $E and $F + + When DCP encounters EAE instructions, some slight heuristics + are done to determine the mode. The mode is initially A; SWAB, + DAD, and DST cause the mode to change to mode B etc. + When these heuristics are too poor, you can use the $E section + to force mode A and the $F section to force mode B. + +- Section $M + + This section has the same effect as section $N, only all + octals are given negative, E.G. 7770 becomes -10. + It is also possible to give $B and $M to the same LOC. + Example: 7477 is now translated as -"A. + +- Section $C + + Now you can give comment!! + + | Format | NNNN:THIS IS COMMENT + | Effect | NNNN ........ /THIS IS COMMENT + | Attention | The $C section must be the last one in the info-file: + + When $C is seen in the info-file, a setup is made to + give the comment and no more input will be read ( E.G. The program + acts like $$ on the end is seen). The comments are added to + the listing in the last pass of the program. + + __YOU MUST SORT THE ADDRESSES.__ + + 300:COMM1 + 200:COMM2 + + Has as effect that from adress 300 on, no more comment will + be given, since address 200 is not found any more. + + __DO NOT GIVE COMMENT ON ADDRESSES WHICH DO NOT BELONG + TO THE PROGRAM__ + +- Extension of $S section + + As arguments in the $S section you can give N, L, A, I, B, M, + (with the obvious meaning, see above ) and also U. + U should only be used for the addresses 200 and 7700. + It marks the entrypoint of the user service routine and gives + a nice translation of each USR call. + +- Extension of $Z section + + New possible arguments: M, B. + +### DCP-V24 (ERROR TABLE) + +| Number | ERROR +| ------ | -------------------------------------------------------------| +| 0000 | PREMATURE END OF .BN INPUT | +| 0237 | CLOSE ERROR | +| 0305 | LOOKUP FOR SYS:CREF.SV FAILED | +| 1414 | OUTPUT ERROR OR NO ROOM FOR OUTPUT | +| 1511 | NO CARRIAGE RETURN WHERE EXPECTED IN THE INFO FILE | +| 1707 | NO : AS SEPARATOR IN $C SECTION | +| 2145 | UPPER BOUND IN BOUND PAIR LESS THAN LOWER BOUND | +| 2235 | NO : AS SEPARATOR IN FIRST LINE OF $C SECTION | +| 2331 | INPUT ERROR (INFO FILE) | +| 2431 | ASCII STRING CONTAINED A SIXBIT ZERO, BUT NO AT THE END | +| | (I.E. A WORD 00XX). (THIS MIGHT HAVE BEEN AN @, | +| | BUT IS USUALLY AN ERROR.) | +| 2446 | ASCII STRING WITHOUT TRAILING ZERO | +| 2461 | DCP COULD NOT FIND A SUITABLE DELIMITER FOR THE ASCII STRING | +| | IN THE RANGE "" TO "? | +| 2525 | IMPOSSIBLE | +| 2614 | TEXT BUFFER OVERFLOW (TOO MANY OR TOO LONG IDENTIFIERS) | +| 2634 | NO IDENTIFIER WHERE EXPECTED (IN A $T SECTION) | +| 3266 | ZERO SUBROUTINE ADDRESS SPECIFIED IN A $S SECTION | +| 3305 | S-BUFFER OVERFLOW (TOO MANY SUBROUTINES WITH ARGS) | +| 3367 | UNKNOWN TYPE LETTER IN SPECIFICATION OF SUBROUTINE ARGS | +| 3406 | $Z NOT FOLLOWED BY = | +| 3411 | $Z= NOT FOLLOWED BY A NONZERO NUMBER | +| 3422 | NO CARRIAGE RETURN OR SEMICOLON WHERE EXPECTED IN $Z HEADER | +| 3430 | NO = WHERE EXPECTED IN $Z HEADER LINE | +| 3441 | ZERO LOWER BOUND IN BOUND PAIR IN $Z SECTION | +| 3463 | Z-BUFFER OVERFLOW | +| 3517 | PREMATURELY EXHAUSTED Z-FORMAT | +| 3541 | UNKNOWN Z-FORMAT SYMBOL | +| 4070 | T-BUFFER OVERFLOW | +| 4324 | NO VALUE ASSIGNED TO FIRST TAG IN $T SECTION | ADDED doc/uwfocal-manual-supp.md Index: doc/uwfocal-manual-supp.md ================================================================== --- /dev/null +++ doc/uwfocal-manual-supp.md @@ -0,0 +1,760 @@ +# U/W FOCAL Manual Supplement for the PiDP-8/I + +The [U/W FOCAL Manual][uwfm] is well written as far as it goes, but +there are gaps: + +1. It inspires questions in the reader's mind without providing an + answer. While that is actually a hallmark of a good book, the U/W + FOCAL manual sometimes does it for topics that are properly within + its scope and so should be answered within. + +1. It omits coverage for some topics we wish it would cover, though + they are not properly within its scope. + +1. It is written somewhat generically for the whole PDP-8 family as of + late 1978, whereas the PiDP-8/I project is focused on a single model + from 1968. + +This document is [our](#license) attempt to fill these gaps. +[Extensions and corrections][hack] are welcome. + +You might also find the [DECUS submission for U/W FOCAL][duwf] and the +[U/W FOCAL reference cards][uwfr] helpful. + +[duwf]: http://www.pdp8.net/pdp8cgi/query_docs/view.pl?id=191 +[hack]: https://tangentsoft.com/pidp8i/doc/trunk/HACKERS.md +[uwfm]: https://tangentsoft.com/pidp8i/doc/clean-os8-packs/doc/uwfocal-manual.md +[uwfr]: https://tangentsoft.com/pidp8i/doc/clean-os8-packs/doc/uwfocal-refcards.md + + +## Starting and Stopping U/W FOCAL + +The section "Starting the Program" in the [U/W FOCAL Manual][uwfm] is +entirely concerned with loading U/W FOCAL from paper tape using the +front panel and the BIN loader. + +The PiDP-8/I software project does not currently ship U/W FOCAL in SIMH +paper tape image form. Instead, it's installed by default on the OS/8 +system disk, which greatly simplifies starting it: + + .R UWF16K + +Yes, that's all. You're welcome. `:)` + +To get back to OS/8, just hit Ctrl-C. + + +## Loading and Saving Programs + +There are many ways to get program text into U/W FOCAL other than simply +typing it in. This section gives several methods, because each may be of +use to you in different circumstances. Some of them may not be of direct +use to you, but may open your eyes to techniques that may be useful to +you in other contexts, so we encourage you to read this entire section. + + +### Pasting Text in from a Terminal Emulator: The Naïve Way + +If you are SSHing into your PiDP-8/I, you might think to write your +FOCAL programs in your favorite text editor on your client PC then copy +and paste that text into U/W FOCAL over SSH. Currently, that won't work. +(2017.10.05) We believe it is because of the way U/W FOCAL handles +terminal I/O and interrupts. If you try, the input ends up trashed in +FOCAL. + + +### Pasting Text in from a Terminal Emulator: The Way That Works + +"But I really really want to write my FOCAL programs in [my favorite +text editor][mfte] and paste them into my PiDP-8/I," I hear you say. +Dispair not. There is a path. Follow. + +The problem affecting U/W FOCAL which prevents it from handling input at +modern paste-through-SSH speeds doesn't affect OS/8 itself, so we'll use +it as an intermediary: + + .R PIP + *HELLO.DAThe `PUNCH` Command + +When the [U/W FOCAL Manual][uwfm] talks about loading and saving +programs, it is in terms of the `PUNCH` command, because the manual is +focused on the paper tape based version of U/W FOCAL. + +The PiDP-8/I software project ships the OS/8 version of U/W FOCAL +instead, which doesn't even have a `PUNCH` command. (It appears to have +been replaced by the `PLOT` command, mentioned on [`CARD1.DA`][uwfr], +but that's of no use to us here, since SIMH doesn't support pen +plotters. (Yet.)) + +Even if it did work, mounting and unmounting simulated paper tapes under +SIMH is a bit of a hassle. We can do better. + + +### The `LIBRARY` Command + +The effective replacement for `PUNCH` in the OS/8 version of U/W FOCAL +is the `LIBRARY` command. + +If you've read [the manual][uwfm], you may be wondering if it's +overloaded with `LINK` and `LOOK`, but no: those commands are apparently +missing from the OS/8 version. (Open question: how do you use multiple +fields of core for program code with the OS/8 version, then?) + +Briefly, then, I'll show how to use some of these commands: + + .R UWF16K ⇠ start fresh + *1.10 TYPE "Hello, world!"! ⇠ input a simple one-line program + *L S HELLO ⇠ write program to disk with LIBRARY SAVE + *L O HELLO ⇠ verify that it's really there + HELLO .FD 1 ⇠ yup, there it is! + *E ⇠ ERASE all our hard work so far + *W ⇠ is it gone? + C U/W-FOCAL: 16K-V4 NO/DA/TE ⇠ goneski + *L C HELLO ⇠ load it back in with LIBRARY CALL + *W ⇠ did it come back? + C U/W-FOCAL: HELLO NO/DA/TE + + 01.10 TYPE "Hello, world!"! ⇠ yay, there it is! + *L D HELLO ⇠ nuke it on disk; it's the only way to + *L O HELLO ⇠ ...be sure + * ⇠ Houston, we have no program + +See the [DECUS submission][duwf] and `CARD2.DA` in the [refcards][uwfr] +for more examples. + + +### The `WRITE` Command + +One problem with using U/W FOCAL's `LIBRARY` command for this is that +it saves programs as core images, which are a) non-relocatable; and b) +non-portable to other versions of FOCAL. We can fix both of these +programs by saving the program to an ASCII text file instead. + +With a program already typed in or loaded from disk: + + *O O HELLO; W; O C + +All of that has to be on a single line, with the semicolons. (See +[below](#ls-hard-way) for what you must go through if you do not!) + +What this does is opens a data output file (extension `.DA`) and makes +it the output destination, so that the following `WRITE` command sends +its text there, and then it is immediately closed with `O C`, returning +control back to the terminal. + +You can then load that program back into U/W FOCAL with the same command +we used above with the `PIP` solution: + + *O I HELLO + +If you `TYPE` that file from OS/8, you might be wondering why the banner +line doesn't cause a problem on loading the file back in: + + C U/W-FOCAL: HELLO NO/DA/TE + +That leading `C` causes U/W FOCAL to treat it as a comment. Since we're +in "direct mode" at that point, the comment is simply eaten. + + +### The Hard Way + +You might be wondering why we needed to put the `O O` command on a +single line with 3 other commands [above](#ls-write). It is because if +we don't, the `WRITE` and `OUTPUT CLOSE` commands get added as the first +and last lines of our output file, and then we must edit them out before +we can read that file back into U/W FOCAL. As an exercise in practical +use of U/W FOCAL and OS/8's `EDIT` program, we will now show what it +takes to recover from that. + +Page 8 of the [DECUS documentation for OMSI FOCAL][domsi] provides a +good description of this issue and how to work around it to place a text +version of a program on the disk: + +> ... FOCAL assumes `.FC` and `.FD` as name extensions for program and +> data files respectively. Data files are saved in standard [...] ASCII +> format and are compatible with EDIT and TECO-8. Program files are +> saved in core image format and may be transferred by PIP only with +> the `/I` option. To produce an ASCII file containing a FOCAL program, +> `OPEN` an `OUTFILE`; `WRITE ALL` then `OUTPUT CLOSE`. + +The resulting file contains the `WRITE ALL` command at the beginning and +the `OUTPUT CLOSE` command at the end. Removing those lines enables the +file to be read back in as a program using the `OPEN INPUT` command. + +Let's see how to do this, step by step. First, let's enter a simple +program: + + .R UWF16K + *1.10 T "HELLO",! + *G + HELLO + +Now let's save it to an ASCII text file on the OS/8 disk using separate +U/W FOCAL commands, rather than the one-liner we did above: + + *OPEN OUTPUT TEST,ECHO ⇠ uses *.FD by default + *W + C U/W-FOCAL: TEST NO/DA/TE + + 01.10 T "HELLO",! + *OUTPUT CLOSE + +Next, we'll break out of the U/W FOCAL environment to get back to OS/8 +and show that the file is there, but with lines we don't want: + + *^C ⇠ that is, hit Ctrl-C + .TYPE TEST.FD + *W + C U/W-FOCAL: 16K-V4 NO/DA/TE + + 01.10 T "HELLO",! + *OUTPUT CLOSE + +So, let's fix it. We'll use OS/8's `EDIT` program for this, but you +could just as well use `TECO` or another text editor you like better: + + .R EDIT + *TEST.FDEnter is optional +and merely causes FOCAL to print another `*` prompt, which clarifies the +transcript. + +We added the `,ECHO` bits in the commands above only to make U/W FOCAL +echo what it's doing to the terminal to make the transcripts clearer. +In your own work, you might want to leave this off. + +By skipping both of these optional bits and abbreviating the commands, +the final terminal transcript above condenses considerably: + + .R UWF16K + *O I TEST ⇠ assumes *.FD, just like O O + _G ⇠ no ECHO this time, so no * prompt, just EOF indicator + HELLO + +[domsi]: http://www.pdp8.net/pdp8cgi/query_docs/view.pl?id=366 + + +## Lowercase Input + +The version of U/W FOCAL we include by default on the PiDP-8/I's OS/8 +system disk copes with lowercase input only within a fairly narrow +scope. The fact that it copes with lowercase input at all is likely due +to the fact that the version we ship was released late in the commercial +life of OS/8, by which time lowercase terminals were much more common +than at the beginning of OS/8's lifetime. + +The examples in the [U/W FOCAL Manual][uwfm] are given in all-uppercase, +which means there is no reason you would immediately understand how U/W +FOCAL deals with lowercase input, having no examples to build a mental +model from. If you just guess, chances are that you will be wrong sooner +or later, because U/W FOCAL's behavior in this area can be surprising! + +The two main rules to keep in mind are: + +1. U/W FOCAL is case-sensitive for variable and built-in function + names, but it is case-insensitive for command names. + +2. U/W FOCAL doesn't support lowercase variable and function names. It + may sometimes appear to work, but internally, U/W FOCAL isn't doing + what you want it to. + +The following gives incorrect output because of a violation of rule 1: + + *type fsin(pi/2)! + 0.000000000E+00* + +The correct answer is 1. It fails because there is no built-in function +called `fsin` nor a built-in constant `pi`. + +FOCAL gives an answer here instead of detecting our failure to call +things by their right names because it is falling back to its rule to +use a value of 0 where no value or function is available to do what you +asked. Zero divided by 2 is 0; then it tries to subscript a nonexistent +`fsin` variable with index 0, so it punts and gives the answer you see +above, zero. + +A better language would have detected your errors and given a +diagnostic, but U/W FOCAL is implemented in less than a page of PDP-8 +core memory, roughly the same number of bytes as +[Clang](http://clang.llvm.org/) gives when compiling an empty C program +on the machine I'm typing this on. The fact that U/W FOCAL detects +errors *at all* is somewhat impressive. + +To get the expected result, call the `FSIN` function and use the `PI` +constant, which are very much not the same thing as `fsin` and `pi` to +FOCAL: + + *type FSIN(PI/2)! + 1.000000000E+00 + +U/W FOCAL doesn't care that you gave the `type` command in lowercase, +but it *does* care about the case of the function and variable names. + +U/W FOCAL's tolerance of lowercase in command names doesn't extend to +arguments. In particular, the `OPEN` command's argument must be +uppercase: `o o` doesn't work, nor does `O o`, but `o O` does. + +Violating rule 2 can be even more surprising: + + .R UWF16K ⇠ We need a fresh environment for this demo. + *s a=1 ⇠ What, no error? I thought you said... + *s b=2 + *s c=3 + *type $ ! + * + +No, that transcript isn't cut off at the end: the `TYPE` command simply +doesn't give any output! Why? + +The reason is that U/W FOCAL can't \[currently\] cope with lowercase +variable names. + +But wait, it gets weird: + + *s A=1 + *s foo=42 + *type $ ! + A (+00) 1.000000000E+00 + &/(+00) 4.200000001E+01 + +We now see output for our uppercased `A` variable, but what is that `&/` +noise? Apparently "`foo`" somehow gets mangled into `&/` by FOCAL's +command parser. + +We have not yet tried to investigate the reason `foo` gets saved into a +mangled variable name and `a`, `b`, and `c` above do not, because the +workaround is simple: keep CAPS LOCK engaged while typing +FOCAL programs except when typing text you want FOCAL to send back out +to the terminal: + + *1.1 TYPE "Hello, world!"! + *G + Hello, world! + +See the [Variables section][vars] of [`CARD2.DA`][card2] for more +information on variable naming. + +[card2]: uwfocal-refcards.md#card2 +[vars]: uwfocal-refcards.md#variables + + +## Default Output Format + +FOCAL is primarily a scientific programming language. That, coupled with +the small memory size of the PDP-8 family and the slow terminals of the +day mean its default output format might not be what you initially +expect. Consider these two examples pulled from the [U/W FOCAL +Manual][uwfm]: + + *TYPE FSGN(PI), FSGN(PI-PI), FSGN(-PI) ! + 1.000000000E+00 0.000000000E+00-1.000000000E+00 + *TYPE 180*FATN(-1)/PI ! + -4.500000000E+01 + +This may raise several questions in your mind, such as: + +1. Why is there no space between the second and third outputs of the + first command? + +2. Why does the ouptut of the first command begin in the second column + and the second begin at the left margin? + +3. Is the second command giving an answer of -4.5°? + +If you've read the U/W FOCAL Manual carefully, you know the answer to +all three of these questions, but those used to modern programming +environments might have skimmed those sections and thus be surprised by +the above outputs. + +The first two questions have the same answer: U/W FOCAL reserves space +for the sign in its numeric outputs even if it doesn't end up being +needed. This was done, no doubt, so that columns of positive and +negative numbers line up nicely. It might help to see what's going on if +you mentally replace the spaces in that first output line above with `+` +signs. + +This then explains the apparent discrepancy between the first and second +commands' outputs: the first output of the first command is positive, +while the second command's output is negative, so there is a space at +the beginning of the first output for the implicit `+` sign. + +As for the third question, the default output format is in scientific +notation with full precision displayed: 4.5×10¹ = 45 degrees, the +correct answer. + + +### Improvements + +The following changes to the examples as given in the manual show how +you can get output more suitable to your purposes: + + *TYPE %1, FSGN(PI):5, FSGN(PI-PI):5, FSGN(-PI)! + 1 0 -1 + +That sets the output precision to 1 significant digit, which is all we +need for the expected {-1, 0, -1} ouptut set. The tabstop formatting +options (`:5`) put space between the answers, but there is a trick which +gives similar output with a shorter command: + + *TYPE %5.0, FSGN(PI), FSGN(PI-PI), FSGN(-PI)! + 1 0 -1 + +That tells it to use 5 significant digits with zero decimal digits. +Since the answers have only one significant digit, FOCAL right-justifies +each output with 4 spaces. There are 5 spaces between the `1` and `0` +outputs because of that pesky implicit `+` sign, though. + +The second example above can be improved thus: + + *TYPE %3.2, 180*FATN(-1)/PI ! + -45.0 + +That tells FOCAL to display 3 significant digits, and to include up to 2 +decimal places even if the traling one(s) would be 0, thus showing all 3 +significant digits in an answer expected in degrees. If you'd wanted 4 +significant digits with any trailing zeroes instead, you'd give `%4.3` +instead. If you'd given `%3`, the output would be `-45`, the trailing +zero being deemed unnecessary. + + +## ASCII Character & Key Names + +Many of the common names for keys and their ASCII character equivalents +have shifted over the years, and indeed they shifted considerably even +during the time when the PDP-8 was a commercially viable machine. The +following table maps names used in the [U/W FOCAL Manual][uwfm] to their +decimal ASCII codes and their common meaning today. + +| Old Name | ASCII | Current Name | +| ----------- | ----- | ------------- | +| `RUBOUT` | 127 | Delete or Del | +| `DELETE` | 127 | Delete or Del | +| `BACKARROW` | 95 | Underscore | +| `UNDERLINE` | 95 | Underscore | + +Beware that the ASCII values above differ from the values given in the +U/W FOCAL Manual Appendix "Decimal Values for All Character Codes." +FOCAL sets the 8th bit on ASCII characters for reasons unimportant here. +Just add 128 to the values above if you need to get the FOCAL +equivalent. + +Some terminals and terminal emulator software may remap Backspace and +Delete, either making one equivalent to the other or swapping them. +Without such remapping, if you hit the key most commonly marked +Backspace on modern keyboards, U/W FOCAL will just insert an ASCII +character 8 at that point in the program entry, almost certainly not +what you want. You either need to remap Backspace to Delete or hit the +key most commonly marked Del. + +The U/W FOCAL Manual also references keys that used to appear on some +terminals, most especially teletypes, which no longer appear on modern +keyboards: + +| Teletype Key | Modern Equivalent | +| ------------ | ----------------- | +| `LINE FEED` | Ctrl-J | +| `FORM FEED` | Ctrl-L | + + +## Front Panel Differences + +Whenever the [U/W FOCAL Manual][uwfm] refers to the PDP-8's front panel, +it is speaking generically of all the models it ran on as of October +1978. The PDP-8 models introduced in the decade following the +introduction of the PDP-8/I differ in many ways, and one of the greatest +areas of difference is in their front panel controls and indicators. We +do not intend to fully document all of the differences here, but only to +clarify the differences brought up by the U/W FOCAL Manual. + +You normally will not need to use the front panel with the OS/8 version +of U/W FOCAL we distribute with the PiDP-8/I software distribution since +you start and stop U/W FOCAL through OS/8 rather than the front panel. +However, we thought these matters could use clarification anyway. + +Beyond this point, when we refer to the PDP-8/e, we also mean the 8/f, +which shared the same front panel design. We also include the 8/m, which +normally came with a minimal front panel, but there was an optional +upgrade for an 8/e/f style front panel. These three models are therefore +interchangeable for our purposes here. + + +### `START` vs. `CLEAR` + `CONTINUE` vs. `RESET` + +With the PDP-8/e, DEC replaced the `START` front panel switch of the +preceding PDP-8/I with a `CLEAR` switch. Why did they do this? + +On a PDP-8/I, the difference between `START` and `CONTINUE` is sometimes +confusing to end users, since in many cases they appear to do the same +thing. Why have both? The difference is that `CONTINUE` simply resumes +operation from the current point in the program where it is stopped, +whereas `START` resets several key registers and *then* continues. + +The PDP-8/e change splits this operation up to avoid the confusion: the +old `START` keypress is equivalent to `CLEAR` followed by `CONTINUE`. +(This pair of switches also has a `START` label above them, a clear +functional grouping.) + +The U/W FOCAL Manual also speaks of a `RESET` switch in conjunction with +the FOCAL starting and restarting the computer. I haven't been able to +track down which PDP-8 model has such a switch yet, but for our purposes +here, I can say that it just means to load the starting address and hit +`START` on a PDP-8/I. + + +### `EXTD. ADDR LOAD` + +The PDP-8/e has many fewer switches on its front panel than the PDP-8/I, +yet it is a more functional machine. One of the ways DEC achieved this +is by removing the `IF` and `DF` switch groups and adding the +`EXTD. ADDR LOAD` switch, which lets you set the `IF` and `DF` registers +using the same 12-bit switch register used by the `ADDR LOAD` switch. + +The `ADDR LOAD` switch on a PDP-8/e does the same thing as the +`Load Add` switch on a PDP-8/I. + + +### Switch Direction + +DEC reversed the meaning of switch direction between the PDP-8/I and the +PDP-8/e, and the [U/W FOCAL Manual][uwfm] follows the 8/e convention: on +the 8/I, up=0=off, whereas on the 8/e, up=1=on. Keep this in mind when +reading the U/W FOCAL Manual's references to front panel switch +settings. + + +### Switch Ordering + +When the [U/W FOCAL Manual][uwfm] talks about the switch register (SR), +it numbers the switches left to right, not by their logical bit number +in the switch register. That is, "Switch 0" is the leftmost (high order +bit) SR switch, not "bit 0" in the SR, which would be the rightmost SR +switch. + + +## Error Codes + +The [U/W FOCAL Manual][uwfm] gives a somewhat different error code table +than the one on `CARD4.DA` of the [U/W FOCAL reference cards][uwfr]. For +the most part, the latter is just a simple superset of the former, and +both apply. In some cases, though, the two tables differ, or one of them +differs from the `UWF16K` program we ship on the OS/8 system disk. + + +### `?18.32` vs `?18.42` — `FCOM` index out of range + +The two error code tables give different error codes for this condition. +However, since I have not been able to get this error to happen, I do +not know which code is correct for our current version of FOCAL. + + +### `?31.<7` — Non-existent program area called by `LOOK` or `LINK` + +Our current implementation of U/W FOCAL removed those commands in favor +of `LIBRARY`, so you can't make this one happen. An error in a `LIBRARY` +command is most likely to give `?26.07` instead. + + +### Irreproducible Errors + +There are some errors listed in one or both tables that I have been +unable to cause, though I have tried: + +| Code | Meaning +| ------ | ------- +| ?07.44 | Operator missing or illegal use of an equal sign +| ?18.32 | `FCOM` index out of range (value given in the manual) +| ?18.42 | `FCOM` index out of range (value given on the refcard) +| ?27.90 | Zero divisor + + +### Untested Error Cases + +I have not yet created programs large enough to test the "out of space" +codes `?06.41` (too many variables), `?10.50` (program too large), +`?13.65` (insufficient memory for `BATCH` operation), `?23.18` (too much +space requested in `OUTPUT ABORT` or `CLOSE`), `?23.37` (output file +overflow), and `?25.02` (stack overflow). + +There are also some errors I simply have not yet tried to cause: +`?01.03`, `?01.11`, `?12.10`, `?12.40`. + + +## Missing Hardware Support + +The [U/W FOCAL reference cards][uwfr] and the [DECUS submission][duwf] +talk about features for hardware we don't have. Either the +command/feature doesn't exist at all in the version of U/W FOCAL we +distribute or it doesn't do anything useful, lacking support within the +version of SIMH we distribute. + +Broadly, these features are for the PDP-12, the LAB-8/e, Tektronix +terminals, and pen plotters. Should anyone extend SIMH with a way to +control such hardware (or emulations of it) we may consider putting +these features back into our distribution of U/W FOCAL. + +In the meantime, the following facilities do not work: + +* The `FADC`, `FJOY`, `FLS`, `FRS`, and `FXL` functions don't exist + +* There is no plotter support in SIMH, so the `PLOT` command doesn't + exist + +* Although support for the VC8E point-plot display exists in SIMH, the + `VIEW` command to drive it is not present in our version of U/W + FOCAL. + +* Error code `?14.15` can't happen; we have no "display buffer" + +* Error codes `?14.50` and `?14.56` can't happen; SIMH doesn't + simulate a PDP-12 or a LAB-8/e + + +## `FRA` Built-In Function + +`CARD3.DA` and `CARD4.DA` in the [U/W FOCAL reference cards][uwfr] refer +to a `FRA` built-in function which the [manual][uwfm] does not document. +Lacking documentation, we have not been able to test it. Once we figure +out what it is supposed to do, it will be documented here. + +Until then, the three `?17.XX` error codes listed on the refcard are +untested. + + +## `ZVR` Command + +Some U/W FOCAL documents talk about a `ZVR` command. It is the Zero +VaRiable command and is thus just another way of spelling `ZERO`, since +U/W FOCAL only pays attention to the first letter of the command. + + +## Differences Between U/W FOCAL and Other FOCALs + +The [DECUS submission for U/W FOCAL][duwf] lists the following +advantages for the version of U/W FOCAL included with the PiDP-8/I +software distribution as compared to FOCAL,1969, FOCAL-8, and OMSI PS/8 +FOCAL: + +1. Extended library features with device-independent chaining and + subroutine calls between programs. + +2. File reading and writing commands, 10 digit precision, full 32k + memory support, 36 possible functions, 26 possible command letters. + +3. Computed line numbers and unlimited line lengths. + +4. Tabulation on output, format control for scientific notation. + +5. Double subscripting allowed. + +6. Negative exponentiation operators permitted. + +7. `FLOG`, `FEXP`, `FATN`, `FSIN`, `FCOS`, `FITR`, and `FSQT` rewritten + for 10-digit accuracy. + +8. Character manipulations handled with `FIN`, `FOUT`, and `FIND`. + +9. Function return improvements: + + * `FSGN(0)=0` in U/W FOCAL; `=1` in FOCAL,1969 + * `FOUT(A)=0` in U/W FOCAL; `=A` in PS/8 FOCAL + +10. n/a; see [above](#miss-hw) + +11. 6 special variables are protected from the `ZERO` command: `PI`, + `!`, `"`, `$`, `%`, and `#`. + + `PI` is initialized as 3.141592654. + +12. The limit on the number of variables is 676 + +13. Text buffer expanded to 15 blocks + +14. Two-page handlers permitted + +15. Program and file names are wholly programmable. File size may be + specified. OS/8 block numbers may be used in place of file names. + +16. The `OPEN` and `DELETE` commands can have programmed error returns. + +17. Improved distribution and random initialization of `FRAN`. + +18. `ERASE`, `MODIFY`, and `MOVE` do not erase variables. + +19. `WRITE` doesn't terminate a line; `MODIFY` doesn't echo form feed. + +20. U/W FOCAL's starting address is 100 (field 0) or 10200 (field 1). + + +## Converting Programs from Other Versions of FOCAL + +Programs saved by other versions of FOCAL generally don't have the same +format as the core images used by U/W FOCAL. You must therefore use one +of the [text based loading methods](#loading) to save your program text +out of the other FOCAL and load it into U/W FOCAL. + +Also, while the `ERASE` command may be used to zero variables in other +FOCALs, you must use `ZERO` for that in U/W FOCAL. If your program +starts off with `ERASE` commands to initialize its variables, there's a +pretty good chance your program will just erase itself under U/W FOCAL. + + +## License + +Copyright © 2017 by Warren Young and Bill Cattey. Licensed under the +terms of [the SIMH license][sl]. + +[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md ADDED doc/uwfocal-manual.md Index: doc/uwfocal-manual.md ================================================================== --- /dev/null +++ doc/uwfocal-manual.md @@ -0,0 +1,3056 @@ +# U/W FOCAL Manual V4E, October 1978 + + +UWFUWFU           UWFUWFU  UWFUWFU           UWFUWFU  UWFUWFUWFUWFUwFUWFUWFUWF +WFUWFUW           WFUWFUW  WFUWFUW           WFUWFUW  WFUWFUWFUWFUWFUWFUWFUWFU +FUWFUWF           FUWFUWF  FUWFUWF           FUWFUWF  FUWFUWFUWFUWFUWFUWFUWFUW +UWFUWFU           UWFUWFU  UWFUWFU           UWFUWFU  UWFUWFUWFUWFUwFUWFUWFUWF +WFUWFUW           WFUWFUW  WFUWFUW           WFUWFUW  WFUWFUW +FUWFUWF           FUWFUWF  FWUFWUF           FUWFUWF  FUWFUWF +UWFUWFU           UWFUWFU  UWFUWFU     U     UWFUWFU  UWFUWFU +WFUWFUW           WFUWFUW  WFUWFUW    UWF    WFUWFUW  WFUWFUW +FUWFUWF           FUWFUWF  FUWFUWF   UWFUF   FWUFWUF  FUWFUWFUWFUWFUWFUW +UWFUWFU           UWFUWFU  UWFUWFU  WWFUWFU  UWFUWFU  UWFUWFUWFUWFUWFUWF +WFUWFUW           WFUWFUW  WFUWFUW UWFUWFUWF WFUWFUW  WFUWFUWFUWFUWFUWFU +FUWFUWF           FUWFUWF  FUWFUWFUWFUWFUWFUWFUWFUWF  FUWFUWFUWFUWFUWFUW +UWFUWFU           UWFUWFU  UWFUWFUWFUWFUWFUWFUWFUWFU  UWFUWFU +WFUWFUW           WFUWFUW  WFUWFUWFUWFU FUWFUWFUWFUW  WFUWFUW +FUWFUWFW         WFUWFUWF  FUWFUWFUWFU   WFUWFUWFUWF  FUWFUWF +UWFUWFUWFUWFUWFUWFUWFUWFU  UWFUWFUWFU     UWFUWFUWFU  UWFUWFU + FUWFUWFUWFUWFUWFUWFUWFU   WFUWFUWFU       FUWFUWFUW  WFUWFUW +  WFUWFUWFUWFUWFUWFUWFU    FUWFUWFU         WFUWFUWF  FUWFUWF +      UWFUWFUWFUWFU        UWFWUFU           UWFUWFU  UWFUWFU + + + +## Index to Major Topics in This Manual + +* [Abbreviations](#abbreviations) +* [Arithmetic operators](#operators) +* [Break key](#ctrl-keys) +* [Character codes](#character-codes) +* Commands + * [Summary](#commands) + * [Direct, indirect](#dir-ind-cmd) + * [`ASK`](#io-cmds) + * [`BREAK`](#break) + * [`COMMENT`](#comment) + * [`DO`](#do) + * [`ERASE`](#erase) + * [`FOR`](#for) + * [`GOTO`](#goto) + * [`HESITATE`](#hesitate) + * [`IF`](#if) + * [`JUMP`](#jump) + * [`KONTROL`](#kontrol) + * [`LINK`](#link) + * [`LOOK`](#look) + * [`MODIFY`/`MOVE`](#modify) + * [`NEXT`](#next) + * [`ON`](#on) + * [`OPEN`](#open) + * [`PUNCH`](#punch) + * [`QUIT`](#quit) + * [`RETURN`](#return) + * [`SET`](#set) + * [`TYPE`](#io-cmds) + * [`WRITE`](#write) + * [`XECUTE`](#xecute) + * [`YNCREMENT`](#yncrement) + * [`ZERO`](#zero) + * [Editing](#editing) + * [Enclosures](#enclosures) + * [Error messages](#error-messages) + * [Formatting](#formatting) + * Functions + * [Summary](#function-summary) + * [Program Defined](#pdfs) + * [`FABS`](#fabs) + * [`FATN`](#fatn) + * [`FBUF`, `FCOM`](#fbuf) + * [`FCOS`](#fcos) + * [`FDIN`](#fdin) + * [`FEXP`](#fexp) + * [`FIN`](#fin) + * [`FIND`](#find) + * [`FITR`](#fitr) + * [`FLOG`](#flog) + * [`FMIN`, `FMAX`](#fmin) + * [`FMQ`](#fmq) + * [`FOUT`](#fout) + * [`FRAC`](#frac) + * [`FRAN`](#fran) + * [`FSGN`](#fsgn) + * [`FSIN`](#fsin) + * [`FSQT`](#fsqt) + * [`FSR`](#fsr) + * [`FTRM`](#ftrm) + * [Input echo](#input-echo) + * [Input terminators](#input-terminators) + * [I/O operators](#io-operators) + * [Line numbers](#line-numbers) + * [Loops](#loops) + * [Numbers and variables](#numbers) + * [Patches](#patches) + * [Punctuation](#punctuation) + * [Reading in programs](#punch) + * [Symbol table dump](#symbol-table-dump) + * [Trace feature](#trace) + * [Variables](#variables) + + +## Introduction to U/W-FOCAL for the PDP-8 + +UWF is a powerful interactive language for the PDP-8¹ which combines +ease of use with an extensive set of features, allowing one to perform +complex calculations or control laboratory experiments with a minimum of +effort. UWF is an advanced version of FOCAL,¹ a stack processing +language which is somewhat similar to BASIC and FORTRAN but much easier +to use! Programmers who are familiar with either of these languages +should be able to write programs for UWF almost immediately, while those +who are just learning will find that UWF's simplified commands and +built-in editor make their progress very rapid. + + +### Hardware Requirements + +The minimum system configuration for running UWF is an 8K machine +capable of executing the PDP-8 instruction set and some sort of +terminal. UWF will automatically take advantage of any additional memory +present as well as permitting the use of high-speed I/O devices such as +punched paper tape or an audio tape recorder for program and data +storage. There is also a much more elaborate version available for +systems licensed to use the OS/8 operating system¹ which provides +complete device-independent file support. + + +### Loading UWF + +To load the binary tape containing UWF into your machine, proceed as +follows: + +1. Make sure that the BIN loader is resident in Field 0. + +2. Set the Switch Register to 7777 and hit `ADDR LOAD`, then reset + Switch 0 if input is from the High Speed reader (leaving 3777), + otherwise go to the next step. + +3. Place the tape in the reader so that the read-head is positioned + over the leader section and hit the `START` (or `CLEAR` and + `CONTINUE`) switches. + +The run light will go off when the tape has finished loading; check to +be sure the display is zero, indicating a successful load. If not, +repeat steps 1-3 above to see if you get the same checksum error each +time. If you do, the tape you are using has probably been damaged and +you should try again with another copy. + + +### Starting the Program + +In order to start UWF for the first time, do the following: + +1. Set the Switch Register to 0100 (UWF's Starting Address) + +2. Press the `ADDR LOAD` and `EXTD. ADDR LOAD` switches + +3. Set switches 0-2 and 6-11 for any [options desired](#opt-switch) + +4. Now set the `RUN/HALT` switch to `RUN`, and hit `CONTINUE` + +UWF should respond immediately with a congratulatory message and +indicate the amount of memory available in your system. For +installations with more than 8K, here is how the additional memory space +is used: + +| Core | Features Allowed | +| ---: | -------------------------------------------- | +| 12K | Expanded symbol storage or `FCOM` | +| 16K | One additional program area or `FCOM` | +| ... | ... | +| 32K | Five more program areas, or four plus `FCOM` | + +If you wish to limit the amount of memory available for any reason, you +should set switches 9-11 in the switch register before proceeding with +Step 4: + +> Switches 9-11 (octal): 0=All, 1=28K, 2=24K, 3=20K, 4=16K, 5=12K and 6 or 7=8K + +There are a number of other 'custom' features which can be installed +automatically when the program is first started up. These options are +controlled by the setting of bits 0-2 and 6-8 in the switch register. +The first group (0-2) selects various terminal options while the second +group (6-8) controls additional features. Switches 3-5 are ignored and +may be set to any value. Once UWF has been started the first time, step +3 is unnecessary and the switches may remain set to '100'. + + + +| Switch | Function | +| -----: | -------------------------------------------------- | +| 0 | Use 'CRT-style' rubouts instead of 'backslashes' | +| 1 | Output four 'nulls' after every Carriage Return | +| 2 | Print error messages on a new line | +| 6 | Add three extra 'secret variables' (`&`, `:`, `\`) | +| 7 | Add the `KONTROL` command and the `FDIN` function | +| 8 | Add the `FCOM` and `FBUF` functions (requires 12K) | + +Example: A switch setting of '4134' limits the program to 16K, adds +`FCOM`, `FBUF` and the digital I/O routines, and installs 'scope +rubouts'. The '100' bit is ignored. + +Some of these patches can also be installed (or removed) after the +program has been started; see [the Patches section below](#patches) for +further details. Note that adding the `FCOM` function reduces the +effective memory size by 1 field, hence users with 16K who add this +option will normally lose the first additional program area. Since it +might be more desirable in this particular case to have `FCOM` replace the +extra variable storage, there is a 'magic location' which can be changed +(*before* you start things up!) to effect this arrangement. (16K +configurations only; see [the Patches section below](#patches) for +details.) + +Note that UWF runs with the interrupt system *ON* which allows program +execution to overlap certain I/O operations. The result is faster run +times, a 'live' keyboard and the possibility of adding 'background' +tasks which can be controlled by the program or 'high-level' interrupts +in which an external event causes the execution of a specific group of +statements within the program. + +With the interrupt system enabled, however, it is possible that an +'unknown' device will create a continuous interrupt and thus prevent UWF +from running. If the `RUN` light goes on but there is no output as soon +as you hit the `CONTINUE` switch, halt the machine, hit the `RESET` or +`CLEAR` switch a few times, and then restart at location 100. If UWF +still appears to be stuck in an endless loop, you will probably have to +add an appropriate 'clear flag' instruction to the interrupt routine. +See [the Patches section below](#patches) for the proper location. + + +### UWF's Control Keys + +UWF recognizes the following control keys: + +1. CTRL/F is the master program break key: it will restart + UWF at any time, assuming that the program is still running. + +2. CTRL/C is the monitor break key. It will eventually trap + to a resident 'ODT' package which is not yet implemented. + +3. CTRL/S (`XOFF`) stops output to the terminal. This + provides users with video terminals time to inspect their results. + +4. CTRL/Q (`XON`) resumes output to the terminal. Some + terminals issue `XON`/`XOFF` codes automatically when the screen + fills up. + +5. The RETURN key is used to terminate all command lines. + UWF will not recognize a command until the RETURN key is typed. + +6. The RUBOUT or DELETE key is used to cancel the + previous character. On hard-copy terminals, a `\` is echoed for each + character deleted. On video terminals they just disappear! + +7. The LINE FEED key is used to retype a command line + *before* typing RETURN in order to verify that all + corrections were made properly. This is mostly for hard-copy + terminals. + +Remember: UWF can be interrupted at any time simply by typing +CTRL/F. This is accomplished by holding down the +CTRL key and then typing the letter `F`. UWF will respond by +typing a question mark (`?`) followed by the line number where the +program was interrupted and then print an asterisk to indicate that it +is ready for further instructions: + + ?@ 05.13 UWF was interrupted at line 5.13 + + +## Direct and Indirect Commands + +UWF prints an asterisk (`*`) whenever it is in command mode waiting for +new instructions. You can then type in either 'direct commands' which +are executed immediately, or 'indirect commands' which are saved for +execution at a later time. To use UWF as a fancy calculator simply give +it a 'direct command' and hit the RETURN key. For example, if you enter +the command: + + *TYPE PI + +UWF will print the value '3.141592654E+00', correct to 10 significant +figures. The Direct Command feature is the essence of interactive +programming since it permits one to work through a long calculation a +step at a time, or to try out several different ways of doing something. +You can experiment with any of UWF's commands by simply typing them in +as you read through this manual. + +Indirect Commands always begin with a line number which indicates the +order in which they are to be executed. They may be entered in *any* +order, however, and can be examined or changed at any time. Changes to +indirect commands are facilitated by the use of UWF's built-in editor +which allows lines to be modified, moved to a different part of the +program, or deleted. Since indirect commands can be selectively executed +by direct commands it is possible to build a very powerful set of +'macros' which can then be called with just a few keystrokes. + +Line numbers in UWF have a 'dollars and cents' format: `XX.YY` where +`XX` may range from 00-31 (the 'group' number) and `YY` may have any +value from 00-99 (the 'step' number). Group and Step both have special +meanings in some commands, so the first line of the program is usually +labeled `1.1`. Notice that leading and trailing zeros are not necessary, +but one must always include a space after the line number to separate it +from the commands on the rest of the line. Here are some sample indirect +commands: + + *3.61 TYPE ! + *12.7 COMMENT + *1.99 QUIT + +A standard programming practice is to index sequential commands by an +increment of either '.05' or '.1'. Thus line '1.2' would be used for the +statement following line '1.1' rather than line '1.11'. This leaves room +to insert up to 9 additional lines in case changes to the program are +necessary. Of course lines can always be moved to make room, but it is a +nuisance to have to do this and such changes might require alteration of +other parts of the program as well. + + +### Group and Relative Line Numbers + +Several UWF commands are capable of operating on all statements with the +same group number. To reference an entire group of lines one simply +specifies the group number without designating any particular program +step: `WRITE 1`, for example, will list all the program steps in 'Group +1'. Since the number '1' and the number '1.00' are indistinguishable to +UWF, it is not possible to write just line 1.00 without writing the rest +of the lines in the same group as well. For this reason the first line +in a group is generally reserved for comments in order to avoid any +complications with group operations. + +UWF can also designate a 'sub-group' operation consisting of all the +lines following a specified line in the same group. Such operations are +indicated by a 'negative line number': 'WRITE -1.5', for instance, will +list all of the lines in Group 1 starting from line 1.5 (if it exists). + +Line numbers in the range '.01-. 99" are termed 'relative line numbers', +i.e. they refer to lines in the *current group*, rather than to lines in +'Group 0'. The use of such line numbers is encouraged because it makes +the program more compact and also allows subroutines to be moved easily +from one part of the program to another without having to worry about +internal references. Lines with numbers less than 1.00 *can* be saved as +part of the indirect program, but they can only be executed when the +program is started from the beginning since there is no way to branch to +them at a later time. + +Finally, references to line '0' also have a special meaning. A few +commands interpret such references to mean 'the entire program', while +most others regard 'line 0' as a reference to 'the next command'. Line 0 +itself is the permanent comment line (the 'Header Line') at the +beginning of the program. + + +### Punctuation + +It is a common practice to put several commands on the same line in +order to reduce the amount of paper required for listing the program as +well as to consolidate related operations. A 'semicolon' (`;`) is used +to separate such commands: + + *SET A=5; TYPE A + +Commands which operate on more than one expression use a comma to +separate the values. Thus the command `TYPE A,B` is equivalent to `TYPE +A; TYPE B`. Spaces may be included to improve the readability of a +program, but one must remember that 'space' is a terminator, equivalent +to a comma, so the command `TYPE A B` is interpreted as `TYPE A,B`, not +as `TYPE AB`. + + +## Numbers and Variables + +UWF can handle up to 10-digit numbers with a magnitude range of +10615. Numbers may be written as signed or unsigned +quantities and may include a decimal fraction as well as a +'power-of-ten' exponent indicated by the letter `E`. All numbers are +stored internally in a 'floating-point' format with 35 bits of mantissa +and 11 bits of exponent. This is equivalent to more than 10-digit +accuracy. UWF will respond with an error message if a user attempts to +enter a number with too many digits. The following all represent the +value 'sixty': + +* 60 +* 60.00 +* 6E1 +* 600.0E-1 + +UWF also allows letters to be treated as numbers so that questions may +be answered with a 'YES' or 'NO' response rather than with a numeric +reply. When decoded in this manner, the letters 'A-Z' have the values +'1-26', except that the letter 'E' always means 'power-of-ten'. Thus the +answer 'NO' would have the numerical value '155' and the number 'sixty' +could also be written as `0DT` or `0FEA`. Note that the leading '0' is +only required when incorporating such 'numbers' into a program. It is +not required as part of a user response. + + +### Variable Names + +Variables are used to store input values or to save intermediate +results. Variables are thus like the storage registers on a calculator +except that the programmer may make up his own names to designate the +values stored. UWF allows variable names of any length, but only the +first two characters are retained internally. Thus the names JOHN and +JOE would both refer to the variable `JO`. The first character of a +variable name must obviously not be a number, nor can it be the letter +`F` since that letter is used to designate functions. However UWF does +allow symbols such as `$` and `"` to be used as part of a variable name +so you can have quantities such as `A$`, `A`, and `A"`. Variables are +always stored as numeric quantities; UWF does not currently have +'string' variables. + + +### The Symbol Table + +The names of the variables which have been used by the program are saved +(along with their values) in a region of memory called the 'Symbol +Table'. This area is completely independent of the area used to store +the program so changes to the text buffer do not affect any of the +values stored in the symbol table. This is extremely convenient since it +allows a program to be interrupted, modified, and then restarted +somewhere in the middle, knowing that any intermediate results obtained +will still be available. Of course the programmer may examine any such +values simply by `TYPE`ing them out, or he may change a few values with +a direct command before restarting the program. Variables are always +assumed to have the value 'zero' until another value has been assigned. +The `TYPE $` command can be used to list all the values in the Symbol +Table in the order they were defined by the program. The ZERO command is +used to clear the table or to selectively set some of the variables to +zero. Variables with the value '0' may be replaced by other non-zero +variables when the symbol table fills up. This is transparent to the +programmer since 'undefined' variables are always zero anyway. + + +### Protected Variables + +The symbols {`!`, `"`, `#`, `$`, `%`} and optionally {`&`, `:`, `\`}, +along with the value of `PI`, are 'protected' variables which cannot be +replaced or removed by a ZERO command. This makes them useful for saving +results which are needed by a second program. Since they cannot be input +or output directly and do not appear in a symbol table dump, they are +also sometimes called 'secret' variables. Note that UWF automatically +sets `PI` equal to 3.141592654, so users should not use `PI---` as a +variable name or this value will be lost. The variable `!` ('bang') is +used as the dimension constant for [double subscripting](#dsub) and many +of the remaining 'secret variables' serve as dummy arguments for +[Program Defined Functions](#pdfs). To `TYPE` the values of these +variables, you must prefix a `+` sign or enclose them in parentheses: +`TYPE +!` or `TYPE (!)` will output the value of the first one. + + +### Subscripted Variables + +Variables may be further identified by attaching a subscript enclosed in +parentheses immediately after the name, e.g. `A(I)`. Such subscripts may +consist of arithmetic expressions involving other subscripted variables, +so quite intricate relations can be developed. Unlike many other +high-level languages, UWF does not require any 'dimension' statements +for processing subscripted variables, nor are the subscripts limited to +only positive integers. (They are limited to 12 bits, however.) A +variable such as `APPLE(-PIE)` is thus perfectly acceptable although UWF +will view this more prosaically as simply `AP(-3)`. Non-subscripted +variables are the same as those with a subscript of zero, i.e. `A` = +`A(O)`. + + + +To handle double subscripting, UWF *does* require a small amount of +additional information. Before using a double subscripted variable the +programmer must store the maximum value of the first subscript in the +protected variable `!`. This value may be greater than the actual +maximum without incurring any storage penalty, but if it is too small +more than one array element will be stored in the same location. Since +this single 'dimension constant' is used for all arrays it should be +chosen for the largest array in cases where the program uses several +different sizes. + +To illustrate: suppose that operations on a 5x5 array were necessary. +Then `!` ('bang') should be set to 5. If 3x3 arrays were also needed +simultaneously (which is not very likely) their elements would all be +unique and only 9 storage locations would be used, not 25. Non-square +arrays are handled just as easily: a 5x20 array would still only require +that `!` be set to 5 since that is the maximum value of the *first* +subscript. This method of storing two-dimensional arrays proves very +convenient for a wide range of linear algebra problems. The value of `!` +is generally used as a loop limit so that the routines can be used with +any size array. + + +## Arithmetic Operators + +UWF recognizes 6 basic arithmetic, and 1 special 'character value' +operator: + +1. `+` Addition +2. `-` Subtraction +3. `*` Multiplication +4. `/` Division +5. `^` Signed Integer Powers +6. `=` Replacement +7. `'` Value of next character + +These 7 operators may be combined with explicit numbers and function or +variable names to create 'Arithmetic Expressions' such as: + + X= -B/2*A + Y= FSQT(B^2-4*A*C) + +Such expressions can be used *anywhere* that explicit numbers appear in +this writeup. In particular, they may be used to compute line numbers. +All expressions are evaluated to 10-digit accuracy independent of the +format used for output. Intermediate results are generally rounded off +rather than being truncated. Most commands use, or operate on, +arithmetic expressions. If such expressions are *omitted*, a value of +'zero' is always assumed. This occurs frequently when evaluating line +numbers, hence you should recall the comments about line '00.00' +mentioned [above](#line-numbers). + + +### Priority of Arithmetic Operations + +Arithmetic operations are performed in the following sequence: + +| Priority | Op | Description | +| -------: | ------------------------------ | +| 1st | `^` | integer power | +| 2nd | `*` | multiplication | +| 3rd | `/` | division | +| 4th | `-` | subtraction and negation | +| 5th | `+` | addition | +| Last | `=` | replacement | + +When UWF evaluates an expression which includes several operations, the +order above is followed. For example, UWF evaluates the expression: + + X=5^2/5*2-Z=5/2 + +leaving `X` equal to 'zero' and `Z` equal to 2.5. + +Notice that multiplication has a higher priority than division. This is +different from the convention in many other languages where these +operations have equal priority. In most cases this difference is of no +consequence. The effect of embedding replacement operators is to cause +portions of the expression to be evaluated in a somewhat different order +than would be the case if they were not included. In the example above, +for instance, the quantity `5*2` is divided by the quantity `5*2` and +then the quantity `Z` which is equal to `5/2` is subtracted from the +result. However, if one were to add a `Y=` operator after the first `/` +then the quantity `5*2` would be divided by `Y` which would be equal to +`5*2-Z`. + + +### Enclosures + +The order of evaluation can also be changed by the use of enclosures. +Three different kinds are allowed by UWF: Parentheses `()`, Square +Brackets `[]`, and Angle Brackets `<>`. Subscripts and function +arguments are common examples of expressions contained in enclosures. +UWF treats all sets identically — they must be matched, of course! — +except in some of the monitor commands in the OS/8 version. If the +expression contains nested enclosures, UWF will evaluate it starting +with the innermost set and working outward. For example: + + [A=5*-5]^2 + +is evaluated as 'four hundred' with `A`=20 and `B`=5. + + +## Abbreviations + +UWF doesn't care whether you tell it to `TYPE` or `TAKEOFF`! The reason +is that only the *first* letter of the command is recognized, just as +only the first *two* letters of a variable name have significance. So +while we have been carefully spelling out all the commands in the +examples so far, we could just as well have abbreviated them to their +first letters. + +This feature of the language is both good and bad. On the one hand it +greatly reduces the amount of typing required and at the same time +increases the number of program steps possible. But on the other hand, a +program containing hundreds of single letter commands looks more like a +sheet of hieroglyphics than anything else. This makes it quite difficult +for the beginner to understand the program logic until he himself has +become more familiar with the meaning of all the symbols. For maximum +clarity the examples in this writeup will generally be spelled out, but +you should realize that the commands `T PI` and `TYPE PI` will produce +*exactly* the same result. + +We will now turn to a detailed examination of all the commands available +to the UWF programmer, beginning with the editing commands since they +are required for further program development. + + +## Command Mode Editing + +When UWF is in command mode you can use the `RUBOUT` or `DELETE` key to +correct any typing errors. Each time that you hit this key UWF will +delete the preceding character and echo a `\` on the terminal. If you +have a video terminal, and you set switch 0 up when you started UWF for +the first time — or made the appropriate patch yourself — hitting +DELETE will actually remove the character from the screen. +This is obviously much nicer since 'what you see is what you've got'. On +the other hand, users with a hard-copy terminal can always just hit the +LINE FEED key to have the current input line retyped so that +they can see just how it 'really' looks to UWF. There is no limit to the +length of input lines; however, if your terminal does not handle +'wrap-around' automatically, the practical limit is the width of paper. + +In addition to RUBOUT, the BACKARROW (or +UNDERLINE key as it is identified on newer terminals) may be +used to delete all characters to the left, including the line number of +an indirect command. You may then start over again. It is not necessary +to hit RETURN although you may wish to do so to get back to +the left margin again. Note that LINE FEED will not echo a +blank link and RUBOUT will stop echoing a `\` when it reaches +the beginning of the line. + +The use of BACKARROW as a 'line-kill' character necessarily +means that this character (and RUBOUT, of course) cannot be +part of the program, but all remaining ASCII characters, both upper and +lower case, can be used. Control codes can also be used, but they should +be scrupulously avoided since they are non-printing and are therefore +impossible to find when they are embedded in a program. In fact, if you +ever have a mysterious error in what appears to be a perfectly good +command, just try retyping it in its entirety to eliminate any 'ghosts'. + +Once you hit the RETURN key, UWF will digest whatever you +have typed, so subsequent changes require the use of the editing +commands. The text buffer can hold approximately 7000 (decimal) +characters — typically 3-4 pages of printout. To list any or all of this +material you use the `WRITE` command; to eliminate some of it you use +`ERASE`, and to make changes without having to retype the unchanged +part, you use the `MODIFY` command. This command can also be used to +`MOVE` parts of the program to a different location. + + +### `WRITE` + +The `WRITE` command, without any modifier, will list all of the indirect +commands currently saved in the text buffer. Lines are typed out in +numerical sequence, no matter in what order they were entered, and are +separated into the groups you have specified. For this reason it is very +convenient to use a different group number for each major part of the +program even if such a section only has a few program steps. Using the +first line (line `XX.00`) for a `COMMENT` to describe the purpose of +that section is also highly recommended. + +The `WRITE` command can also be qualified by a string of numbers to +limit the listing to selected portions of the program. `WRITE 1`, for +example, will print out just the commands belonging to Group 1, while +`WRITE 2.2` will list only that single line. A command such as `WRITE +1,2,3.3,4.9,5` will list 3 groups and 2 single lines, in the order +specified. Of course you should try to plan your program so that it +executes smoothly 'from top to bottom', but if you do need to add a +major section at the end, the `WRITE` command can be used to at least +make a listing showing the logical program flow. Another convenient +feature of the `WRITE` command is the ability to list a specific line +and all lines following it within the same group. This is done by +specifying a *negative* line number. Thus `WRITE -1.5` will list line +1.5 (if it exists) plus the remainder of Group 1. The `WRITE` command +will not produce an error if the line or group you specified is missing +— it will simply not list it: What you see is what you've got! + + +### `ERASE` + +The `ERASE` command is used to delete parts of the program. `ERASE` +without a qualifier deletes the entire program, while `ERASE 2` will +delete just Group 2. Other possibilities are `ERASE 9.1` which will only +remove that single line, and `ERASE -4.5` which will eliminate the +second half of Group 4. Since `ERASE 0` erases everything, you must use +an `ERASE -.01` command to erase all of Group 0. There is no way to +erase lines such as `2.00` without erasing the entire group at the same +time; this is one restriction on the use of such lines. Unlike the +`WRITE` command, only a single qualifier may be used with `ERASE`, and +UWF will return to command mode immediately after executing the command. +Typing in a new line with the same number as an old one will effectively +erase the previous version. Entry of just a line number by itself will +result in a 'blank line' which may be used to separate sub-sections of a +program. Note that this treatment of blank lines differs from that used +by BASIC. Blank lines will be ignored during program execution. + + +### `MODIFY`/`MOVE` + +To change a program line or to move it to a different part of the +program, you must use the `MODIFY` or `MOVE` commands. `MODIFY` without +a qualifier can be used to examine the header line, but it cannot be +used to change this line. `MODIFY` with a single line number permits +changes to the line specified while a `MODIFY` (or `MOVE`) with *two* +line numbers allows similar changes, but saves the modified line with +the new number. The old line, in this case, remains just as it was. + +`MODIFY` only operates on single lines at the moment, so a command such +as `MODIFY 1` will allow changes to line `1.00`, not to all of Group 1. +Similarly, `MOVE 2,3` will move line `2.00` to line `3.00`; it will not +move all the lines in Group 2. Since UWF does not have a 're-number' +command, moving lines and then erasing the old copy is the only way to +add additional lines when you forget to leave enough room between +sequential line numbers. + +After you have entered a `MODIFY` (or `MOVE`) command, UWF will +optionally print out the line number and then pause until you enter a +search character. As soon as you have done so, the line specified will +be typed out through the first occurrence of this character. If you want +to insert material at this point, just type it in; if you want to delete +a few characters, simply use the `RUBOUT` or `DELETE` key. Other editing +options may be invoked by typing one of the following control keys. +Note: mistakes made while trying to modify a line often lead to embedded +control codes, so if you do get confused, just type CTRL/F and try +again. + +| Keystroke | Name | Description | +| ----------------------------------------- | ---- | ------------------------------------------------- | +| CTRL/F | — | Aborts the command — the line is unchanged | +| CTRL/G | BELL | Rings bell and waits for a new search character | +| CTRL/J | LF | Copies the rest of the line without changes | +| CTRL/L | FF | Looks for the next occurrence of search character | +| CTRL/M | CR | Terminates the line at this point | +| BACKARROW/UNDERLINE | — | Deletes all chars to the left, except line number | +| RUBOUT/DELETE | — | Deletes previous character, as in command mode | + +The last two operations are similar to those available during command +mode except that BACKARROW or UNDERLINE does not +delete the line number. To remove the first command on a line containing +several commands, just enter a semicolon (`;`) as the search character, +wait for the first command to be typed out, hit BACKARROW or +UNDERLINE and then hit the LINE FEED key. + +CTRL/G and CTRL/L may be used to skip quickly to +the part of the line requiring changes. If the change(s) you wish to +make involve frequently used characters (such as an `=`), you can +initially select a different symbol which occurs less frequently and +then use BELL to change to the character you really wish to +find. Or you can simply keep hitting the FORM FEED key to +advance through the line. In case your terminal happens to respond to a +FF, you will be pleased to know that UWF does not echo this +character! + +If you just want to move a line from one location to another, type a +LF as the initial search character. If you are adding new +commands in the middle of a line, be sure to use the LF key — +not the RETURN key — to finish copying the rest of the line. +Otherwise you will lose the commands at the end of the line and you will +have to `MODIFY` the line a second time in order to re-enter them! If +you have a hard-copy terminal you may wish to `WRITE` out the line after +you have modified it to check for additional errors. With a video +terminal, on the other hand, the corrected line will be displayed just +as it is. + +If you have many lines to move (say all the lines in Group 5), and you +have a slow terminal, you can disable the printout during the Move in +order to speed things up. To do this, simply disable the keyboard echo +by using [the `O I` command](#echo-option). A disadvantage to this +method is that not even the `MOVE` commands will be printed so you have +to operate 'in the dark', but this is still the best way to make such a +major program change. To restore the keyboard echo just hit CTRL/F. + +On video terminals, the number of the line being modified is printed out +at the beginning so that the changes will be properly positioned on the +screen. With a hard-copy terminal, however, the line number is not +normally printed in order to leave as much room as possible for rubouts +and insertions. [The Patches section below](#patches) indicates the +location to change if you wish to add the line number printout in this +case. + + +## Expanded Text Storage + +If your machine has more than 12K of memory, UWF will automatically use +Fields 3-7 for additional text buffers. This allows such systems to keep +several different programs in memory at the same time, which is obviously +a very convenient thing to do. The `LOOK` command is then used to select +the desired 'area' for editing, program execution, etc. Programs in +different areas are essentially independent and may use the same line +numbers, but the symbol table and the 'stack' are shared by all areas. + +The `LOOK` command has the form: `LOOK Area`, where `Area` has the value +`0` for the main text buffer and `1`, `2`, `3`, etc. (up to `5`) for the +additional fields. `LOOK` always returns to command mode and is normally +only used as a direct command. `L 1` will switch to Area 1 while `L 0` +(or just `L`) will return to Area 0. For calls between program areas, +see [the `LINK` command](#link). + + +## Input/Output Commands + +UWF's I/O commands are called `ASK` and `TYPE`, respectively. The `TYPE` +command has appeared previously in a few of the examples; basically, it +converts the value of an arithmetic expression to a string of ASCII +characters which are then sent to the terminal, or to whatever output +device has been selected as a result of an appropriate [`OPEN` +command](#open). Similarly, the `ASK` command is used to input numeric +values, either from the keyboard or from another input device. Both of +these commands recognize 6 special operators for controlling the format +of I/O operations. These operators are, in fact, just the symbols +previously identified as 'protected variables', and it is because of +their special significance in `ASK` / `TYPE` commands that they cannot +be input or output directly. These operators and their meanings are as +follows: + +| Op | Description | +| --- | ----------------------------------------------------------- | +| `!` | Generate a new line by printing a CR/LF | +| `"` | Enclose character strings for labeling | +| `#` | Generate a RETURN without a LINE FEED | +| `$` | Print the contents of the Symbol Table | +| `%` | Change the output format | +| `:` | Tabulate to a given column or ignore input | + +You will notice that these are mostly 'output' operations. Nevertheless, +they perform the same function during an `ASK` command that they do in a +`TYPE` command. The `#` operator does not work on all I/O devices and is +therefore seldom used. It was originally intended for overprinting on +the same line, but may be [easily patched](#patches) to generate a +FORM FEED, should that be desirable. The remaining operators +will now be discussed in greater detail. + + +### The New Line `!` (Bang) Operator + +The `!` operator is used to advance to a new line. UWF never performs +this function automatically, so output on a single line may actually be +the result of several `ASK` or `TYPE` commands. 'Bang' operators can be +'piled together' to produce multiple blank lines: `TYPE !!!!!`, for +example, would advance 5 lines. Note that to produce a single blank +line, you may require either 1 or 2 `!`s depending upon whether anything +has been written on the first line. + + +### The Quote `"` Operator + +UWF uses the `"` operator to enclose strings which are output just as +they appear in the program. Thus the command: `TYPE "HELLO THERE, HOW +ARE YOU TODAY?"` would simply print the message enclosed by the quote +marks. The `ASK` command uses such output for prompting: `ASK "HOW OLD +ARE YOU? ",AGE` will print the question and then wait for a response. In +some cases the [`TRACE` operator (`?`)](#trace) is also useful for +printing labels during an `ASK` or `TYPE` command. + + +### The Symbol Table Dump `$` Operator + +The Symbol Table Dump operator (`$`) has already been [mentioned +briefly](#symbol-table). It prints all the symbols defined by the user's +program in the order in which they were encountered. It does not print +the values of the 'secret variables'. To conserve paper and to permit as +many symbols as possible to be listed on a video terminal, the listing +normally has three values per line. This format can be changed simply by +specifying a different number after the `$`. Thus, `TYPE $5` will change +the default value to 5, which is convenient on terminals which can print +up to 132 characters per line. The total number of symbols possible +depends upon the amount of memory available. In an 8K machine there will +only be room for about 120 variables, while in a 12K machine one can +have approximately 675. For internal reasons, a Symbol Table Dump always +terminates execution of the command line it is on, hence commands +following it on the same line will not be executed. + + +### The Format `%` Operator + +The format operator (`%`) allows UWF to print numeric results in any of +three standard formats: integer, mixed decimal, or 'floating-point' +(scientific notation). A format remains in effect until another one is +selected. Initially UWF is set to print all results in full-precision +scientific notation so that all digits of a result will be output. +However for many calculations a 'decimal' or 'integer' style of output +is more desirable. Such formats are selected by the value of an +arithmetic expression following the `%` operator which has the form: + + %ND.DP + +where `ND` is the Number of Digits to be printed (the result will be +rounded off to this precision), and `OP` is the requested number of +Decimal Places. `DP` should be smaller than `ND` unless `ND` is zero; if +`DP` is zero the result will be an 'integer' format and no decimal point +will be printed. Thus the command `TYPE %2,PI` will produce the result +'   `3`'. + +Notice that the form of the format specification is similar to that used +for line numbers. This may help to explain why it is necessary to use +`%5.03`, rather than `%5.3`, when you wish to have 5 digits printed with +up to 3 decimal places. The number of decimal places actually printed +may not be exactly what you have requested. If UWF finds that the number +being output is too big to fit the format you specified it will reduce +the number of decimal places. For example, if you try the command: + + TYPE %5.04, 123.456 + +you will actually get the value '  `123.46` printed since it is not +possible to show 4 decimal places with only 5 digits. Note however that +UWF *did* print the 5 most significant digits in a format approximately +like the one requested. Programmers accustomed to dealing with large +powerful computers which print only a string of `*****`s under similar +circumstances should find UWF's approach quite sensible. + +What happens if the number is so large that even the most significant +part overflows the field specified? In that case UWF automatically +switches to floating-point format for that value so you will be able to +see an unexpected result without having to re-run the entire program! +You can try this out simply by typing the value `123456` without +changing the format from the previous setting. UWF will print: +'  `1.2346E+05`. + +To purposefully select a floating-point format you should specify one +with `ND` equal to 0. Thus the format `%.05` will print 5-digit numbers +in proper scientific notation (1 digit before the decimal point). The +default format when UWF is first loaded is `%.1` which prints all 10 +digits. To return to this format you can simply specify `%`, since the +value '0' is treated the same as `%.1`. Note that using an arithmetic +expression for the format specification, rather than just a fixed +number, permits the format to be changed dynamically while the program +is running: `%VF` would select a format using the value of the variable +`VF`. + +Finally, note that UWF will never print more than 10 significant digits, +the limit of its internal accuracy. If the quantity `ND` is larger than +this, spaces will be used to fill out the number. If the quantity `DP` +is larger, zeros will be added. In any case, if the number is negative +UWF will print a minus sign just ahead of the first digit. A plus sign +is never printed — except as part of the exponent — but a space is +reserved for it anyway. An additional space is also printed at the +beginning in order to separate the number from any previous output. This +space may be omitted (or changed to an `=` sign) by making the patch +shown in [the Patches section below](#patches). + +To summarize the various output format settings: + +| Format | Description | +| ------ | ---------------------------------------- | +| `%N` | `N` digit integer format | +| `%N.D` | `N` digits with up to `D` decimal places | +| `%.D` | `D` digits in scientific (F.P.) notation | +| `%` | the same as `%.1` — full precision F.P. | + + +### The Tab `:` Operator + +The tab (`:`) operator provides a convenient way to create column +output. The expression following the colon is used to set the column, +i.e. `:10` specifies column 10. The tab routines do not attempt to go +backward if the column specified is to the left of the current print +position; the command is simply ignored in this case. 'Tabs' are +recommended in place of a string of spaces so that changes in the output +format will not affect subsequent columns. + +There are two special cases: tabbing to column 0 and tabbing to a +negative column. Neither is possible since columns are numbered from 1 +to 2047, but both are useful operations. Expressions which have the +value zero can be evaluated by the tab operator within a `TYPE` command +*without* producing any output. This is convenient occasionally, +especially for calling the [`FOUT` function](#fout). Tabbing to a +negative column has been given a quite different interpretation, +however. + +Since the current version of UWF can only input numeric values with the +`ASK` command, there is a need for a method to skip over label fields +when re-reading output produced by another program. This facility is +provided by 'tabbing' to a negative column number which causes no +output, but instead reads and ignores the specified number of +characters. Thus the command `TYPE :-1` will *read* 1 character from the +input device. This may well appear confusing, since we have an 'output' +command waiting for input, so the `ASK` command may be used instead: +`ASK :-1` performs the same function. This feature provides a simple way +to get the program to wait for operator intervention. For example, the +command `TYPE "TURN ON THE PUNCH":-1` would print the message and then +wait for any keyboard character to be typed. An `ASK :-2000` command +will let a visitor type almost anything s/he likes into the computer +without danger of destroying a valuable program. + + +### `ASK`/`TYPE` Summary + +Having discussed all the `ASK`/`TYPE` operators, there is really very +little more to explain about the commands themselves. `TYPE` can +evaluate a whole series of arithmetic expressions which are generally +separated by commas or spaces or one of the above operators, while `ASK` +can input values for a whole list of variables, again separated by +commas or spaces. Here are a few examples: + + TYPE !!:10"TODAY IS"%2,15 %4" OCTOBER"1978! + ASK "TYPE A KEY WHEN YOU ARE READY TO G0":-1 + TYPE !"THE ROOTS ARE:" %5.02, R1 :20 R2 !! + ASK !"WHAT IS THE INITIAL VALUE OF X? " IX + +Notice that the tab (`:`) and new line (`!`) operators can be included +in an `ASK` command to help format the input. Thus `ASK X :10 Y !` would +keep the input responses in two nicely aligned columns. It is quite +convenient to be able to output the necessary prompting information with +the `ASK` command; other languages frequently require separate commands +(such as `PRINT` followed by `INPUT`) for these operations. The [trace +operator](#trace) is also useful in `ASK` and `TYPE` commands when one +is interested in a 'minimal effort' I/O structure. + +One other feature of a `TYPE` command should be noted: it is possible to +save the value of a quantity being `TYPE`d just by including a +replacement operator in the expression. Thus `TYPE X=5` will output the +value '5' and also save it as the value of the variable `X`. + +Numeric input for the `ASK` command can take any of the forms [listed +above](#numbers-variables); specifically: signed integers, alphabetic +responses, decimal values or numbers containing a power-of-ten exponent. +Because such numbers are processed as they are being input, it is not +possible to use the RUBOUT key to delete an erroneous +character. Rather, one must effectively hit the 'clear key' (as on a +calculator) and then re-enter the entire number. The 'clear' function is +indicated by typing a BACKARROW or UNDERLINE just +as it is during command input. If you do attempt to use +RUBOUT, no `\` will be echoed which serves as a reminder that +this key is ignored during an `ASK` command. + + +### Input Terminators + +UWF allows a variety of characters to serve as input terminators. In +addition to the RETURN key, one may use a SPACE — +spaces in front of a number are ignored, but may be used to format the +input as desired; spaces following the number always act as a terminator +— a COMMA, SEMICOLON, or other punctuation marks +such as a QUESTION MARK or COLON. A +PERIOD is, of course, recognized as a decimal point, but a +second period also works as a terminator. Any of the arithmetic +operators also serve as terminators; in particular, the / and +- characters are often convenient. This allows responses such +as `1/2` or `1-5` for the values of *two* different variables. + +In fact, any character *except* `0`-`9`, `A`-`Z`, RUBOUT and +LINE FEED or FORM FEED can be used to terminate +the response to an `ASK` command. More to the point, however, is the +fact that the program can test to see which terminator was used. This +allows a very simple input loop to read an indefinite number of items +until a specific terminator — a `?`, for instance — is found. See the +discussion of the [FTRM function](#ftrm). + +The ALTMODE or ESCAPE key is a special case: +typing either of these keys leaves the previous value of the variable +unchanged. This allows quick responses to repeated requests for the same +value. The program, of course, can pre-set the value of the variable so +that an ALTMODE response will merely confirm the expected +value. + + +## Arithmetic Processing Commands + +There are four commands in this group: `SET`, `XECUTE`, `YNCREMENT` and +`ZERO`. + + +### `SET` + +The most frequently used command in the UWF language is the `SET` +command. This command evaluates arithmetic expressions without producing +any output (except when the [trace feature](#trace) is enabled). Such +expressions typically have the form: + + SET Variable Name = Arithmetic Expression + +But more general expressions, particularly those containing +sub-expressions, are perfectly acceptable. Thus a command such as `SET +A=B=C=5` could be used to set all three variables to the same value +while `SET I=J+K=1` would initialize the value of `K` to 1 as well as +set `I` to `J+1`. Expressions used with the SET command do not need to +contain replacement operators: the command `SET FIN()` could be used, +for instance, to input a single character. The value of the function +would not be saved, however; this is sometimes useful when calling 'I/O' +functions for their 'side-effects'. + +Note that the word `SET` (or its abbreviation `S`) is not optional as it +is in some other languages. The flexible syntax employed by UWF makes it +mandatory that every command begin with a command letter. One SET +command, however, will process as many expressions as can fit on a +single line. The expressions should be separated by commas or spaces; +for instance: + + SET A=1,B=2,C=A+B + +which is equivalent to: + + SET C=(A=1)+B=2 + +Another point to remember is that the same variable may appear on both +sides of an `=` sign. Thus `SET X=X+5` has the effect of redefining the +value of `X` to be 5 more than the initial value. This can get to be +tricky if the same variable appears several times in a single expression +on both sides of replacement operators. The rule here is that in each +instance the variable will have its current value until the entire +expression to the *right* has been evaluated; then it will be replaced +with the new value. To give a fairly simple yet intriguing example: + + SET A=B+A-B=A + +which is equivalent to: + + SET C=B+A,B=A,A=C-B + +This will interchange the values of `A` and `B`. Another expression +which does the same thing is: `SET A=B+0*B=A`. Notice that the +processing of this expression involves two different values of `B`: The +first time `B` is encountered it is on the right side of an `=`, so its +current value is used; the second time it is on the left side, so the +rest of the expression is evaluated, the substitution made, and then +processing of the first part is resumed. Thus `A` retains its original +value until the very end (when it is replaced by the initial value of +`B`, which was saved on the stack). + + +### `ZERO` + +The special case of 'SET Var=0' is conveniently handled by the `ZERO` +command. A single `ZERO` command may be used to set several variables to +zero, making it very convenient for initializing sums and 'flags': `ZERO +A,#,C` will set those three variables to zero. As a special case, if no +variables are specified, the `ZERO` command clears the entire symbol +table. This effectively sets *all* the variables to zero since this is +the default value for 'undefined' quantities. + +One other use of the `ZERO` command should be mentioned. When the Symbol +Table fills up, UWF tries to replace any variables which have the value +'0' with new variables. This procedure succeeds as long as there is at +least 1 variable with this value, since that one will simply be renamed, +and no matter what the name, it will always be zero. As a result of this +scheme, programmers may regain symbol table space by `ZERO`ing unneeded +variables when they are finished with them. + + +### `YNCREMENT` + +Another special case of the `SET` command — `SET Var = Var + 1` is +handled by the `YNCREMENT` command. This command allows a list of +variables to be either incremented or decremented by the value '1'. The +command `Y K`, for example, is equivalent to `SET K=K+1` while `Y -J` is +the same as `SET J=J-1`. Of course commands such as `Y N,O-P` are +permitted; this one increments the variables `N` and `O` and decrements +`P`. Either commas, spaces or minus signs may be used to separate the +variable names. + + +### `XECUTE` + +The `XECUTE` command has been included for compatibility with earlier +versions of UWF. Its purpose was to evaluate arithmetic expressions +without setting useless 'dummy' variables. This is now accomplished by +the `SET` command itself simply by omitting the replacement operator. +Thus `SET FOUT(7)` may be used to ring the bell on the terminal. +Internally, `SET` and `XECUTE` are identical; it is recommended that +`SET` be used in new programs. + + +## Branch and Control Commands + +This class of commands is used to test arithmetic results, set up loops +and otherwise control the sequence of command execution. There are 11 +commands in this category — UWF has a very rich control structure built +around two fundamentally different types of transfers: the `GOTO` branch +and the `DO` call. Both interrupt the normal sequence of command +execution, but the `GOTO` is an unconditional branch while a `DO` call +eventually returns to the next command following the call. The `DO` +command is similar to the `GOSUB` in BASIC, but is considerably more +flexible. + + +### `GOTO` + +This command has the form `GOTO line number`. It causes an immediate +transfer to the line specified. The `GO` command is the usual way of +starting the indirect program at the lowest numbered line; it may be +used to start the program at any other line as well: `G 2.1` will start +at line '2.1'. An explicit line number may be replaced by an arithmetic +expression to create what FORTRAN calls an 'Assigned Goto': `SET X=5.1 . +. . GOTO X`. + + +### `DO` + +The `DO` command is effectively a subroutine call. A `DO` command +without a modifier (or equivalently, a `DO 0` command) calls the entire +stored program. This may be used as a Direct Command in cases where you +wish to follow such action with additional commands, e.g. `DO;TYPE +FTIM()` might be used to check the running time of a benchmark program. + +`DO` also accepts a list of line and group numbers such as `DO +-.7,8,9.1`, which would call the subroutine starting at line XX.70 in +the current group, then Group 8, and finally line 9.1. `DO` is +completely recursive: a DO may thus 'do' itself! Note that the commands +called by a DO are not designated anywhere as subroutines — they may be, +and usually are, just ordinary commands somewhere in the main program. +This is one of the major differences between `DO` calls in UWF and +`GOSUB`s in BASIC. Suppose, for example, that the program had a line +such as: + + 1.3 ZERO A,B,C; SET D=5, E=6 + +which occurred in Group 1 as part of an initialization sequence. If the +same set of commands were needed later in Group 12, one would only need +to write `DO 1.3`. This facility for re-using common parts of the +program is akin to writing 'macros' and is generally considered to be a +good programming practice. The one feature missing from the `DO` command +is the ability to explicitly pass arguments to the 'subroutine'; this +must be handled by the use of 'common' variables. As you will see later +on, [Program Defined Function calls](#pdfs) provide this capability in a +somewhat limited form. + +A DO call may be terminated in one of four ways: + +1. There are no more lines to execute in the range specified +2. A `RETURN` command is encountered +3. A loop containing a `DO` is terminated by a `NEXT` or `BREAK` +4. A `GOTO` transfers to a line outside the range of the `DO` + +The first condition is the most common, especially for single line +calls. The second condition is explained below, while the third is +explored in the discussion of the `NEXT` command. That leaves only the +fourth possibility: `GOTO` branches can be used to terminate a `DO` call +simply by transferring to a line outside of the range; however the line +transferred to will be executed first, which can lead to slightly +unexpected results. For instance, if the line branched to happens to +immediately precede the group, no exit will occur because UWF will find +itself back in the proper group again when it finishes the line. Another +somewhat similar case occurs when calling a 'sub-group': `GOTO` +transfers anywhere in the same group will be honored without causing a +return. Thus if you wish to force a return from a `DO` call, do it with +the [`RETURN` command](#return), not with a `GOTO`. + + +### `RETURN` + +The `RETURN` command provides a way to selectively exit from a `DO` call +in cases where the entire subroutine is not required. Since a `DO` call +always specifies the implied range of the subroutine (a single line or +an entire group), a `RETURN` command is normally not required. There are +cases, however, especially when calling a 'sub-group', in which a +`RETURN` is necessary to force an early exit. If there is no subroutine +call to return from, `RETURN` will go back to command mode instead, i.e. +it behaves just like a `QUIT` command. This is a useful feature, since +programs which end with a `RETURN` can be run normally, but can also be +called as subroutines via the [`LINK` command](#link). + +`RETURN` can also designate a line number, for example: `RETURN 5.3`. In +this case the normal return to the calling point is aborted (except for +[PDF calls](#pdfs)) and the program continues from the line specified. +This is a very important feature since it effectively transforms a `DO` +call into a `GOTO` branch. It is all the more useful since it can be +'turned on and off simply by making the return point an arithmetic +expression which, when zero, indicates a normal return, but otherwise +causes a branch to the line specified. This gives UWF a 'multiple +return' feature which is found in only a few high-level languages. + + +### `IF` + +The form of the `IF` command is: + + IF (Arithmetic Expression) negative, zero, positive + +where 'negative', 'zero', and 'positive' are line number expressions not +containing commas. Depending upon the sign of the value being tested, +the program will perform a `GOTO` branch to one of the three +possibilities. The expression being tested must be enclosed in +parentheses and must be separated from the command word by a space. + +Not all of the branch options need to be specified, and relative line +numbers are especially useful for those which are. Here are some +examples of `IF` commands: + +| Example | Meaning | +| --------------------------- | ---------------------------------------- | +| `IF (D=B^2-4*A*C) .2,.3,.4` | Tests for all 3 possibilities | +| `IF (A-5) 5.1, 5.1` | Branches if A is less than or equal to 5 | +| `IF (-X) .9 or IF (X),,.9` | Branches if X is greater than 0 | +| `IF [I-J] , .2` | Branches only if I equals J | +| `IF .4,,.4` | Branches only if W is non-zero | + +These examples illustrate the flexible nature of the `IF` command. In +commands with only 1 or 2 branch options, if the branch is *not* taken, +the next sequential command will be executed — whether this command is +on the same line or on the next line unless the `IF` is in a [`FOR` +loop](#for). Here, then, is a case where 'line 0' is interpreted as the +'next command'. Also note (example 1 above) that the expression being +tested may contain replacement operators so that the value may be saved +for use elsewhere in the program. + + +### `ON` + +The `ON` command is identical in form to the `IF` command: `ON (exp) +N,Z,P`. The difference is that `DO` calls are used in place of `GOTO` +transfers, so upon completion of the subroutine, the program will +continue with the next command following the `ON` test.² This is often a +very convenient thing to do since it allows additional processing for +specific cases. As with the `IF` command, not all 3 calls need to be +specified, so one can test just for equality (zero), or for some other +condition. Notice that an entire group can be called by the `ON` +command. + + +### `JUMP` + +The `JUMP` command has two distinct forms which have been designed to +serve the needs of interactive programs: + + JUMP line number + +or: + + JUMP (expression) SI, S2, S3, S4, S5, . . . + +The first form is a conditional `GOTO` in which the branch is taken +*unless* there is a character waiting in the input buffer. This form is +used to test the keyboard for input without interrupting the program if +there isn't any. This feature is essential in interactive programs which +allow program flow to be controlled dynamically from operator response. +For example: + + 1.1 YNCR I; JUMP .1; TYPE I + +will hang in a loop incrementing the variable `I` until a key is struck, +then type the number of cycles. The character used to interrupt the +program can be read with the [`FIN` function](#fin) and so used to +further control program flow. If the example above simply called `FIN` +to read the character directly, the program would hang in the input wait +loop and nothing further could be accomplished until the operator struck +a key. + +The second form of the `JUMP` command provides a computed subroutine +(`DO`) call which is essentially similar in form to the `ON` command +except that the actual *value* of the arithmetic expression being tested +is used (rather than just the *sign* bit) to determine which subroutine +to call. The call list is indexed from 1 to N, and any number of +subroutines may be specified. Values of the expression which do not +match up with a specified call are ignored. In the example shown above, +Subroutine No. 4 will be called if the expression has the value 4.5, +whereas if the expression has the value -1, 0, or 12.3, no subroutine at +all will be called. As with the `IF` and `ON` commands, line numbers may +be omitted (or set to zero) to avoid a call for certain values of the +expression. + +Typically the expression is simply the ASCII value of a keyboard +character which is used to select an appropriate subroutine. For +example: + + JUMP (FIN()-'@> A,B,C,,E + +will call subroutine `A` if the letter `A` is typed, etc. Notice that +typing the letter `D` is treated as a `NOP` by this particular command. +As with the `ON` command, the program normally continues with the next +sequential command following the subroutine call unless a `RETURN` +command is employed to transfer elsewhere. + + +### `LINK` + +The `LINK` command allows systems with more than 12K to call subroutines +stored in different text 'areas',³ thus 'linking' such areas together as +part of a 'main' program. The command has the form: + + LINK Area, Subroutine Pointer + +where 'Area' may have the values '0' or '1' in a 16K system, and up to +'5' if sufficient memory is available. The 'Subroutine Pointer' is a +line or group (or sub-group) number as described for the `DO`, `ON` and +`JUMP` commands. A value of '0' specifies that the entire area is to be +used as a subroutine. Examples: + +| Command | Meaning | +| --------------: | ----------------------------------------------- | +| `L,4` | Calls group 4 in Area 0 | +| `L 1,-8.5` | Calls sub-group starting at line 8.5 in Area 1 | +| `L,.3` | Calls line XX.30 in the same group in Area 0 | +| `L 2,;T "DONE"` | Executes all of Area 2, then types `DONE` | + +Notice that the comma is required punctuation even when the second +parameter is zero, as in the last example.⁴ To avoid returning to the +calling area at the end of the subroutine, use a RETURN command with a +non-zero line number, such as `R .9` to abort the normal return +sequence. By using a computed line number in such a command the calling +program can control the return. A [`QUIT` command](#quit) can also be +used to cancel all returns. + +The variables created or used by a program in one area are shared by all +areas, so be careful to avoid conflicts. Also, since each `LINK` saves +its return on the 'stack', watch out for calls which never return, but +simply chain from one area to another. This will eventually lead to a +'stack overflow' which can be cured by using a `QUIT X` command to +cancel all pending returns. + +The `LINK` command functions properly for calls from within the same +area, but the `DO` command is clearly preferable since, for one thing, +it can handle multiple calls which the `LINK` command cannot. `LINK` can +be used in direct commands; it is somewhat similar to the `LIBRARY +GOSUB` command in the OS/8 version. + + +### `QUIT` + +The QUIT command stops program execution and resets the 'stack' pointers +so that all pending operations (such as subroutine returns) are +destroyed. CTRL/F as well as any execution error performs an effective +`QUIT`, thereby returning to command mode. There are rare occasions, +however, when it is desirable to be able to 'quit' and then simulate a +keyboard restart so that the program will continue running without +actually returning to command mode. This is accomplished by specifying a +non-zero line number as a 'restart' point. Thus, `QUIT 1.1` will stop +execution, clear the stacks, and then restart at line 1.1. To restart at +the lowest numbered line of the program, use a `Q .001` command. `QUIT +0` or just `Q` will stop the program and return to command mode. + +It is also possible to use `QUIT` to specify a restart point for any +error condition. This is accomplished by specifying a *negative* line +number, i.e. something like `QUIT -9.1`. This command will not stop the +program when it is executed; it will merely remember the line number and +then continue with the next command. If an error subsequently occurs, +however, the program will be automatically restarted at line 9.1 instead +of returning to command mode. + +This provides UWF with a somewhat limited error recovery procedure, but +one which can be used to take care of certain 'unexpected' conditions +which might develop while the program was running unattended, Note that +it is up to the user to determine which error caused the restart. One +way that this could be accomplished is to select different restart +points for different sections of the program where specific errors might +be expected. This feature should be considered somewhat 'experimental' +in the sense that it may not be included in later releases of UWF if +other features appear to be more important. + +The error trap is automatically reset every time UWF returns to command +mode in order to prevent conditions set by one program from causing an +unexpected restart at a later time. + + +## Loop Commands + +UWF has 3 commands for constructing program loops. The `FOR` command +sets up the loop, the `NEXT` command serves as an optional terminator, +and the `BREAK` command provides a way to exit from a loop before it +runs to completion. UWF's loops are slightly different from those in +other languages, but the differences, once recognized, are easy to +accommodate. Basically, UWF uses 'horizontal' loops which consist of +only the statements following the `FOR` command on the same line. Most +other algebraic languages use 'vertical' loops which consist of a number +of contiguous program steps with some way to designate the end of the +loop. + +UWF's approach is convenient for short loops since the commands to be +repeated are simply coded on the same line with the `FOR` command and no +'end-of-loop' designation is required. Loops which require several lines +of code are handled just as easily by putting a `DO` command in the main +loop to call all the statements which cannot be placed on the same line. +A `NEXT` command at the end of those statements then serves to designate +both the end of the loop as well as the continuation point of the +program. Symbolically, UWF's loops may thus have either of these two +forms: + + FOR * * *; loop commands + +or: + + FOR * * *; DO -.YY + XX.YY first loop command + second loop command + . . . + last loop command; NEXT + +The latter form is practically identical to that used by BASIC or +FORTRAN, with the mere addition of the `DO` call on the first line. + + +### `FOR` + +This command initializes a loop, assigning a 'loop variable' to count +the number of iterations. The form is: + + FOR Var = Initial Value, Increment, Final Value ; + +or, more generally: + + FOR expression 1, expression 2, expression 3 ; + +where the first variable to the left of a replacement operator in +expression 1 will be used as the loop counter. The semicolon after +expression 3 is required punctuation. An increment of +1 is assumed if +only the initial and final values are given. Notice that the increment, +if specified, is the *second* expression! This is different from the +convention used by BASIC and FORTRAN. There are no restrictions on any +of the expressions: they may be positive, negative or non-integer. Thus +one can increment either 'forward' or 'backward', using any step size. +The execution of the FOR command is such, however, that one pass will +always occur even if the 'initial value' is greater than the 'final +value'. In any case, the exit value of the loop variable will be one +increment more than the value it had in the final iteration. + +Here are some examples: + +1. `FOR I=J=1,10;` +2. `FOR L=1,N; FOR M=-L,L;` +3. `FOR X(I)= 10, -1, 1;` +4. `FOR A=0, PI/180, 2*PI;` +5. `FOR Q= P/2, Q, 5*Q;` + +Notice that loops may contain other loops (Ex. 2). Such 'nesting' is +permitted to a depth of 15 or so, but in practice, loops are rarely +nested more than 5 deep. Another point, illustrated in example 5, is +that the initial value of the loop variable can be used by the +expression for the increment and the final values; also notice that +subscripted variables are permissible as loop indices (Ex. 3). + +In example 1, it may appear that both `I` and `J` will be used as +control variables. This is not the case: only the first variable (in +this case `I`) will be incremented. Other variables (such as `J`) may be +given values by replacement operators in any of the three expressions, +but these values will not change during the loop (unless commands within +the loop change them). It is often quite convenient to use the `FOR` +command to initialize several variables used in the loop along with the +value of the loop index. + +The novice programmer who wishes to try writing a simple loop might +begin with the following direct command: + + FOR I=1,10;TYPE I,I^2,! + +which will print out the first 10 squares in some (unspecified) format. +The more experienced programmer will quickly appreciate UWF's loop +structure; for one thing, no rules regarding branches into the middle of +a loop are necessary since there is no way to branch to the middle of a +line! + + +### `NEXT` + +The normal termination for a loop is at the end of the line containing +the `FOR` command. If the loop contains a `GOTO` branch, however, the +end of the line branched to becomes the terminator. It is convenient at +times, especially in direct commands, to terminate the loop in the +middle of a line so that other commands which logically follow the loop +can be placed on the same line. The `NEXT` command serves this purpose, +as shown in the following example: + + FOR * * *; loop commands; NEXT; other commands + +which excludes 'other commands' from the loop. + +This construction also works in 'vertical' loops: + + FOR * * *; DO -.# + # commands + commands + NEXT + +The commands executed by the `DO` will be terminated upon encountering +the `NEXT` command. But more importantly, when the loop is finished, UWF +will continue with the first command following the `NEXT`, thus skipping +over the commands in the body of the loop. If this `NEXT` command were +to be omitted or replaced by a `RETURN`, the program would simply 'fall +through' to the first statement in the loop. (The one indicated by `#` +in the example above.) + +Notice that the `NEXT` command contains no references to the loop +variable. This is a little different from the way most versions of BASIC +implement this command, but the effect is quite similar and since only +the first letter of the command word is decoded, variations such as `NI` +or `NEXT-J` may prove helpful to some programmers. Nested loops, of +course, may require 'nested `NEXT`s': `N;N`. Here is an example which +types out all the elements of a 5×5 array a row at a time with a CR/LF +printed at the end of each row: + + FOR I=1,5; FOR J=1,5; TYPE A(I,J); NEXT; TYPE ! + +`NEXT` has one other feature: it may be used with a line number to +specify a continuation point other than the next sequential command. +Thus: `FOR * * *; commands; NEXT .8` will branch to line XX.80 when the +loop runs to completion. + +**Note 1:** A `NEXT` command which is executed outside of a FOR loop is +ignored unless it specifies a line number, in which case the branch will +always be taken. A `NEXT` command may thus be placed at the beginning of +any line and used as a target for a 'do nothing' branch from within a +loop without affecting the normal execution of that line. + +**Note 2:** Loops which contain conditional branches (i.e. `IF` +commands) should be careful that all paths end with an appropriate +`NEXT` if it is desired to skip over the statements in the loop under +all conditions. Whichever `NEXT` is executed on the final iteration will +determine the program flow. + + +### `BREAK` + +Once a loop has been initiated it must normally run to completion. +Branching to a line outside of the loop is not effective: that line will +simply be treated as a continuation of the main loop. (See +[above](#next) comments about `GOTO`s in a loop.) One way to force an +exit from a loop would be to set the loop variable to a value greater +than the final value. This is obviously not very 'elegant', to say the +least, so the `BREAK` command has been provided to solve this +difficulty. A `BREAK` causes an immediate exit from the loop, preserving +the current value of the loop index, and the program then continues with +the next sequential command following the `BREAK`. As you might expect, +`BREAK` may also specify a line number so you can branch to a different +part of the program at the same time that you leave the loop. A `BREAK` +without a line number is ignored (just like the `NEXT` command) if it is +encountered outside of a loop, so lines containing `BREAK`s can be used +by other parts of the program. Each `BREAK` exits from only a single +loop, so to exit from nested loops it would be necessary to use multiple +`BREAK` commands: `B;B 15.1` will exit from 2 loops and then transfer to +line 15.1. + + +## Miscellaneous Commands + +We will now finish the alphabet: `C`, `H`, `K`, `P`, `U`, and `V` are +the remaining command letters. `U` and `V` are not implemented in this +version and may be used for whatever purpose the user desires. The '@' +command is also available for expansion purposes. + + +### `COMMENT` + +Any command beginning with a `C` causes the rest of the line to be +ignored. Such lines may thus be used for comments describing the +operation of the program. In particular, line XX.00 (the first line in a +group) should generally be reserved for comments. Branching to a comment +line from within a loop will terminate that cycle of the loop. In this +way a `COMMENT` is equivalent to the Fortran `CONTINUE` statement. A +`NEXT` command performs the same function and in addition may be used to +designate the continuation of the program. + + +### `HESITATE` + +The `HESITATE` command delays the program for a time specified by the +command. The argument (which must be an integer) is nominally in +milliseconds, so `H 1000` will generate approximately a 1 second delay. +However, the exact delay is directly dependent upon the cycle time of +the machine, so some calibration is necessary. Here is an example using +the `H` command: + + FOR T=1,9; TYPE "*"; HESITATE 250*T + + +### `PUNCH` + +The `PUNCH` command allows the programmer to save a copy of the text +buffer and the symbol table in binary format, ready to reload with the +standard BIN loader. This command requires either a High Speed punch or +an audio (cassette) recorder. Tapes created with the `PUNCH` command can +only be read with the BIN loader since they are not punched as ASCII +characters. The advantage to punching tapes in this format is that they +tend to be somewhat shorter than ASCII tapes and they also contain a +checksum so there is less probability of an error going undetected. The +disadvantage, however, is that they are absolute memory dumps and so are +not necessarily transportable between different versions of UWF. They +also cannot be loaded by UWF itself from a remote location, but require +access to the front panel of the computer in order to activate the BIN +loader as well as to restart UWF once the tape is loaded. + +To use this command (assuming that you have a cassette recorder, but the +same procedure applies to a HS paper tape punch), advance the tape to an +unused area, turn on the recorder and then type `P` followed by a +RETURN. Approximately 5 seconds of leader code will be +punched, followed by the contents of the text buffer and then the symbol +table. To restore a program (and any symbols in use at the time it was +dumped), position the tape at the start of the leader, and while this +section is being read, start the BIN loader from the front panel. If you +start the loader before reaching the leader section, the computer will +halt with a checksum error (`AC` not zero); hit the `CONTINUE` switch +quickly, and if you are still in the leader, all will be well. After +reading in the program tape you must manually [restart UWF at location +100](#starting). + +The `PUNCH` command always returns to command mode (like `MODIFY` and +`ERASE`) so it cannot be followed by other commands, and should not be +included in the program itself. In systems with more than 12K the +`PUNCH` command will dump the contents of the *current* program area, so +to save the program in Area 3, for example, use a `L 3` command to get +to it and then type `P` to punch it out. A program can only be reloaded +into the area that it came from, so if you wish to move a program to a +different area, you must `WRITE` it out (rather than `PUNCH`ing it), and +then read it in again as explained [below](#write-ascii-tape). + + +### The `OPEN` Commands + +In addition to the `PUNCH` command described above, UWF has a series of +`OPEN` commands which allow `ASK` and `TYPE` (or other I/O operations) +to use something other than the terminal. These commands consists of +*two* words (or two single-letter abbreviations) separated by a space. +You may recall that the letter `O` has already been used for the `ON` +command and wonder how it could also be used for `OPEN`. `OPEN` and `ON` +can be distinguished, however, since `ON` must always be followed by an +arithmetic expression. Here is a short summary of the `OPEN` commands +currently available. The mnemonics, which were chosen in part to be +compatible with the OS/8 version, are somewhat less than perfect! + +| Mnemonic | Command | Description | +| -------------- | ------- | ----------------------------------------- | +| OPEN INPUT | `O I` | Selects the Terminal as the Input device | +| OPEN OUTPUT | `O O` | Selects the Terminal as the Output device | +| OPEN READER | `O R` | Selects the High Speed Reader for Input | +| OPEN PUNCH | `O P` | Selects the High Speed Punch for Output | +| OUTPUT TRAILER | `O T` | Punches leader/trailer code (ASCII 200) | +| OPEN ----,ECHO | `O -,E` | Connects the Input device to the Output | + +Only the first two commands (`O I` and `O O`) and the ECHO option are +useful unless you have a high speed reader/punch (or an audio tape +recorder). The list of `OPEN` commands could also be expanded to include +things like `O L` (for selecting a line printer) or `O S` to send output +to a 'scope display, etc. Such expansion is, however, entirely up to the +user. + + +### I/O Device Selection + +The Input and Output devices are always reset to the terminal when you +hit CTRL/F. To select a different device, use the appropriate +`OPEN` command. For example, to read in a program from the High Speed +Reader, simply type in an `O R` command, and henceforth, until this +assignment is changed, all input to UWF will come from the reader rather +than from the keyboard. In particular, even direct commands will be +taken from the reader, so you can set up a program tape to run the +machine while you are gone. Also, if the tape contains a listing of a +program it will be read into the text buffer just as though you were +typing it in yourself. This is an alternative method for saving programs +which has the advantage that they are available as ASCII tapes which can +be edited or processed by other programs. A 'time-out' trap in the +reader routine normally senses when the end of the tape has been reached +and then restores the terminal as the input device. A +BACKARROW or UNDERLINE is printed on the terminal +to indicate that it is the active input (and output) device once more. +If you need to manually restore the terminal to its usual status, just +hit CTRL/F. + + + +Similarly, to select the High Speed Punch (or Cassette Recorder) for use +as the output device, just use an `O P` command. To dump the text buffer +on tape, for example, enter the commands: + + O P,T; W; O T,O (do not hit RETURN) + +and then start the punch or recorder. Hit RETURN and then +wait for the asterisk (`*`) to reappear on the terminal. + +To re-read such a tape at a later time, position it in the reader +somewhere in the leader section, use the `ERASE` command to clear the +program area, and then type `O R` followed by the RETURN key. +If input is from a paper tape reader, the reader will now begin to read +the tape. If input is from an audio recorder you should actually start +the tape moving (in the leader section) before hitting the +RETURN key, otherwise the first few characters are likely to +be 'garbage' as the tape comes up to speed and UWF may well conclude +that you have run out of the tape before you have even begun! + +It is also possible to use the reader/punch for data storage purposes. +This works best with paper tape since the audio recorder lacks a +'stop-on-character' capability, making it difficult for UWF to keep up +with the data once the tape has started moving. By way of an example, +the following command will read in 50 numbers from the high-speed +reader: + + O R; FOR 1=1,50; ASK DATA(I); NEXT; O I,E + +Notice that an `O I,E` command is used at the end of the loop to restore +input to the keyboard. If this command were omitted the H.S. reader +would continue to be used for input, probably causing an error to occur +since it is unlikely that the next data value on the tape would +correspond to anything expected from the keyboard. The `,E` part of this +command is explained more fully in the next section. + + +### The ECHO Option + +The `,E` option may be added to either an `O I` or `O R` command to +specify that the input characters are to be 'echoed' to the output +device. Generally this option is *always* used with `O I` and *never* +used with `O R`. The echo option may at first appear slightly confusing +since UWF normally runs with the keyboard echo *on* and thus one comes +to expect that whatever is typed will be printed on the terminal. This +makes the terminal appear much like a simple typewriter and tends to +obscure the fact that if UWF were not sending back each character it +received, *nothing* would be printed! The `ECHO` option must be +specified when selecting the input device, or *no echo* will be +assumed. Thus an `O I` command will select the keyboard for input (it +may already *be* selected) and effectively turn the echo off. An `O I,E` +command is necessary to restore the echo under program control. Of +course any program error or typing CTRL/F, will also restore the echo. + +The ability to disable the input echo is convenient at times since it +allows a program to read one thing and possibly print something else. An +example of this mode of operation occurs during command input: when you +type the RUBOUT key you do not get this character printed, +but rather a backslash (`\`), or on a video terminal, a three character +sequence: Backspace, Space, Backspace, +which effectively removes the character from the screen. UWF programs +can also be written to use the keyboard for program control, and in such +cases it is often desirable to have 'silent' input. You can try this out +quickly by using a direct `O I` command to disable the echo. Now type in +O, Space, I, Comma +E and hit RETURN and the echo will return again. + +Another time when you will want to disable the echo is when reading in a +program tape on the 'low-speed' reader. If you turn off the echo in this +case you can avoid getting an unwanted listing while you relax to the +rhythm of a quiet little 'burp, burp, burp' instead of a 'clackety clack +clack'. Just hit CTRL/F at the end of the tape to turn on the +echo again. + +Similarly, when reading a data tape from the high-speed reader it is +generally undesirable to have it all printed on the terminal. Thus the +`O R` command automatically disables the echo; but if you wanted to see +what some of the data looked like, you could use an `O R,E` command. To +make a copy of a program or data tape you would first switch output to +the punch and then turn on the echo to 'print' each character received +on the output tape, e.g. + + O P;O R,E;S FIND() + +The [`FIND` function](#find) keeps reading from the input device, +looking for the character code specified. In this case a 'null' was +used, which will never be found, so the effect of this command is to +continue reading until the end of the tape is reached at which point the +terminal will automatically be restored as the I/O device with the echo +enabled. If only portions of a tape were to be copied, you could use the +`FIND` function to search for an appropriate character and then switch +I/O back to the terminal yourself. You can use the `ECHO` option to skip +sections of the tape by disabling the echo until you 'find' the right +character and then turning it back on to copy some more. + + +### The Leader/Trailer Option + +The `T` option punches leader/trailer code (ASCII 200). This is +convenient (but not essential) for separating output on paper tape, and +somewhat more important when using an audio recorder since there is no +visual indication of breaks in the data. Blank tape may also be used as +'leader' and both are ignored each time the reader is selected as the +input device. However, after the first valid input character has been +read these same codes are interpreted as the 'end-of-tape' and cause +both input and output to be restored to the terminal. A +BACKARROW or UNDERLINE is also printed to indicate +that the EOT was detected. This character serves the dual purpose of +also removing any 'garbage' characters which might have been read after +the last valid input. + +The `T` option can be used alone (`O T`) or in conjunction with another +`OPEN` command. + +The number of L/T codes punched is determined by an optional arithmetic +expression following the letter `T` (and separated by a space from it) +with the previous specification being used as the default. The initial +value is 512, which is about right for use with an audio recorder, but +somewhat ridiculous for paper tape. (Over 4 feet of leader!) A value of +70 or so is more appropriate in this case. You can always just repeat +the `T` option to get a slightly longer leader if you want to: +`O T 100,T` will punch out 200 L/T codes but leave the default set at +100. Notice how this option was used in the [example +above](#write-ascii-tape) for writing out all of the program buffer. The +length specified by the `T` option is also used by the [`PUNCH` +command](#punch). + + +### `KONTROL` + +This is an optional command which may be used to program the DR8-EA +parallel I/O module. The `K` command is used to set and clear individual +bits in the output register while the [`FDIN` function](#fdin) is used +to read the corresponding bits in the input register. These options are +added by the initialization routine if [Switch 7 is *UP*](#opt-switch). + +The `KONTROL` command uses *positive* numbers to turn bits on and +*negative* numbers to turn them off. Each bit is directly controllable, +independent of the setting of any of the others. Thus a `K 1` command, +for example, will turn on bit '1' without changing the state of any of +the other 11 bits, while a `K -1` command will turn it off again. In +order for this scheme to work successfully, the bits must be numbered +from 1-12 rather than from 1-11, which is the usual convention. This is +because '-0' is not distinguishable from '+0'. In fact, '0' is +interpreted to mean 'clear all bits', so a `K 0` command (or just `K` +since '0' is the default for all arithmetic expressions) can be used to +quickly initialize this register. + +More than one bit position can be set at a time, e.g. a command such as: + + K 1,-2,3 + +will set bit 1, clear bit 2, and finally set bit 3. + +In this form, each operation occurs sequentially with perhaps 10 +milliseconds or so between operations. This allows a command such as +`K 1,-1` to be used to generate a short pulse on line 1. If it is +necessary for several signals to occur simultaneously, those operations +can be enclosed in parentheses: + + K 1,(2,3,A),-1 + +will set bit 1, then bits 2, 3, and 4, then clear bit 1. + +Since for some purposes it is more convenient to be able to specify +various bit combinations with a single arithmetic expression rather than +setting and clearing each bit individually, a third mode of operation is +also available. In this mode, the last 4 bits (bits 9-12) are set to the +value of an expression preceded by an `=` sign. The remaining 8 bits are +not changed. Thus a `K,=5` command would first clear all bits — the +comma indicates a missing argument which is the same as '0' — then set +bits 10 and 12 while clearing bits 9 and 11 (which were already clear in +this case). + +To summarize the 3 different forms of the `KONTROL` command: + +| Command | Meaning | +| ------------ | ----------------------------------------------------------------------------------------------------------- | +| `K N,-N` | Turns a single bit on or off; N=0 turns *all* bits off | +| `K (L,M,-N)` | Performs all operations in parentheses simultaneously instead of sequentially | +| `K =N` | Sets the 4 least-significant bits to the binary value of `N`; this form may not be used inside parentheses. | + + +## Error Messages + +UWF traps any 'illegal' operation such as division by zero or an unknown +command and prints a peculiar little message to tell you what the +problem was and where in the program it occurred. If you type in the +command: `SET 2=3` for example, UWF will reply with: + + '?07.44' + +which is its way of telling you that you have something besides a +variable on the left side of an `=` sign. To decode the error message, +you should look at the [error code table](#error-codes) below (or the +Summary card) which lists all of the error diagnostics and their +probable cause. + +If this same error had occurred while the program was running (i.e. not +from a direct command), the error message would also indicate the line +in the program containing the erroneous statement: + + ?07.44 @ 15.13 + +indicates 'operator missing or illegal use of an equal sign' in line +15.13. + +The program `QUIT`s whenever an error occurs, thus all pending +operations are canceled, and in general it is impossible to resume +*precisely* at the point of interruption, but it is often possible to +make the necessary changes, perhaps update a few variables with direct +commands, and then restart from a point close to where the error +occurred. + +This version also has an 'auto-restart' feature which allows the program +to continue after an error instead of returning to command mode. This +feature is selected by [an option in the `QUIT` command](#quit). + + +## The `TRACE` Feature + +To further assist in finding the source of an error, UWF has a facility +for printing out each program step as it tries to execute it. Thus, when +an error occurs you can see exactly where the problem is. The 'trace' +feature is turned on by the occurrence of a `?` in the program (not one +which is preceded by a single quote or enclosed in double quotes, +however) and turned off again by another `?`. Thus only the portion of +the program between pairs of question marks will be output while the +program is running. The `?` may be given in a direct command, so to +trace the entire program, just use a `GO?` command to start it. +Similarly, a `DO 5.2?` command will selectively trace that one line. + +As a further aid to finding logical errors (as opposed to simple +programing mistakes), when the trace is on, UWF will print out the +result of all expressions appearing in `SET` commands. Thus you can see +the values of all the variables as well as the program steps which +created those values. A video terminal is obviously preferable for +program traces since rather voluminous output can be generated in quite +a short time. + +A somewhat secondary use of the `TRACE` feature is for simplified +input/output prompting. Whenever variables have names closely resembling +their usage, it is a bit of a waste to have commands such as: + + ASK "AGE? "AGE + +or + + TYPE "COST="COST + +when, with only a small sacrifice in the punctuation, the following will +do just as well: + + ASK ?AGE ? + +or + + TYPE ?COST? + +UWF will print out just the characters enclosed by the `?`s. For this +reason it is preferable to use 'spaces' as separators rather than +'commas', i.e. + + ASK ?A B(I) C(J,K) ? + +will print out each variable name followed by a space and then wait for +its value to be input. One small disadvantage to this 'trick' is that +when such statements are *actually* being traced, the text enclosed by +`?` marks will *not* be printed due to the 'toggling' nature of the +trace switch. + +There is one other small anomaly associated with the trace feature: A +command such as `SET !=5,$=10` will not set those two 'secret variables' +when it is traced, but will instead first perform a CR/LF and then dump +the symbol table! This is because during a program trace all `SET` +commands are treated internally as though they were `TYPE`s and hence +the secret variables take on their special roles as operators. There is +a simple solution to this problem, however, and that is to simply prefix +a `+` sign or otherwise embed such variables in the midst of an +arithmetic expression so that they are no longer recognized as +`ASK`/`TYPE` operators. Thus the command `SET +!=5,+$=10` would be +traced properly. + + +## Command Summary + +The following table provides a quick review of UWF's entire command +repertoire. + +| Command | Form | Example | +| ------------------ | ------------------------------------------------ | -------------------- | +| `@` | a not implemented in this version |  | +| `ASK` | list of variables, "prompts", formatting options | `A X,Y(I),Z(J,K)` | +| `BREAK` | line number | `B` or `B 11.45` | +| `COMMENT` | your programs whenever possible | `C FOR COMMENTS` | +| `DO` | list of lines, groups, or sub-groups | `D .7, -9.5, 10` | +| `ERASE` | line, group, sub-group, or 'all' | `E 5 or E 9.1` | +| `FOR` | var = start, increment, finish | `F I=1,5;F J=I,-1,0` | +| `GOTO` | line number | `G 11.8 or G .3` | +| `HESTATE` | time delay desired | `H 1000` | +| `IF` | (arithmetic expression) negative, zero, positive | `I (K=I-J), .5` | +| `JUMP` | line number | `J .3;C WAIT LOOP` | +| `JUMP` | (arithmetic expression) one, two, three, four... | `J (N) 1, .2, -3.4` | +| `KONTROL` | bit positions | `K 1,(-1,2,3),=X` | +| `LOOK` | program area | `L 1` | +| `LOOK` | program area, subroutine pointer | `L 2,4.1 or L,10` | +| `MODIFY` | line number | `M 5.1` | +| `MOVE` | old line number, new line number | `M 3.3,6.3` | +| `NEXT` | line number | `F I=1,10;N;T PI` | +| `ON` | (arithmetic expression) negative, zero, positive | `O (A-5) -9.2, 9` | +| `PUNCH` | punches program and variables in binary format | `P` | +| `QUIT` | line number | `Q or Q 5.1` | +| `RETURN` | line number | `R or R .2` | +| `SET` | list of arithmetic expressions | `S A=5, B=C=A/2` | +| `TYPE` | arithmetic expressions, "labels", formatting | `T !?A ?:10"B="B` | +| `U` | available for user expansion |  | +| `V` | available for user expansion |  | +| `WRITE` | list of lines, groups, sub-groups, or 'all' | `W or W -1.5,2,3.1` | +| `XECUTE` | list of arithmetic expressions (same as `SET`) | `X FSIN(#)/FCOS(#)` | +| `YNCR` | list of variables | `Y I-J,K L` | +| `ZERO` | list of variables or 'all' | `Z,#,A,B(I),C(J,K)` | +| `OPEN INPUT, ECHO` | normal terminal input | `O I,E` | +| `OPEN READER` | selects high-speed reader | `O R` | +| `OPEN PUNCH` | selects high-speed punch | `O P` | +| `OPEN OUTPUT` | selects terminal for output | `O O` | +| `OUTPUT TRAILER` | punches leader/trailer code | `O T` | + + +## Internal Functions + +In spite of the fact that only about 3.3K words have been used to +implement UWF, there are nearly 20 built-in functions and a facility for +adding a limitless number of Program Defined Functions. + +The 'internal' functions provide the user with full-accuracy +('10-digit') approximations for commonly used relations such as log, +exponential, sine, cosine, square root, etc. Also included are simple +numerical functions such as absolute value, integer, sign and fractional +parts, maximum/minimum, etc. And finally, there are a few functions for +character processing and special I/O operations such as reading the +Switch Register and loading the MQ register. All function names in UWF +begin with the letter `F`; thus, variables names may not begin with this +letter. + + +### Transcendental Functions + +This class of functions, so named because the relations they represent +can only be expressed as infinite series, includes the natural log and +exponential functions and the three most common trigonometric functions. +The series approximations used by UWF have been optimized by a +constrained least-squares procedure to reduce the error over the +principal argument range to at worst a few parts in 10 billion. + +The transcendental functions can be removed if you wish to increase the +number of variables available in the 8K version. Removing them creates +space for another 55 variables — a total of 175 instead of only 120. +Program Defined Functions can be incorporated in their place at the +expense of greater execution time and slightly poorer accuracy. See [PDF +example 6](#pdf-transcendentals) and [the Patches section](#patches) +below for details. + + +#### `FLOG` + +`FLOG(X)` returns the natural logarithm of the absolute value of the +argument. An error occurs if `X` is zero since the theoretical result is +infinite. No error occurs if `X` is negative, although the log function +is, in fact, only defined for positive arguments. This implementation +facilitates the use of `FLOG` for extracting roots and raising values to +non-integer powers. The common (base-10) logarithm is easily obtained +from the `FLOG` function just by dividing by `FLOG(10)`. Example: + + *TYPE %,"NATURAL LN(PI)="FLOG(PI) :45"COMMON LOG(PI)="FLOG(PI)/FLOG(10)! + NATURAL LN(PI)= 1.144729886E+00 COMMON LOG(PI)= 4.971498727E-01 + + +#### `FEXP` + +`FEXP(x)` returns the value of *ex* where *e*=2.718281828... +The value of 'e' is always available as `FEXP(1)`. This function is +often used to extract roots and compute non-integer powers. For example, +X3.5 is found from the expression: `FEXP(3.5*FLOG(X))`. +Similarly, the cube root of 27 may be found from the expression: +`FEXP(FLOG(27)/3)`. The absolute value of the argument must be less than +approximately 1400 in order to avoid numeric overflow. + + +#### `FSIN`/`FCOS` + +`FSIN(A)` and `FCOS(A)` return the value of the sine and cosine of the +angle `A` when `A` is measured in *radians*. A radian is a unit of +angular measure preferred for scientific and engineering work because it +eliminates factors of π in many formulæ. One radian is +1/ of a full circle, or approximately 60°. + +To convert angles from degrees to radians you simply multiply by +`PI/180`. The value of `PI` is a protected variable which is always +available. Here is a short table of the values of `FSIN` and `FCOS` over +the first quadrant as produced by the command shown. Notice how the +radian value was saved for use in the second function call: + + *FOR A=0,10,90; TYPE %2,A %15.1, FSIN(R=A*PI/180), FCOS(R)! + 0 0.0000000000 1.0000000000 + 10 0.1736481776 0.9848077530 + 20 0.3420201433 0.9396926207 + 30 0.5000000001 0.8660254037 + 40 0.6427876096 0.7660444431 + 50 0.7660444431 0.6427876096 + 60 0.8660254037 0.5000000001 + 70 0.9396926207 0.3420201433 + 80 0.9848077530 0.1736481778 + 90 1.0000000000 0.0000000000 + + +#### `FTAN`/`FATN` + +The tangent function is not provided as an internal function since it is +just the ratio of `FSIN`/`FCOS` and is thus easy enough to compute. The +user may implement his own FTAN function, however, as described in the +discussion of [Program Defined Functions](#pdfs). + +The inverse tangent function (a.k.a arctan) is available, however. +`FATN` accepts values of any magnitude and returns the *angle* (in +radians) which would give that tangent. The range of answers is from +/2 (-90°) to /2 (+90°). +To convert from radians to degrees, just multiply by `180/PI`. For +example, to check that the angle whose tangent is -1 is, in fact, -45°: + + *TYPE 180*FATN(-1)/PI ! + -4.500000000E+01 + + +#### Trigonometric Identities + +All other trig functions can be derived from these primary functions. +For example: + +| Function | Identity | +| ----------------- | --------------------- | +| Cotangent |`FCOS(A)/FSIN(A)` | +| Arcsine | `FATN(A/FSQT(1-A*A))` | +| Arccosine |`FATN(FSQT(1-A*A)/A)` | +| Hyperbolic sine |`(FEXP(A)-FEXP(-A))/2` | +| Hyperbolic cosine |`(FEXP(A)+FEXP(-A))/2` | + +Consult any advanced algebra book for other such identities. + + +### Other Built-In Functions + +#### `FSQT` + +The `FSQT` function computes the square root of the argument using an +iterative approximation which guarantees that no more than the last bit +will be in error. Example: + + *TYPE FSQT(2), FSQT(2)^2! + 1.414213562E+00 2.000000000E+00 + + +#### `FABS` + +`FABS` returns the absolute value of the argument: + + *TYPE FABS(-1), FABS(1)! + 1.000000000E+00 1.000000000E+00 + + +#### `FSGN` + +`FSGN` returns -1, 0, or +1 depending upon whether the argument was +negative, zero or positive. Example: + + *TYPE FSGN(PI), FSGN(PI-PI), FSGN(-PI) ! + 1.000000000E+00 0.000000000E+00-1.000000000E+00 + + +#### `FITR` + +`FITR` returns the InTegeR part of the argument. Thus `FITR(PI)` is `3` +and `FITR(-5.5)` is `-5`. Note that some languages have an +[entier][entier] function which is the 'integer less than or equal to +the argument'. For positive numbers this produces the same result as +UWF's `FITR` function, but for negative values it gives the next lowest +number. If you are converting a program which was originally written in +another language, be sure to watch for this subtlety! It should be noted +that many functions and commands in UWF convert values to an integer +form internally without requiring the programer to do so. Subscripts, +for example, are always used in integer form, meaning that `A(1.5)` is +legal, but is no different from `A(1)`. In general, a value which is +used as an index or is stored in a hardware register is always converted +to an integer before use. + +[entier]: https://en.wikipedia.org/wiki/Floor_and_ceiling_functions#Notation + + +#### `FRAC` + +`FRAC` returns the fractional part of a number — the part which `FITR` +discards! This may be used to do "modulo-N' arithmetic or to check for a +remainder. The user is cautioned, however, that the value returned by +`FRAC` may have only limited accuracy and hence checks for 'exact' +values computed from expressions containing the `FRAC` function should +generally be avoided. To illustrate, the fractional value of '.002' is +.002, but the fractional value of 1.002 is off in the 8th place while +that of 1000000.002 is only correct to 3 digits. This is simply the +result of taking the difference between two large numbers. + + +#### `FMIN`/`FMAX` + +These functions compare two arguments, returning the algebraically +smallest or largest value. Thus `FMIN(+1,-2)` would return -2 while +`FMAX` would return +1. These functions have several uses. A simple +example in connection with the `FLOG` function allows one to avoid the +'log-of-zero' error with a call such as `FLOG(FMAX(1E-10,X))`. +Similarly, the `FMIN` function can be used to avoid typing nonexistent +values when dumping an array in a multi-column format. In this example, +`C` is the number of columns and `N` the number of data values in the +array: + + FOR 1=1,C,N; FOR J=I,FMIN(N,C+I-1); TYPE Q(J); NEXT; TYPE ! + +As a final example, an entire array can be scanned for its extrema +simply by comparing each element with the previous best estimates: + + SET MIN=MAX=A(1); FOR I=2,N; SET MIN=FMIN(A(I),MIN), MAX=FMAX(A(I),MAX) + +A disadvantage of this method for locating the extremes is that no +information is available as to which element is biggest or smallest, +only the values are returned. + + +#### `FRAN` + +The `FRAN` function returns a different pseudo-random number each time +it is called. The numbers are limited to the range 0-1 and have an +approximately 'flat' distribution. Other distributions, for instance +Gaussian or Lorentzian functions, can be created as Program Defined +Functions by using `FRAN` in an appropriate expression. The function is +initialized by the input wait loop so the values you observe will appear +to be truly random. The pair-wise and higher-order groupings do have a +small correlation coefficient, but even so, a reasonable value of `PI` +can be obtained using `FRAN` to generate a 2-dimensional scatter +pattern. The principal use of `FRAN` appears to be for games. + + +## Character and I/O Functions + +The remaining internal functions handle character manipulation and other +special-purpose I/O operations. The character functions include `FIN`, +`POUT`, `FIND` and `FTRM`, while `FSR`, `FMQ` and `FDIN` are 'I/O-type' +functions. `FBUF` and `FCOM` provide access to extended memory for +storing large data arrays. + + +### `FIN` + +The `FIN` function reads a single character from the Input Device and +returns the numerical value of that character. A list of character +values may be found in Appendix I, and the value of any character can be +obtained within the program simply by preceding it with a single quote +mark. Thus the expression `('A)` will have the value of the letter `A` +(193) while `('A-'Z`)` will be the difference of the codes for `A` and +`Z`. Character strings can be read with the `FIN` function and later +output with `FOUT`; this is a bit slow, but it does provide UWF with a +limited string-handling facility. + + +### `FOUT` + +The `FOUT` function generates a single character from the value of an +arithmetic expression. It will thus output what `FIN` has input: +`FOUT(193)` will generate the letter `A`. More commonly, however, `FOUT` +is used to output special control characters which would otherwise be +invisible if they were simply included in a `TYPE "..."` command. For +instance, `FOUT(7)` is used to ring the 'bell', while `FOUT(140)` +outputs a 'form-feed' character. `FOUT(27)` generates an +ESCAPE code which is used by many terminals to initiate +special functions such as reverse video, cursor movement, etc. + +`FOUT` expects arguments in the range 0-255; values beyond this range +will be output, but should be avoided. Most terminals respond in the +same way to values in the range 0-127 and 128-255. UWF's input routines, +however, always return values in the higher range (128-255) in keeping +with the standard established for the PCM-12. + +The value returned by `FOUT` is always *zero*, not the value of the +character code! This was done to simplify calling the function as part +of a command. For instance, you can output a form feed ahead of a +program listing by using a `WRITE FOUT(12)` command instead of just +`WRITE`. Similarly, since 'tabbing' to column zero is ignored, you can +include `FOUT`s in `ASK` or `TYPE` commands just by putting them in 'tab +expressions'. To print a 'double quote' mark, for instance, you could +use the following: + + TYPE "THIS IS A ":FOUT('")" MARK!" + +which will produce: + + THIS IS A " MARK! + + +### `FIND` + +`FIND` searches for a character equal to its argument, reading and +echoing all characters it encounters until it finds a match. The echo is +controlled by the setting of the input echo switch, as [described +earlier](#echo-option). The character which matches is *not* echoed, +however, but is returned as the value of the function. To output this +character too, you may use a call such as `S FOUT(FIND('A))` where `A` +is the search character. To read in a comment line, just search for a +Carriage Return: `SET FIND(141)`. To read the same line in from a paper +tape, however, you should search for the line feed following the CR: +`SET FIND(138)`. This is due to different conventions for the +'end-of-line' character. `FIND` also checks continually for a +CTRL/Z. This is recognized as an 'End-of-File' mark and +causes `FIND` to return with the value 'zero' instead of with the value +of the search character. + + +### `FTRM` + +As discussed [earlier](#input-terminators), the `ASK` command treats any +input other than `0`-`9` and `A`-`Z` as a terminator, which means that +data values may be conveniently 'flagged' by the use of a special +terminating character. The purpose of the `FTRM` function is to then +pass this information back to the program so that special action may be +taken if necessary. For instance, a program might need to be able to +work with either metric or English measurements, using an appropriate +terminator to differentiate between them. Similarly one can devise a +'pocket calculator' program which accepts numbers terminated by one of +the arithmetic operators and then performs the indicated function. One +of the more common uses for this feature is to permit an indefinite +number of data values to be read in, sensing a special terminator for +the last value. A loop like the one in the example below (which checks +for a `?`) is all that is required: + + 4.1 ZERO N;TYPE "ENTER QUIZ GRADES, TERMINATE THE LAST ONE WITH A '?'"! + 4.2 ASK G(N=N+1); IF (FTRM()-'?) .2,,.2; TYPE %2"THERE WERE"N "GRADES"! + + +### `FBUF`/`FCOM` + +These functions allow UWF to use extra memory for data storage and are +thus of interest only for systems with more than 12K. They may be added +by setting Switch 8 *UP* when UWF is started for the first time. (See +[above](#opt-switch).) FBUF is designed to handle 12-bit (signed) +integer data while `FCOM` may be used for storing either 24-bit integers +or 48-bit floating-point values. Both functions are called in the same +manner: the first argument specifies the relative location in the +storage area and the second argument (if any) is the value to be stored +at that location. The function always returns the value at the location +specified. Thus: + +| Function | Description | +| ----------- | --------------------------------------------- | +| `FCOM(I)` | returns the `I`th value in the `FCOM` area | +| `FBUF(I,V)` | stores the value of `V` in the `I`th location | + +The range of the index is typically 0-4095 for FBUF and 0-1023 for +`FCOM`. `FCOM` has another mode however, in which data is stored as +two-word integers (rather than four-word floating point values) thereby +doubling the amount of storage available but limiting the range of the +data to ±223. To use `FCOM` in this mode, specify a +*negative* index. (The legal range is -1 to -2048.) Here is a loop which +stores the square root of all numbers from 0-1023: + + FOR I=0,1023; SET FCOM(I,FSQT(I)) + +Although `FBUF` and `FCOM` share the same field, FBUF starts from the +'bottom up' while `FCOM` stores from the 'top down', so both functions +may be used simultaneously. Furthermore, both functions are fully +recursive, so calls such as `FCOM(I,FCOM(J))` may be used to move data +from one location to another. + + +### `FSR` + +The FSR function reads the value of the Switch Register. This may be +used to control program options. The value is treated as a signed +number, so the range is from -2048 (4000 octal) to +2047 (3777 octal). + + +### `FMQ` + +The FMQ function displays the integer part of the argument in the MQ +register. This is quite handy for 'spying' on the progress of a long +calculation simply by displaying the value of a loop index. Since FMQ +returns the integer part of the argument, it can be included in a +subscript expression, such as 'A(FMQ(I))' which is functionally the same +as 'A(I)' but also displays the index in the MQ. + + +### `FDIN` + +This is an optional function for reading the input register of a DR8-EA +parallel I/O module. It may be added (along with the `KONTROL` command) +by setting Switch 7 *UP* the first time UWF is started. The interface +may be wired to respond to either levels or pulses, the difference being +that it will 'remember' a pulse, but 'forget' when a level changes. Each +bit is separately addressable, and each may be wired for pulse or level +sensing. For use with the `FDIN` ('Digital INput') function, the bits +are considered to be numbered from 1-12 (rather than from 0-11), just as +they are for the [`KONTROL` command](#kontrol). + +The value of `FDIN(0)` (or just `FDIN()` since 'zero' is always the +default value of an argument) is simply the weighted sum of all input +bits which have been 'set'. Bit '1' has the value 2048, bit '2' 'weighs' +1024, etc. The maximum value is thus '4095' if all the bits are turned +on. Any bits which are read by the `FDIN` function will be reset if they +are resettable, i.e. if they are wired for 'pulse' input. This ensures +that only one occurrence of an event will be detected by the program. + +`FDIN` can be made to respond to only a single bit — or to a collection +of bits — by including various arguments as the programmer desires. For +instance, `FDIN(1)` will only sense the state of bit '1'. If bit 1 is +on, `FDIN` will have the value 2048, while if it is off, the value 0 +will be returned, regardless of the setting of any other bits. +Furthermore, only bit 1 will be reset. The value of `FDIN(-1)` on the +other hand, will be the status of all bits *except* bit 1, i.e. bits +2-12. Any bits which are read will be reset as described above. + +More complicated masks can be constructed by specifying multiple bits. +Thus `FDIN(1,3)` will only look at bits '1' and '3', while `FDIN(-2,-5)` +will look at *all but* bits 2 and 5, etc. + + +## Program Defined Functions + +UWF allows the user to define his own set of special functions within +the program. Such 'Program Defined Functions' ('PDFs') may consist of +any set of UWF commands, ranging from a single program step to as much +as an entire group. A PDF is very similar to an ordinary subroutine +(`DO`) call, but with 3 important differences: + +1. a PDF may pass arguments to the subroutine + +2. a PDF returns a numeric value: the value of the function + +3. a PDF may occur in any command, not just `DO`, `ON`, `LINK`, etc. + +The last difference is especially important since it allows subroutine +calls in some circumstances when they might not otherwise be possible. + +The form of a PDF call is: + + F( line number, argument list ) + +where the letter `F` identifies this as a function call and the line (or +group) number identifies the function. This number can be replaced by a +suitably chosen variable so that one may use a ['named' function +call](#named-pdf) rather than a 'numeric' one. The argument list is not +required, but may contain several arguments. Typically, only 1 or 2 are +used although this is not a fundamental restriction. The arguments may +consist of other PDF calls which do not themselves have arguments, or +any other internal functions, with or without arguments. The use of +nested PDF calls containing an argument list is restricted since the +arguments are not stored recursively. Here are few examples of Program +Defined Functions: + +| Call | Description | +| ----------: | --------------------------------------------- | +| `F(2,A*B)` | Calls Group 2, passing `A*B` as the argument | +| `F(.9,X,Y)` | Calls line XX.90 in the current group | +| `F(-9.5)` | Calls sub-group at line 9.5 with no arguments | + +Coding a PDF is no different from writing an ordinary subroutine, but +the mechanism for passing argument values and returning the function +result needs to be explained. The value of each arithmetic expression +appearing in the argument list is saved in a specific 'protected +variable'. The first argument is saved in the variable `#`, the second +one in the variable `$`, and the third in the variable `%`. Additional +arguments are possible, and if necessary more protected variables should +be defined when [initializing UWF](#initializing). The ordinary +variables created by the program may also be used as 'common' variables +(those appearing in both the 'main' program and the definition of the +function) for passing information to the subroutine. + +PDF calls are not required to always have the same number of arguments, +so infrequently used parameters can be placed after frequently used +ones. These will not be changed unless they are modified by the +subroutine itself. In the first example, the value of `A-times-B` is +placed in the variable `#`. In the second example, `X` is placed in `#`, +and `Y` goes into `$`. If this function were called subsequently with +only a single argument, the value placed in `$` would not be disturbed. +No arguments are used in the third example, but any variables defined by +the program may be used by the subroutine. This is the only reasonable +way to handle arrays. + +The subroutine must then be written to use the appropriate protected +variable whenever it needs the value of the corresponding argument. A +routine to compute the length of a vector, for instance, might use an +expression such as `FSQT(#*#+$*$)`. + +The value returned by the function is just the result of the last +arithmetic expression processed by the subroutine. This expression may +be evaluated by any suitable command, but typically the `SET` command is +employed. To begin with a very simple example, here is how you could +code the tangent function: + + 9.9 SET FSIN(#)/FCOS(#); COMMENT: THIS IS THE TANGENT FUNCTION + +You could also include a replacement operator to save the result in a +variable, or you could use the `TYPE` command to output the result of +the expression, or whatever. Since it is the *last* result which is +returned as the value of the function, however, if other calculations +are necessary for checking the result or performing ancillary +calculations, the value desired must be saved and then `SET` again just +before returning. + +There are a number of UWF commands which do not disturb a PDF result and +so may be used without caution in the definition of the function. These +are `COMMENT`, `RETURN`, `YNCREMENT` and `ZERO`. On the other hand, +branching commands always evaluate a line number (which may be zero), +and so cannot be used to terminate a PDF without destroying the +(expected) function result. It should also be pointed out that the line +number option in a [`RETURN` command](#return) will be ignored by a PDF +call. This is necessary to ensure that the program returns to complete +the function call. + +While most PDF calls just use an explicit line or +group number to identify the function, it is possible to be somewhat +more elegant! By using a variable with a nicely selected name you can +specify the `F(TAN,X)` function rather than the `F(9.9)` function. To do +this, just set the variable `TAN` to the value 9.9. This has the +additional advantage that you can easily move the subroutine to a +different part of the program without having to change all the function +calls. + + +## Examples of Program Defined Functions + +Here are a few interesting PDFs which illustrate some of the things you +can do. A symbolic name has been used in most cases; it must be set to +the value of the line or group actually used to code the function. + +1. `F(PWR,X,Y)` — raises `X` to the `Y` power when `Y` is non-integer: + + SET FEXP($*FLOG(#)) + + Sample call: `TYPE F(PWR,27,1/3)    3.000000000` + +2. `F(!,N)` — computes the Nth factorial (maximum value of N is about 300) + + FOR I=$=1,#; SET $=$*I + + Sample call: `TYPE F(!,5)           120.0000000` + +3. `F(SUM)` — computes the sum of the subscripted array `G(I)` + + ZERO $; FOR I=1,N; SET $=$+G(I) + + Sample call: `SET AVE=F(SUM)/N` + +4. `F(PN,X)` — evaluates the polynomial + `Y=C(0)+C(1)*X+C(2)*X^2+...+C(N)*X^N` + + FOR I=N,-1,$=0; SET $=$*#+C(I) + + This function is useful for computing series approximations + +5. `F(OCTAL,VALUE)` — converts a number from decimal to octal + + FOR I=N=0,4,SET N=N+(#-8*#=FITR(#/8))*10^I + + Sample call: `TYPE F(OCTAL,1000)     1750` + + This is the most interesting of the functions shown so far, if for + no other reason than that it uses all the arithmetic operators in a + single SET command as well as some fancy redefinitions within the + loop. The technique employed is quite general for changing from one + number base to another, so simply by interchanging the 8s and 10s in + the definition you can construct a function to give you the decimal + equivalent of an octal number: + + TYPE F(DECIMAL,1000)            512 + + To be still more elegant you can rewrite the function to use the + value of `$` in place of the number 8 shown above and thus have a + general-purpose routine for converting to any number base less than + or equal to 10. A fun thing to do once you have made this change is + to try it out with a direct command such as: + + FOR J=2,10; TYPE F(BASE, 99, J)! + + which will then type out the value of 'ninty-nine' in all number + bases from 2-10. The loop limit represents the maximum number of + digits required to represent the number, so if you try this with + large numbers and small number bases you will probably need to + increase the limit to something more than '4'. + +6. PDF replacements for the + transcendental functions: + + These functions may be used in place of the internal functions in + the event that you wish to delete some of them to increase the + number of variables available on an 8K machine. + + F(EXP)=     25.1 IF (#*#-.01).2; SET #=F(EXP,#/2)^2 + EXP=25.1    25.2 SET #=1+#+#*#/2+#^3/6+#^4/24+#^5/120 + + F(LOG)=     26.1 IF (#*#-2.04*#+1).2; SET #=2*F(LOG,FSQT(#)) + LOG=26.1    26.2 SET #=(#-1)/(#+1), #=2*(#+#^3/3+#^5/5+#^7/7) + + F(ATN)=     27.1 IF (#*#-.01).2; SET #=2*F(ATN,#/(1+FSQT(1+#*#))) + ATN=27.1    27.2 SET #=#-#^3/3+#^5/5-#^7/7 + + F(SIN)=     28.1 IF(#*#-0.1).2; SET #=F(SIN(#/3), #=3*#-4*#^3 + SIN=28.1    28.2 SET #=#-#^3/6+#^5/120 + F(COS)=     28.3 SET F(SIN, PI/2-#) + + F(TAN)=     29.1 IF (#*#-.01).2;S #=F(TAN,#/2), #=2*#/(1-#*#+1E-99) + TAN=29.1    29.2 SET #=#+#^3+#^5/7.5+#^7/315 + + F(ASIN)=    30.1 IF (#*#-.01).2;S #=2*F(.1,#/(FSQT(1+#)+FSQT(1-#))) + ASIN=30.1   30.2 SET #=#+#^3/6+.075*#^5+#^7/22.4 + F(ACOS)=    30.3 SET PI/2-F(ASIN) + + F(HSIN)=    31.1 IF (#*#-.01).2; SET #=F(HSIN,#/3), #=3*#+4*#^3 + HSIN=31.1   31.2 SET #=#+#^3/6+#^5/120 + F(HCOS)=    31.3 SET FSQT(F(HSIN)*#+1) + + The method used in these functions is to recursively reduce the + argument to a value typically less than .1, evaluate a series + approximation which is reasonably accurate for an argument of this + magnitude, and then 'bootstrap' back using an identity such as + e2X=(eX)2. Thus the approximation + for `F(EXP)` is evaluated after reducing the argument to the proper + range and then the result is squared enough times to return to the + original value. This clever method was devised by A.K. Head. + +7. In many cases a PDF call is preferable to a simple `DO` because it + can pass a parameter or two to the subroutine at the same time and + can also return a 'status' value. As an example of such a use, + consider a subroutine for finding the roots of a quadratic equation. + There are three possible cases: the roots are equal, the roots are + real but unequal, or the roots are complex numbers. If the values + produced by the subroutine are stored in `R1` and `R2`, then after + calling the routine one must still decide how to interpret the + results. If the subroutine were to return the value of the + 'discriminant' this could be accomplished as follows: + + ON (F(QR)) complex, equal, unequal + + where `QR` is the group number of the Quadratic Root subroutine and + 'complex', 'equal', 'unequal' are line or group numbers associated + with the `ON` command which serves both to call the subroutine and + to test the result at the end. Other such examples will undoubtedly + occur to the reader. + + +## Function Summary + +Here is a list of all the functions implemented in the standard version +of UWF. Since up to 36 internal functions are possible, it should be +clear that this list is not exhaustive. + +| Function | Description | +| ------ | ------------------------------------------------------------ | +| `FABS` | Returns the absolute value of the argument | +| `FATN` | Returns the angle in radians whose tangent is given | +| `FBUF` | Optional: stores or retrieves 12-bit signed integers | +| `FCOM` | Optional: accesses additional memory for data storage | +| `FCOS` | Returns the cosine of an angle measured in radians | +| `FDIN` | Optional: returns value of digital input register | +| `FEXP` | Returns value of eX where \|X\| is less than 1418 | +| `FIN` | Reads and returns the value of a single character | +| `FIND` | Searches for a given character code | +| `FITR` | Returns integer value of the argument | +| `FLOG` | Returns the natural logarithm of the argument | +| `FKAX` | Returns the maximum value of two arguments | +| `FMIN` | Returns the minimum value of two arguments | +| `FMQ` | Displays the argument in the MQ register, returns same | +| `FOUT` | Outputs a single character value | +| `FRAC` | Returns the fractional part of the argument | +| `FRAN` | Returns a random number in the range 0-1 | +| `FSGN` | Returns the sign value of the argument: -1,0,+1 | +| `FSIN` | Returns the sine of an angle measured in radians | +| `FSQT` | Returns the square root of a positive number | +| `FSR` | Returns the signed value of the switch register | +| `FIRM` | Returns the value of the last `ASK` terminator | +| `F` | Program Defined Functions | + + +## Appendix Ⅰ + +### Decimal Values for All Character Codes + +| Code | Character | Name | Code | Char | Code | Char | Code | Char | +| ---- | --------- | ---- | ---- | ------- | ---- | ---- | ---- | ----------- | +| 128 | `Ctrl/@` | NULL | 160 | `SPACE` | 192 | `@` | 224 | \` | +| 129 | `Ctrl/A` | SOH | 161 | `!` | 193 | `A` | 225 | `a` | +| 130 | `Ctrl/B` | STX | 162 | `"` | 194 | `B` | 226 | `b` | +| 131 | `Ctrl/C` | ETX | 163 | `#` | 195 | `C` | 227 | `c` | +| 132 | `Ctrl/D` | EOT | 164 | `$` | 196 | `D` | 228 | `d` | +| 133 | `Ctrl/E` | ENQ | 165 | `%` | 197 | `E` | 229 | `e` | +| 134 | `Ctrl/F` | ACK | 166 | `&` | 198 | `F` | 230 | `f` | +| 135 | `Ctrl/G` | BELL | 167 | `'` | 199 | `G` | 231 | `g` | +| 136 | `Ctrl/H` | B.S. | 168 | `(` | 200 | `H` | 232 | `h` | +| 137 | `Ctrl/I` | TAB | 169 | `)` | 201 | `I` | 233 | `i` | +| 138 | `Ctrl/J` | L.F. | 170 | `*` | 202 | `J` | 234 | `j` | +| 139 | `Ctrl/K` | V.T. | 171 | `+` | 203 | `K` | 235 | `k` | +| 140 | `Ctrl-L` | F.F. | 172 | `,` | 204 | `L` | 236 | `l` | +| 141 | `Ctrl-M` | C.R. | 173 | `-` | 205 | `M` | 237 | `m` | +| 142 | `Ctrl/N` | SO | 174 | `.` | 206 | `N` | 238 | `n` | +| 143 | `Ctrl/O` | SI | 175 | `/` | 207 | `O` | 239 | `o` | +| 144 | `Ctrl/P` | DLE | 176 | `0` | 208 | `P` | 240 | `p` | +| 145 | `Ctrl/Q` | XON | 177 | `1` | 209 | `Q` | 241 | `q` | +| 146 | `Ctrl/R` | DC2 | 178 | `2` | 210 | `R` | 242 | `r` | +| 147 | `Ctrl/S` | XOFF | 179 | `3` | 211 | `S` | 243 | `s` | +| 148 | `Ctrl/T` | DC4 | 180 | `4` | 212 | `T` | 244 | `t` | +| 149 | `Ctrl/U` | NAK | 181 | `5` | 213 | `U` | 245 | `u` | +| 150 | `Ctrl/V` | SYNC | 182 | `6` | 214 | `V` | 246 | `v` | +| 151 | `Ctrl/W` | ETB | 183 | `7` | 215 | `W` | 247 | `w` | +| 152 | `Ctrl/X` | CAN | 184 | `8` | 216 | `X` | 248 | `x` | +| 153 | `Ctrl/Y` | EM | 185 | `9` | 217 | `Y` | 249 | `y` | +| 154 | `Ctrl/Z` | SUB | 186 | `:` | 218 | `Z` | 250 | `z` | +| 155 | `Ctrl/[` | ESC | 187 | `;` | 219 | `[` | 251 | `{` | +| 156 | `Ctrl/\` | FS | 188 | `<` | 220 | `\` | 252 | \| | +| 157 | `Ctrl/]` | GS | 189 | `=` | 221 | `]` | 253 | `} ALTMODE` | +| 158 | `Ctrl/^` | RS | 190 | `>` | 222 | `^` | 254 | `~ PREFIX` | +| 159 | `Ctrl/_` | US | 191 | `?` | 223 | `_` | 255 | `⌑ DELETE` | + +`FOUT(141)` will output a RETURN/LINE FEED while +`FOUT(13)` will just do a RETURN. Codes 225 through 255 are +lower case letters, some of which serve other functions on keyboards +without lower case. Many keyboards use SHIFT/K for `[`, +SHIFT/L for `\`, and SHIFT/M for `]` and +corresponding combinations for the control codes following +CTRL/Z. These symbols are often not printed on the key tops. +Codes 0-127 are the same as codes 128-255 except for the parity bit. UWF +always forces the parity bit during input. + + +## U/W FOCAL V4E Patches + +Here is a list of patches for adding a number of special features to +UWF. They are shown in the format: `FLLLL/ CCCC PPPP; QQQQ` where +`FLLLL` is the Field + Memory location, `CCCC` is the original contents, +and `PPPP` is the patch. In cases where several successive locations are +to be changed, a semicolon is shown, followed by the next patch `QQQQ`. +Note that the `FCOM` patch shown below is for 16K versions only and must +be added *before* UWF is started the first time. + + +### Field 0 + +`00045/ 4463 4442` — Replace extra variable storage with `FCOM` ([16K only](#starting)) + +`00061/ 7610 6213` — Print a CR/LF before printing an error message + + +### Field 1 + +`10402/ 4547 0000` — Eliminate the line number printout in `MODIFY` + +`11216/ 7000 4533` — Make the `ASK` command print a `:` each time + +`11241/ 1377 7040` — Use the `#` operator to output a Form Feed + +`12471/ 1000 1177; 4533` — Change 'rubout' for video terminals + +`13070/ 7106 7107` — Increase the delay after a Carriage Return + +`13134/ 7000 6xxx` — Clear an unwanted interrupt (next 3 locations too) + +`15665/ 1103 1213` — Make `TYPE` print an `=` ahead of each value + +`15666/ 4534 7200` — Remove the initial space (or `=`) printed by `TYPE` + +`14503/ 62X1 62Y1` — Change the data field used by `FCOM` (`X`,`Y` may be 2-7) + +`14545/ 62X1 62Y1` — Ditto for the `FBUF` function (`X` is set at startup) + +`10033/ 4566 5200` — Remove the `FLOG`, `FEXP` and `FATN` functions to increase the
+`12371/ 5020 1754; 1754; 1754` — size of the symbol table in the 8K version + +`10033/ 5200 5303` — Remove `FSIN` and `FCOS` to increase the symbol table size a
+`12367/ 5205 1754; 1754` — little bit more (8K only) + + +## Error Codes for UWF (V4E) October 1978 + +| Code | Meaning | +| -----: | --------------------------------------------------------------------- | +| ? | Keyboard interrupt (CTRL/F) or restart from location 10200 | +| ?01.50 | Group number greater than 31 | +| ?01.93 | Non-existent line number in a `MODIFY` or `MOVE` command | +| ?03.10 | Non-existent line called by `GOTO`, `IF`, `NEXT`, `BREAK` or `QUIT` | +| ?03.30 | Illegal command | +| ?03.47 | Non-existent line or group: `DO`, `ON`, `JUMP`, `LINK` or `PDF` call | +| ?04.35 | Missing or illegal terminator in a `FOR` command | +| ?06.03 | Illegal use of a function or number: `ASK`, `YNCR`, or `ZERO` | +| ?06.41 | Too many variables (`ZERO` unnecessary ones to recover space) | +| ?07.44 | Operator missing or illegal use of an equal sign | +| ?07.67 | Variable name begins with `F` or improper function call | +| ?07.76 | Double operators or an unknown function | +| ?08.10 | Parentheses don't match | +| ?10.50 | Program too large (sorry, you'll have to erase some of it) | +| ?18.32 | `FCOM` index out of range | +| ?19.72 | Logarithm of zero | +| ?21.57 | Square root of a negative number | +| ?22.65 | More than 10 digits in a number | +| ?25.02 | Stack overflow: reduce nested subroutines and expressions | +| ?27.90 | Zero divisor | +| ?31.<7 | Non-existent program area called by `LOOK` or `LINK` | +| ← or \_ | End of input sensed, I/O switched back to the terminal + + +## `FPAL` + +`FPAL` allows the user to code short 'machine language' functions +directly into his program. This provides 'keyboard control' of special +devices which are not supported by any of the normal functions or +commands, and also permits operations requiring only 12-bit arithmetic +to proceed at full machine speed. Routines as long as 3210 +instructions can (in theory) be incorporated, but in practice, `FPAL` +routines are seldom longer than about 5-10 instructions — just enough to +execute a short sequence of `IOT`s to pulse a control line, for +instance. + +The form of the function call is: `FPAL(AC,inst,inst,inst...)` where +`AC` is an arithmetic expression, the value of which will be placed in +the AC prior to calling the routine, and the remaining arguments are +construed as a list of *octal* numbers which represent the desired +machine instructions. These are stored in Field 3 such that the first +instruction is at 'page+1' , the second at 'page+2', etc. After the last +instruction has been tucked away, `FPAL` loads the `AC` with the integer +part of the first argument, clears the `Link`, and calls the routine. +The final value of the `AC` is then returned to the program as the value +of the function. Note that the user does not have to worry about any of +the 'calling' instructions — he only has to write the essential machine +code. + +Here are a few examples which may help clarify how the `FPAL` function +works and illustrate some of the things it can do: + +1. UWF has an `FMQ` function for loading a number into the `MQ` + register (where it is preserved by all internal arithmetic + operations), but no corresponding function for finding out what is + already there. The following `FPAL` function will not only do this, + but will also increment the value in the `MQ` at the same time: + + TYPE MQ=FPAL(,7501,7001,7521) + + Note that the first argument has been omitted in this example, since + no information is being passed *to* the function. The first + instruction (`7501=MQA`) reads the `MQ`, the next (`7001=IAC`) + increments this value and the third (`7521=SWP`) interchanges the + new and old values, saving the new value for a subsequent call and + returning the old value to the program. Machines based on the 6100 + microprocessor may not be able to display the `MQ` while UWF is + running. Using this function however, the value of the hardware + register can be saved in the variable `MQ` and output by the `TYPE` + command as well. So being able to actually 'see' this register is + not a necessity. + +2. Several variations of this routine come to mind almost immediately. + For instance, we could use the hardware `SWP` instruction to + interchange two values: + + SET MQ=FPAL(AC,7521) + + or we could take advantage of the `MQA` instruction to perform an + 'inclusive OR' between a value in the `MQ` and one in the `AC`: + + SET FMQ(A),AB=FPAL(B,7501) + +3. As a final example, suppose that we have constructed an A/D + converter interface which uses the same instruction set as the + AD8-EA. In order to test it out we can use the following `FPAL` + routine to implement the `FADC` function: + + SET CH(N)=FPAL(N,6531,6532,6534,5203,6533) + +The channel number (`N`) will be placed in the `AC` at the beginning and +can be used to control the multiplexer via a `6531=ADLM` instruction. +The converter is then started (`6532=ADST`) and we begin testing the +'done' flag (`6534=ADSD`) to see when it is finished. This involves a +`JMP .-1` instruction which means that the location of the `ADSD` +instruction (relative to a page boundary) must be known. Since `FPAL` +routines always start at 'page+1', (location 'page+0' can be used as a +'temporary'), a jump to the *third* instruction becomes `5203`. When the +conversion is finally done, the result is read into the `AC` +(`6533=ADRD`) and returned to the program. + +It goes almost without saying that such easy access to machine-level +code is both powerful *and* dangerous! No error checking can be +performed, so a single 'typo' can lead to instant disaster! Always be +sure, therefore, to save a copy of a valuable program *before* you try +out any sort of 'wild' `FPAL` function, and be especially careful with +`ISZ`s, `DCA`s, `JMP`s and `JMS`es since they can modify memory or send +the program off 'into the wild blue yonder'. + +Similarly, give special consideration to any `IOT` which might cause a +hardware interrupt since UWF runs with the interrupt system enabled! +Most interfaces have an 'interrupt disable' instruction, but if it is +necessary to use an `IOF` in order to protect UWF from a spurious +interrupt, be sure to clear the flag and then issue an `ION` before +exiting from the function — otherwise it may be necessary to [restart +the interpreter](#starting) in order to activate the interrupt system +again. + + +### Advanced Considerations + +While it is clearly possible to use `FPAL` to implement patches to UWF +itself, this practice is *strongly* discouraged — and no help with such +folly will be offered — since this makes programs 'version dependent'. +On the other hand, there *are* a few 'tricks' which could prove useful +at various times: + +1. The value of the first parameter is actually converted into a 24-bit + integer, of which only the lower 12-bits are loaded into the `AC` at + the beginning of the routine. This means that the values `4095` and + `-1` will both load 77778 into the AC. The high-order + part of the number can be accessed with a `TAD 45` (1045) + instruction, while the low-order half can always be recalled with a + `TAD 46` (1046) if it is needed later on in the function. + +2. The value of the AC is normally returned as a signed number; if it + is more desirable to have an 'unsigned' result you can simply code an + `ISZ .+1` instruction as the last step of the routine. Thus: + `TYPE FPAL (4095)` will return `-1`, whereas `TYPE FPAL (4095,2202)` + will return `4095`. The `2202` instruction is `ISZ .+1` when located + at 'page+1'. + + Notice that numbers appearing in the *first* argument of an `FPAL` + call are treated as 'decimal' values and can be replaced by + variables and/or other functions. The remaining arguments, however, + are processed as character strings and so cannot be replaced by + arithmetic expressions. + + +--------------------------------- + +**End Notes** + +1. FOCAL, PDP-8 and OS/8 are trademarks of Digital Equipment Corp., + Maynard, Mass. + +2. The automatic return can be aborted if desired — see the + [`RETURN` command](#return) for further details. + +3. For more information on storing programs in different areas, see the + [expanded text storage](#etext) discussion. + +4. `LINK` and `LOOK` differ only in the presence or absence of a second + parameter. If only the area is specified, UWF returns to command + mode (`LOOK`), otherwise it executes a subroutine call (`LINK`). + +----------------------------- + +**Formatter's Note** + +This document is based on the [OCR'd text][ocr] of the [scanned U/W +FOCAL Manual V4E from October 1978][scan]. The following edits have been +made to the original text as presented here: + +1. Fixed some grammar, OCR, and heading nesting level errors. Some new + errors have doubtless been added in this process. [Bug reports][tkt] + and [patches][hack] are welcome. + +2. "PDP8" and "PDP/8" are stylized as "PDP-8", and "Binary loader" + changed to "BIN loader" to match DEC documentation. + +3. Asterisk-marked page notes are now numbered end-notes. + +4. Removed page numbers and replaced prose page references with + internal hyperlinks. This version is intended to be read on a + computer screen, not on paper; even if printed, the original + pagination would no longer apply. + +5. Replaced instances of the obscure Latin initialism [v.i.][videf] + (*vide infra*, meaning "see below") with hyperlinks to the + referenced section. + +6. The original document used typewriter formatting. Where sensible, we + use the slightly upgraded formatting possible with Markdown. + + Examples: + + - Replaced emphasis indicated via `-dashes-` with *italics* + + - Headlines are rendered with larger and bolder fonts rather than + CENTERED ALL CAPS TEXT + + - FOCAL program text, keywords within running prose, and computer + output are marked up appropriately + + - Removed hard hyphenation and primitive line justification; that is + done by the Markdown renderer, if at all + +7. Due to the differences between documents made on and transmitted in + paper form — as compared to Markdown with HTML rendering — several + of the input commands and their resulting outputs are presented + differently in this version of the manual. + + A good example is the documentation for the [`FATN`](#fatn) + function, where in the original, the example input was given inline + in the prose, and the output was shown centered on the page. I've + chosen instead to present it as a terminal transcript, with the + command and its output shown just as you'd see it on your terminal. + + This in turn means that in some places I've added the `*` FOCAL + prompt to make it clearer which line(s) are input and which are + output. I've also added `!` operators where necessary to avoid + questions about why you get `*` prompts at the end of program ouput. + +— [Warren Young][wy], September & October 2017, Aztec, NM, USA + +[hack]: https://tangentsoft.com/pidp8i/doc/trunk/HACKERS.md +[ocr]: https://archive.org/stream/bitsavers_decpdp8focct78_4144912/UW_FOCAL_Manual_V4E_Oct78_djvu.txt +[scan]: https://archive.org/details/bitsavers_decpdp8focct78_4144912 +[tkt]: https://tangentsoft.com/pidp8i/tktnew +[videf]: https://en.wiktionary.org/wiki/vide_infra#Latin) +[wy]: https://tangentsoft.com/ + +--------------------------- + +Original document © 1978 by LAB DATA SYSTEMS
+Seattle, Washington 98125
+All rights reserved (JvZ) + +Edits © 2017 by Warren Young
+Released under the terms of the [SIMH License](https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md) ADDED doc/uwfocal-refcards.md Index: doc/uwfocal-refcards.md ================================================================== --- /dev/null +++ doc/uwfocal-refcards.md @@ -0,0 +1,414 @@ +# U/W FOCAL V4E Reference Cards + +### Introduction + +The following material is reformatted from the `CARD[1-4].DA` files +contained within the U/W FOCAL V4E distribution which we used in +creating the PiDP-8/I software project's U/W FOCAL feature. + +Some minimal effort has been made to make this document print well, +though it doesn't paginate the same as the original material. + +Since these files were likely created [before 1978][cl78] and probably +did not have their copyright renewed — if it was in fact applied for, +not an automatic thing at the time in the United States — we believe +this text to be in the public domain. If the authors of the text below +request it, we will remove this file from the PiDP-8/I software +distribution. + +[cl78]: https://en.wikipedia.org/wiki/Copyright_law_of_the_United_States#Works_created_before_1978 + + + + +## U/W FOCAL Quick Reference Card (`CARD1.DA`) + + +### Single Letter Commands + +| `A` | Ask [`"QUERY"`,`X`,`:`,`!`] | Accepts value of `X` from input device | +| `B` | Break [_L1_]% | Exits from a FOR loop, continuing at _L1_ | +| `C` | Comment | Ignores the rest of the line | +| `D` | Do [_G1_,_G2_,_G3_,etc.] | Calls a line or a group as a subroutine | +| `E` | Erase [_G1_] | Deletes all or part of the program | +| `F` | `For X=`_E1_`,`[_E2_`,`] _E3_`;`(_commands_) | Executes line `1+(_E3_-_E1_)/_E2_` times | +| `G` | Goto [_L1_] | Branches to line _L1_ | +| `H` | Hesitate [_E1_]\* | Delays (or synchronizes) the program | +| `I` | If `(E1)` [_L1_,_L2_,_L3_]% | Transfers to _L1_,_L2_,_L3_ on sign of _E1_ | +| `J` | Jump `(`_E1_`)` [_G1_,_G2_,_G3_,_G4_...]% | Calls the subroutine selected by _E1_ | +| `K` | Kontrol [_E1_,_E2_,etc]\* | Controls relays or other digital output | +| `L` | Library/List | Two-letter commands, see the next page | +| `M` | Modify [_L1_,_L2_] | Edits and/or Moves line L1 - see below | +| `N` | Next [_L1_]% | Ends a `FOR` loop, branches to _L1_ when finished | +| `O` | On `(`_E1_`)` [_G1_,_G2_,_G3_]% | Calls subroutine selected by sign of _E1_ | +| `P` | Plot [_X_,_Y_,_L_,_M_]\* | Controls an analog or digital plotter | +| `Q` | Quit [_L1_]% | Stops program, allows restart at _L1_ | +| `R` | Return [_L1_]% | Exits from a subroutine call, continuing at _L1_ | +| `S` | Set [_E1_,_E2_,_E3_,etc.] | Evaluates arithmetic expressions | +| `T` | Type [_E1_,`"TEXT"`,`!`,`#`,`:`,`%`,`$`] | Generates alphanumeric output | +| `U` | User | | +| `V` | View [_X_,_Y_,_Z_]\* | Generates graphic output on a CRT | +| `W` | Write [_G1_,_G2_,_G3_,etc.] | Lists all or part of a program | +| `X` | Xecute | Equivalent to SET | +| `Y` | Yncrement [_X_,_Y_`-`_Z_] | Increments or decrements variables | +| `Z` | Zero [_X_,_Y_,...] | Sets some or all of the variables to zero | + +\* Indicates a non-standard (installation dependent) feature + +% If the line number is omitted (or=0) no branch will occur + +_En_ are Arithmetic Expressions - - [] Enclose optional items + +_Ln_ are Line Numbers from `0.01` to `31.99` - excluding integers + +_Gn_ are Line or Group Numbers from `0` to `+31` (`0` = next or all) + +Line numbers `.01` to `.99` refer to lines in the current group Negative +or Integer line numbers denote a 'Group' operation. Arithmetic +expressions may be used as Line or Group numbers + + +### Arithmetic Operators + +| | ( ) [ ] < > | Three equivalent sets of enclosures | +| ' | Character value | `'A` is the value of the letter `A` | +| ^ | Exponentiation | Positive or negative integer powers | +| * | Multiplication | Note especially that multiplication | +| / | Division | has a higher priority than division | +| - | Subtraction or Negation | Example: (to illustrate priorities) | +| + | Addition | `-5^4/3*A=2+1` is `0-<5^4>/[3*(A=2+1)]` | +| = | Replacement | May be used anywhere in expressions | + + +### Ask/Type Operators + +| , | COMMA or SPACE | Separates variables and/or expressions | +| ! | Carriage return/linefeed | Starts a new line for input or output | +| " | String delimiter | Case shift option uses `\`: `"A\B\C"=AbC` | +| # | Return or Clear Screen | Used for plotting or overprinting | +| $ | Symbol table listing | `TYPE $4` prints 4 variables per line | +| : | Tabulation | `ASK :-15` skips over the next 15 characters | +| | (:0 is ignored) | `TYPE :15` spaces to column 15 if not beyond | +| % | Format control | `%3` Produces 3 Digits in an integer format | +| | (for output only) | `%0.04` = 4 Digits using scientific notation | +| | (input is unformatted) | `%5.02` = 5 Digits, 2 decimal places maximum | + +Letters (but only one E) are legal numeric input: `YES=25E19`. `ALTMODE` +or `ESCAPE` aborts input, with the variable unchanged. `_` deletes all +digits during input — `RUBOUT` is ignored. + + +### Modify / Move Operators + +| `CTRL/F` | Aborts the command leaving the line unchanged | +| `CTRL/G` (bell) | Selects a new search character | +| `CTRL/L` (does not echo) | Searches for next occurrence of character | +| `_` (backarrow or underline) | Deletes all characters to the left | +| `RETURN` | Terminates the line at the current position | +| `LINEFEED` | Copies the remainder of the line unchanged | +| `RUBOUT`/`DELETE` | Removes the previous character, echos a `\` | + +`RUBOUT` or `DELETE` and `_` also work during command input + +`LINEFEED` retypes the corrected input line for verification + + +## Command Summary (`CARD2.DA`) + +In the descriptions below, arguments in square brackets are optional. +Specify the argument, but don't include the square brackets. If a space +is in the square brackets, a space is required to provide the argument. + + +### Miscellaneous Commands + +| `O D` | Output Date | Prints system date in the form `DD.MM.YY` | +| `L E` | Logical Exit | Returns to the OS/8 keyboard monitor | +| `L B` | Logical Branch _L1_ | Branches to _L1_ if -no- input from TTY | +| `J ` | Jump _L1_. | Equivalent to the Logical Branch command | + + +### Filesystem Directory Commands + +| `L A,E` | List All [name][`,E`] | Lists all files after the one specified | +| `L O` | List Only [_name_]\* | Verifies the existence of one `.FC` file | +| `O L` | Only List [_name_]\* | Verifies the existence of one `.DA` file | +| `L L` | Library List [_name_]% | Shows files with the same extension | +| `L D` | Library Delete name [ _L1_] | Removes a name from the directory | +| `L Z` | Library Zero dev:[_length_] | Zeros directory using length given | + +Notes on Directory Commands: + +`E` Adding the phrase `,E` will list all of the 'empties' too + +* Omitting the name lists all files with the same extension + +% A null extension will list all files having the same name + + +### Program Commands + +| `L C` | Library Call name | Loads a program, then Quits | +| `L G` | Library Gosub name [ _G1_] | Calls a program as a subroutine | +| `L R` | Library Run name [ _L1_] | Loads a program and starts at _L1_ | +| `L N` | Library Name [_name_] | Changes the program header | +| `L S` | Library Save name [ _L1_] | Saves the current program | + +[ _G1_] indicates which line or group will be called by `L G` + +[ _L1_] specifies an error return, except for the `L R` command + + +### Input / Output Commands + +| `O A` | Output Abort [_E1_] | Terminates output file with length _E1_ | +| `O B` | Output Buffer | Dumps buffer without closing the file | +| `O C` | Output Close [_E1_] | Ends output, saves file with length _E1_ | +| `O I,E` | Open Input [`,Echo`] | Selects the terminal for input | +| `O O` | Open Output | Selects the terminal for output | +| `O S` | Output Scope | Selects CRT for output (if available) | +| `O I -` | Open Input name [`,E`] [ _L1_] | Switches input to an OS/8 device | +| `O S -` | Open Second name [`,Echo`] [ _L1_] | Selects a second input file | +| `O O -` | Open Output name [`,Echo`] [ _L1_] | Initiates OS/8 (file) output | +| `O E -` | Output Everything device [`,Echo`] | Changes error/echo device | +| `O R R` | Open Restart Read [`,Echo`] | Restarts from the beginning | +| `O R I` | Open Resume Input [`,Echo`] [ _L1_] | Returns to file input | +| `O R O` | Open Resume Output [`,Echo`] [ _L1_] | Returns to file output | +| `O R S` | Open Resume Second [`,Echo`] [ _L1_] | Returns to second input file | + +The `INPUT ECHO` sends characters to the current `OUTPUT` device + +The `OUTPUT ECHO` sends characters to the current 'O E' device + + +### Filename Expressions + +Device and filenames may be written explicitly: `RXA1:`, `MYSTUF`, +`0123.45`. Numeric parts can be computed from (expressions): +`DTA(N):PROG(X).(A+B)`. Negative values specify single characters: +`F(-201)L(-197,.5,PI)=FILE03`. An \ can be +substituted for the name: `LTA1:<20*BN+7>`. Expressions in square +brackets indicate the size: `TINY[1]`, `[SIZE]`. + + +### Variables + +Variable names may be any length, but only the first two characters are +stored; the first character may not be an `F`. Both single and double +subscripts are allowed - a subscript of 0 is assumed if none is given. +The variables `!`, `"`, `#`, `$`, `%` and `PI` are protected from the +`ZERO` command and do not appear in table dumps. `!` is used for double +subscripting and should be set to the number of rows in the array. `#`, +`$`, `%` are used by [FOCAL Statement Functions](#fsf). The `ZVR` +feature permits non-zero variables to replace any which are zero. This +includes `FOR` loop indices, so use a protected variable if the index +runs through zero. Undefined or replaced variables are automatically +set to zero before their first use. + + +### FOCAL Statement Functions + +`F(G1,E1,E2,E3)` executes line or group `G1` after first setting the +variables `#`,`$`,`%` to the values of `E1`,`E2`,`E3` (if any). The +function returun with the value of the last arithmetic expression +processed by the sub routine, including line number & subscript +evaluations. For example: + + 8.1 S FSIN(#)/FCOS(#) is the TANGENT function = F(TAN,A) if 'TA' = 8.1 + 9.1 S FEXP($*FLOG(#)) computes X^Y for any value of Y using F(9.1,X,Y) + + +## Miscellaneous Material (`CARD3.DA`) + +### Internal Functions + +| `FABS(`_E1_`)` | Returns the absolute value of the argument | +| `FADC(`_N_`)` | Reads A/D converter channel N (LAB/8e or PDP12`)` | +| `FATN(`_A_`)` | Computes the arctangent of _A_, result in radians | +| `FBLK(``)` | OS/8 block number of the current input file | +| `FBUF(`_I_`,`_V_`)` | Display buffer storage (single-precision) | +| `FCOM(`_I_`,`_V_`)` | Extended data storage in Fields 2 and 4-7 | +| `FCOS(`_A_`)` | Computes the cosine of _A_ (_A_ is in radians) | +| `FCTR(`_N_`)` | Reads a frequency counter using timebase _N_ | +| `FDAC(`_N_`,`_V_`)` | Sets D/A converter channel _N_ to the value _V_ | +| `FDAY(`_MONTH*256+DAY*8+YEAR-78_`)` | Reads/Sets the OS/8 system date | +| `FDIN(`_B1_`,`_B2_`,`...`,`_Bn_`)` | Reads selected bits from the input register | +| `FDVM(`_N_`,`_R_`)` | Reads a digital voltmeter, channel _N_, range _R_ | +| `FEXP(`_E1_`)` | Base 'e' exponential function `\|`_E1_`\|<1420` | +| `FIN()` | Reads a single character, returns the ASCII value | +| `FIND(`_C_`)` | Searches for code _C_, returning _C_ if found, 0 if `EOF` | +| `FITR(`_E1_`)` | Returns the integer part of the argument | +| `FJOY(`_I_`)` | Places the cursor (joystick) coordinates in _XJ_,_YJ_ | +| `FLEN(`_I_`)` | File length: _I_=`0` for `O`utput, _I_=`1` for `I`nput | +| `FLOG(`_E1_`)` | Natural logarithm of the absolute value of _E1_ | +| `FLS()` | Returns unsigned value of the Left Switches (PDP12) | +| `FMIN(`A_`,`_B`)` | Returns the minimum or argument | +| `FMAX(`A_`,`_B`)` | Returns the maximum argument | +| `FMQ(`_N_`)` | Displays the lower 12 bits of _N_ in the `MQ` register | +| `FOUT(`_C_`)` | Outputs character code _C_, returns the value `0` | +| `FRA(`_I_`,`_V_`)` | Reads or writes in a binary file at location I | +| `FRAC(`_E1_`)` | Returns the fractional part of the argument | +| `FRAN(``)` | Pseudo-random number function, range 0-1 | +| `FSAM(`_N_`)` | Samples _N_ channels and stores results in buffer | +| `FSGN(`_E1_`)` | Returns `-1`,`0`,`+1` for _E1_ negative, zero, positive | +| `FSIN(`_A_`)` | Computes the sine of _A_ (_A_ is in radians) | +| `FSQT(`_E1_`)` | Finds the square root using Newton's method | +| `FSR()` | Reads the Switch Register | +| `FRS()` | Reads the Right Switches on a PDP-12 | +| `FSS(`_N_`)` | Tests Sense Switch _N_: `-1` = `OFF`, `+1` = `ON` | +| `FTIM(`_N_`)` | Reads, sets or clears the elapsed time counter | +| `FTRG(`_N_`)` | Returns status and clears Schmitt trigger _N_ | +| `FTRM(``)` | Returns the last input terminator | +| `FXL(`_N_`)` | Tests external level _N_ (PDP12) returning `-1` or `+1` | + +And others. There are a total of 36 possible function names + +Functions indicated by a * are not available in all versions. The +functions `FBLK` & `FLEN` are useful in filename expressions. `FIN`, +`FOUT`, `FIND` and `FTRM` use decimal ASCII codes - see below. + + +### Decimal ASCII Character Codes + +| Code | Character | Code | Char | Code | Char | Code | Char | +| ---- | -------------------- | ----- | ---------------- | ----- |------- | ----- | --------- | +| 128 | `CTRL/@` (leader/ | 152 | `CTRL/X` | 176 | `0` | 201 | `I` | +| | trailer-ignored) | 153 | `CTRL/Y` | 177 | `1` | 202 | `J` | +| 129 | `CTRL/A` | 154 | `CTRL/Z` (`EOF`) | 178 | `2` | 203 | `K` | +| 130 | `CTRL/B` | 155 | `ESCAPE` or | 179 | `3` | 204 | `L` | +| 131 | `CTRL/C` (OS/8) | | `CTRL/[` | 180 | `4` | 205 | `M` | +| 132 | `CTRL/D` | 156 | `CTRL/\` | 181 | `5` | 206 | `N` | +| 133 | `CTRL/E` | 157 | `CTRL/]` | 182 | `6` | 207 | `O` | +| 134 | `CTRL/F` (`BREAK`) | 158 | `CTRL/^` | 183 | `7` | 208 | `P` | +| 135 | `CTRL/G` (`BELL`) | 159 | `CTRL/_` | 184 | `8` | 209 | `Q` | +| 136 | `CTRL/H` (`BACKSP`) | 160 | `SPACE` | 185 | `9` | 210 | `R` | +| 137 | `CTRL/I` (`TAB`) | 161 | `!` | 186 | `:` | 211 | `S` | +| 138 | `LINEFEED` | 162 | `"` | 187 | `;` | 212 | `T` | +| 139 | `CTRL/K` (`LINEUP`) | 163 | `#` | 188 | `<` | 213 | `U` | +| 140 | FORMFEED | 164 | `$` | 189 | `=` | 214 | `V` | +| 141 | RETURN | 165 | `%` | 190 | `>` | 215 | `W` | +| 142 | `CTRL/N` | 166 | `&` | 191 | `?` | 216 | `X` | +| 143 | `CTRL/O` | 167 | `'` (`APOST`) | 192 | `@` | 217 | `Y` | +| 144 | `CTRL/P` | 168 | `(` | 193 | `A` | 218 | `Z` | +| 145 | `CTRL/Q` (`X-ON`) | 169 | `)` | 194 | `B` | 219 | `[` | +| 146 | `CTRL/R` | 170 | `*` | 195 | `C` | 220 | `\` | +| 147 | `CTRL/S` (`X-OFF`) | 171 | `+` | 196 | `D` | 221 | `]` | +| 148 | `CTRL/T` | 172 | `,` (comma) | 197 | `E` | 222 | `^` | +| 149 | `CTRL/U` | 173 | `-` (minus) | 198 | `F` | 223 | `_` | +| 150 | `CTRL/V` | 174 | `.` (period) | 199 | `G` | 253 | `ALTMODE` | +| 151 | `CTRL/W` | 175 | `/` | 200 | `H` | 255 | `RUBOUT` | + +Codes 224-250 are lower case letters. Codes 000-127 are similar +to codes 128-255 except that the parity bit has been eliminated. + +Many keyboards use `SHIFT/K`, `/L`, `/M`, `/N`, `/O` for `[`, `\`, `]`, `^` and `_` + +A single quote before a character indicates the-value-of: `'A=193` +Use `CTRL/@` to page the TV display to avoid getting error `12.40` + +To erase the screen on a Tektronix terminal: `S FOUT(27) FOUT(12)` + +To make a copy: `S FOUT(27) FOUT(23)`. Note: `FOUT(27)` = `ESCAPE` + +To make bold letters on a Centronics printer: `T :FOUT(14) "text"` + +To set 'Hold Screen' mode (VT50 terminals): `S FOUT(27) FOUT(91)` + +To rubout the last character on the PDP12/LAB8e display `FOUT(92)` + + +## Error Code Table (`CARD4.DA`) + +For extreme economy of memory, FOCAL does not print error message strings. +Instead, an error routine prints a question mark followed by a four digit +fixed point number corresponding to where in the FOCAL runtime executable +the error was encountered. + +I.E. If an error was encountered in the FOCAL interpreter's parsing +of a variable name, the error message prints out the error message +traceable to that parser within FOCAL. + +This means that an error table must be produced, and every time code shifts +around, the error table must be updated. + +The U/W FOCAL manual contains an error table, but it is incomplete. +Here is a complete one which comes from the file CARD4.DA in the +U/W FOCAL archive from which this distribution is taken. + +Errors appearing in bold face denotes an error from a command with an +optional error return. + +| Error | Meaning | +| ------------- | ------------------------------------------------------------- | +| `?` | Keyboard interrupt or restart from location 10200 | +| __`?01.03`__ | Secondary input file missing | +| __`?01.11`__ | No secondary input file to resume | +| `?01.50` | Group number greater than 31 | +| `?01.93` | Non-existent line number in a MODIFY or MOVE command | +| `?03.10` | Line called by `GO`, `IF`, `J`, `R`, `Q`, `L` `B`, or `L R` is missing | +| `?03.30` | Illegal command | +| `?03.47` | Line or group missing in `DO`, `ON`, `JUMP`, `L GOSUB` or a `FSF` | +| | | +| `?04.35` | Bad syntax in a `FOR` command (missing semicolon?) | +| `?06.03` | Illegal use of a function or number: `ASK`, `YNCREMENT`, `ZERO` | +| `?06.41` | Too many variables (ZERO unnecessary ones) | +| `?07.44` | Operator missing or illegal use of an equal sign | +| `?07.67` | Variable name begins with `F` or improper function call | +| `?07.76` | Double operators or an unknown function | +| `?08.10` | Parentheses don't match | +| `?10.50` | Program too large | +| | | +| `?12.10` | Error detected in the `BATCH` input file | +| `?12.40` | Keyboard buffer overflow (eliminated in 8/e versions) | +| `?13.65` | Insufficient memory for `BATCH` operation | +| `?14.15` | Display buffer overflow | +| `?14.50` | Bad Sense Switch number on a PDP12 (range is 0-5) | +| `?14.56` | Illegal external sense line (PDP12 range is 0-11) | +| `?17.22` | `FRA` not initialized | +| `?17.33` | `FRA` index too large (exceeds file area) | +| `?17.62` | `FRA` mode error: only modes 0,1,2,4 allowed | +| | | +| `?18.42` | `FCOM` index too large: reduce program size | +| `?19.72` | Logarithm of zero | +| `?21.57` | Square root of a negative number | +| `?22.65` | Numeric overflow: too many digits in a string | +| `?23.18` | `OUTPUT` `ABORT` or `CLOSE` requested too much space | +| `?23.37` | Output file overflow: recover with: `O O name;O A FLEN()` | +| __`?23.82`__ | Cannot open output file (file open, too big or no name) | +| __`?24.05`__ | No output file to resume | +| | | +| `?24.25` | Illegal `OPEN` command | +| `?24.35` | Illegal `RESUME` command | +| __`?24.40`__ | Input file not found (wrong name? wrong device?) | +| `?24.47` | No input file to restart | +| __`?24.52`__ | No input file to resume | +| `?25.02` | Stack overflow: reduce nested subroutines and expressions | +| __`?25.60`__ | Device does not exist or illegal 2-page handler | +| `?26.07` | Illegal `LIBRARY` command | +| `?26.32` | File specified is already deleted (wrong extension?) | +| | | +| `?26.39` | File loaded is not a FOCAL program - __better reload UWF!__ | +| `?26.56` | Program requested is missing (wrong device?) | +| `?26.66` | `LIBRARY SAVE` error: no name, device full, or no directory | +| `?27.18` | Attempted `LIBRARY` operation on a device without a directory | +| `?27.75` | No length specified in a `LIBRARY ZERO` command | +| `?27.90` | Zero divisor | +| `?29.25` | Cannot use the '<>' construction with `OPEN OUTPUT` | +| `?29.38` | Device error (write-lock, bad checksum or illegal request) | + +`_` Indicates EOF detected in input - I/O continues from terminal + +`?....?` TRACE feature: Text enclosed by `?` marks is typed during execution +to help find the source of an error. The value of each expression in a SET +command is also printed Index: etc/pidp8i-init.in ================================================================== --- etc/pidp8i-init.in +++ etc/pidp8i-init.in @@ -103,11 +103,11 @@ fi # We want SIMH to have a sensible working directory: somewhere the # user can write files and which makes sense when giving SIMH # commands involving file paths. This default is chosen because it - # satisfies both criteria, plus it makes tools/mk-os8-rk05s happy. + # satisfies both criteria, plus it makes tools/mkos8 happy. # If you change the default here, change that script as well. cd "$prefix/share/media" log_daemon_msg "Starting PiDP-8/I simulator" "pidp8i" screenu -dmS pidp8i "$sim" $bscript Index: examples/README.md ================================================================== --- examples/README.md +++ examples/README.md @@ -30,35 +30,35 @@ ## How to Use the BASIC Examples To use the example BASIC program, simply transcribe it into OS/8 BASIC: .R BASIC - NEW OR OLD--NEW - FILE NAME--PAL001.BA - - READY - 10 FOR I = 1 TO 999 - 10 FOR I = 1 TO 999 - 20 A = I / 3 \ B = I / 5 - 30 IF INT(A) = A GOTO 60 - 40 IF INT(B) = B GOTO 60 - 50 GOTO 70 - 60 T = T + I - 70 NEXT I - 80 PRINT "TOTAL: "; T - 90 END - SAVE - - READY - RUN - - PAL001 BA 4A - - TOTAL: xxxxxxx - - READY - BYE + NEW OR OLD--NEW + FILE NAME--PAL001.BA + + READY + 10 FOR I = 1 TO 999 + 10 FOR I = 1 TO 999 + 20 A = I / 3 \ B = I / 5 + 30 IF INT(A) = A GOTO 60 + 40 IF INT(B) = B GOTO 60 + 50 GOTO 70 + 60 T = T + I + 70 NEXT I + 80 PRINT "TOTAL: "; T + 90 END + SAVE + + READY + RUN + + PAL001 BA 4A + + TOTAL: xxxxxxx + + READY + BYE If you're SSH'd into the PiDP-8/I, "transcribing" is simply a matter of cut-and-paste into the terminal window. I've obscured the output on purpose, since I don't want this page to be @@ -166,11 +166,11 @@ If you have the finished assembly code already on your computer and are SSH'd into the PiDP-8/I machine, there is a shortcut for all of the above. At the OS/8 command line, say: .R PIP - *ADD.PA $") + child.sendline(send_line) + + +#### os8_kbd_delay ##################################################### +# Artificially delay the media generation process to account for the +# fact that OS/8 lacks a modern multi-character keyboard input buffer. +# It is unsafe to send text faster than a contemporary terminal could. +# +# The constant is expanded to show that it comes from the 2400 bps of +# the VT05s used by some well-heeled PDP-8 users divided by 7-bit ASCII +# plus necessary start, stop, and parity bits. The constant is inverted +# to get seconds per character instead of characters per second. OS/8 +# must be at least this fast, being contemporary with the VT05. +# +# You might think to try and speed this up by increasing the 2400 bps +# value below. For one thing, the 9600 bps VT52 was probably used with +# some later OS/8 systems. Also, the higher IPS rate of our simulated +# PDP-8 must help here. However, if you actually time doing so, it +# doesn't help, presumably because other I/O overheads (e.g. the TU56 +# reading time) swamp any improvement you can get here. + +_kbd_delay = 1 / (2400 / (7 + 1 + 1 + 1)) +def os8_kbd_delay(): + time.sleep(_kbd_delay) + + +#### os8_send_line ##################################################### +# Core of os8_pmt_send. Also used by code that needs to send text +# "blind" to OS/8, without expecting a prompt, as when driving EDIT. + +def os8_send_line (send_line): + global child + + for i in xrange(0, len (send_line)): + child.send (send_line[i]) + os8_kbd_delay () + child.send ("\r") + + +#### os8_pmt_send ###################################################### +# Wait for an OS/8 command prompt running within SIMH, then send the +# given line. +# +# The prompt string is passed in because OS/8 has several different +# prompt types. + +def os8_pmt_send (prompt, send_line): + global child + + child.expect ("\n%s$" % prompt) + os8_send_line (send_line) + + +#### os8_send_ctrl ##################################################### +# Send a control character to OS/8 corresponding to the ASCII letter +# given. We precede it with the OS/8 keyboard delay, since we're +# probably following a call to os8_send_line or os8_pmt_send. + +def os8_send_ctrl (char): + global child + os8_kbd_delay () + child.send (chr (ord (char[0].upper ()) - ord ('@'))) + + +#### os8_restart ####################################################### +# Called while in the SIMH command prompt, this restarts OS/8. +# +# This one-line function exists to abstract the method we use and to +# document the reason we do it this way. +# +# Currently we do this by calling the OS/8 command entry point, which +# has the virtue that it forces another . prompt, which keeps the send/ +# expect sequencing simple when switching between OS/8 and SIMH command +# modes. +# +# That is why we don't use "cont" here instead: it requires that the +# caller always be aware of when the . prompt went out; complicated. +# +# Another simple alternative is "boot rk0", which actually benchmarks a +# smidge faster here. We choose this method instead because we expect +# that some of our callers will want to do several different things in +# a single OS/8 session, which rebooting would screw up. + +def os8_restart (): + simh_send("go 7600") + + +#### back_to_simh ###################################################### +# Go back to SIMH when the passed prompt string is encountered + +def back_to_simh (prompt): + global child + + child.expect("\n%s$" % prompt) + os8_kbd_delay() + child.sendcontrol('e') + + +#### check_exists ###################################################### +# Check existence of all files needed + +def check_exists (image_copyins): + global child + + # Confirm necessary media images exist. + for copyin in image_copyins: + image = copyin[1] + image_path = dirs.media + image + if (not os.path.isfile(image_path)): + print "Required file: " + image_path + " not found." + simh_send("q") + child.expect(pexpect.EOF) + exit (-1) + # else: print "Found " + image_path + + +#### Data Structures ################################################## +# +# The make procedures use helper procedures +# to confirm that the relevant input image file exists and +# to perform the file copies. +# +# A data structure called "image copyin" +# describes the image file pathname relative to an implied root, +# provides a message string when the action is run, +# names a default destination device for whole image content copies, +# offers an optional array of specific file copy actions. +# +# FUTURE: Parse source path for ".tu56" vs. ".rk05" for more general use. +# Currently all code assumes a copyin comes from a DECtape image. +# +# Example: We Install all files for ADVENT, the Adventure game: +# +# advent_copyin = ['RKB0:', 'subsys/advent.tu56', "Installing ADVENT...", None] +# +# A DECtape device is chosen for attachment in SIMH and +# a 'COPY *.*' command is filled in with the Destination device, and the chosen DECtape. +# +# A data structer called "file copyin" +# provides override destination to allow renames or varied destinations. +# names individual files within a copyin to use +# +# Example: To copy the C compiler we want all .SV files on SYS +# but everything else to RKB0: +# (Note the useful /V option to invert the match.) +# +# cc8_sv_file_copyin = ['SYS:', '*.SV'] +# cc8_rest_file_copyin = ['RKB0:', '*.SV/V'] +# +# A 'COPY' command is filled in with the override destination and +# The file spec is used with the chosen dectape instead of "*.*" +# + +#### copyin_pair ####################################################### +# Copy two images into two destinations with two messages +# +# Assumes our context is "in simh". +# Assumes dt0 and dt1 are free. +# Assumes rk0 is the boot device +# Detaches dt0 and dt1 after using them. +# copyin0 mounts on dt0. copyin1 mounts on dt1. +# Either copyin or both can be None + +def copyin_pair (copyin0, copyin1): + if debug: + if copyin0: + print "Copying: " + copyin0[1] + " to: " + copyin0[0] + "from dt0" + else: print "copyin0 is empty." + if copyin1: + print "Copying: " + copyin1[1] + " to: " + copyin1[0] + "from dt1" + else: print "copyin1 is empty." + + if not copyin0 and not copyin1: return # Nothing to do. + + # The order of events here is a bit funky because we want + # to use both DECtape drives but also + # switch between SIMH and OS/8 as infrequently as possible. + + if copyin0: simh_send("attach -r dt0 " + dirs.media + copyin0[1]) + if copyin1: simh_send("attach -r dt1 " + dirs.media + copyin1[1]) + + os8_restart() + + if copyin0: + if progmsg: print copyin0[2] + if copyin0[3]: # We have specific files to do. + for file_copyin in copyin0[3]: + os8_pmt_send("\.", "COPY " + file_copyin[0] + " + pkg_resources.parse_version("4.0")) + child.delaybeforesend = None if pev4 else 0 + + # Log SIMH and OS/8 output to a file by default, but send it to the + # console instead of the progress messages if -v was given using the + # trick from https://stackoverflow.com/questions/21239338 + child.logfile = open(dirs.log + 'mkos8-' + first_act + '.log', 'w') \ + if progmsg else os.fdopen(sys.stdout.fileno(), 'w', 0) + + if acts["bin"]: make_bin (ap.args) + if acts["src"]: make_src (ap.args) + + simh_send("q") + child.expect(pexpect.EOF) + if progmsg: print "Done!" + + +if __name__ == "__main__": + main() ADDED media/os8/README.md Index: media/os8/README.md ================================================================== --- /dev/null +++ media/os8/README.md @@ -0,0 +1,74 @@ +# OS/8 Media + + +## Bootable Systems + +`os8v3d-bin.rk05` — Bootable OS/8 system made from the binary DECtapes +of OS/8 V3D in the Willem van der Mark Archives described below plus +other tapes in the [`subsys` subdirectory](/file/media/os8/subsys) +during the PiDP-8/I software build process. This is the disk image used +by boot options IF=0 and IF=7. See the [top-level `README.md` +file][tlrm] for instructions on controlling what goes into this image. + +`os8.tu56` — Bootable OS/8 DECtape image used by boot option IF=3. +Primarily intended to demonstrate the uncommon "boot and run from tape" +experience offered by early DEC systems. + + +## Data Disks + +`os8v3d-src.rk05` — OS/8 Source RK05 pack made from the source DECtapes +of OS/8 V3D in the Willem van der Mark Archives, described below. This +is not a bootable OS/8 system. It is merely a convenience for use with a +bootable OS/8 system, so you can avoid mounting the seven "Source" +distribution tapes in succession. + +If you give the following command to SIMH: + + att rk1 media/os8/os8v3d-src.rk05 + +...this disk will appear as RKA1: and RKB1: under OS/8. + + +## Willem van der Mark Archives + +Willem van der Mark wrote a really cool [PDP-8 emulator in Java][vdms]. +He has a well organized archive of DEC media. The following DECtapes +were copied from his [OS8-V3D software archive][vdms]. + +The OS/8 Binary DECtapes were compared against those in [Dave Gesswein's +Archive][dga] and found to be nearly identical. Of the three tapes +reviewed, a total of 2 words differed. Further comparison is possible as +a project for an interested person. + +| DECtape Image File Name | Content Description +| ---------------------------------------------------------------------------- +| `al-4711c-ba-os8-v3d-1.1978.tu56` | DEC OS/8 V3D **Binary** Distribution 1/2 +| `al-4712c-ba-os8-v3d-2.1978.tu56` | DEC OS/8 V3D **Binary** Distribution 2/2 +| `al-4761c-ba-os8-v3d-ext.1978.tu56` | DEC OS/8 V3D Extensions **Binary** Distribution 1/1 +| `al-4549d-ba-fr4-v3d-1.1978.tu56` | DEC OS/8 V3D FORTRAN IV **Binary** Distribution 1/2 +| `al-5596d-ba-fr4-v3d-2.1978.tu56` | DEC OS/8 V3D FORTRAN IV **Binary** Distribution 2/2 +| `al-5642a-ba-macrel-linker.1978.tu56` | DEC OS/8 V3D MACREL/LINKER **Binary** Distribution +| `al-4691c-sa-os8-v3d-1.1978.tu56` | DEC OS/8 V3D **Source** Distribution 1/7 +| `al-4692c-sa-os8-v3d-2.1978.tu56` | DEC OS/8 V3D **Source** Distribution 2/7 +| `al-4693d-sa-os8-v3d-3.1978.tu56` | DEC OS/8 V3D **Source** Distribution 3/7 +| `al-4694c-sa-os8-v3d-4.1978.tu56` | DEC OS/8 V3D **Source** Distribution 4/7 +| `al-4695c-sa-os8-v3d-5.1978.tu56` | DEC OS/8 V3D **Source** Distribution 5/7 +| `al-4696c-sa-os8-v3d-6.1978.tu56` | DEC OS/8 V3D **Source** Distribution 6/7 +| `al-4697c-sa-os8-v3d-7.1978.tu56` | DEC OS/8 V3D **Source** Distribution 7/7 +| `al-4759c-sa-os8-ext-1.1978.tu56` | DEC OS/8 V3D Extensions **Source** Distribution 1/3 +| `al-4760c-sa-os8-ext-2.1978.tu56` | DEC OS/8 V3D Extensions **Source** Distribution 1/3 +| `al-5586c-sa-os8-ext-3.1978.tu56` | DEC OS/8 V3D Extensions **Source** Distribution 1/3 + + +## Other Files + +| File Name | Content Description +| ---------------------------------------------------------------------------- +| `LICENSE.md` | License provided by DEC that makes our use of OS/8 legal +| `local.tu56` | Files created for or vetted by the PiDP-8/I project; used at build time + +[dga]: http://www.pdp8online.com/images/images/misc_dectapes.shtml +[tlrm]: /doc/trunk/README.md +[vdms]: http://vandermark.ch/pdp8/index.php?n=OS8.OS8-V3D +[vdma]: http://vandermark.ch/pdp8/index.php ADDED media/os8/al-4549d-ba-fr4-v3d-1.1978.tu56 Index: media/os8/al-4549d-ba-fr4-v3d-1.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4549d-ba-fr4-v3d-1.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4691c-sa-os8-v3d-1.1978.tu56 Index: media/os8/al-4691c-sa-os8-v3d-1.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4691c-sa-os8-v3d-1.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4692c-sa-os8-v3d-2.1978.tu56 Index: media/os8/al-4692c-sa-os8-v3d-2.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4692c-sa-os8-v3d-2.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4693d-sa-os8-v3d-3.1978.tu56 Index: media/os8/al-4693d-sa-os8-v3d-3.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4693d-sa-os8-v3d-3.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4694c-sa-os8-v3d-4.1978.tu56 Index: media/os8/al-4694c-sa-os8-v3d-4.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4694c-sa-os8-v3d-4.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4695c-sa-os8-v3d-5.1978.tu56 Index: media/os8/al-4695c-sa-os8-v3d-5.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4695c-sa-os8-v3d-5.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4696c-sa-os8-v3d-6.1978.tu56 Index: media/os8/al-4696c-sa-os8-v3d-6.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4696c-sa-os8-v3d-6.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4697c-sa-os8-v3d-7.1978.tu56 Index: media/os8/al-4697c-sa-os8-v3d-7.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4697c-sa-os8-v3d-7.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4711c-ba-os8-v3d-1.1978.tu56 Index: media/os8/al-4711c-ba-os8-v3d-1.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4711c-ba-os8-v3d-1.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4712c-ba-os8-v3d-2.1978.tu56 Index: media/os8/al-4712c-ba-os8-v3d-2.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4712c-ba-os8-v3d-2.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4759c-sa-os8-ext-1.1978.tu56 Index: media/os8/al-4759c-sa-os8-ext-1.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4759c-sa-os8-ext-1.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4760c-sa-os8-ext-2.1978.tu56 Index: media/os8/al-4760c-sa-os8-ext-2.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4760c-sa-os8-ext-2.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-4761c-ba-os8-v3d-ext.1978.tu56 Index: media/os8/al-4761c-ba-os8-v3d-ext.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-4761c-ba-os8-v3d-ext.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-5586c-sa-os8-ext-3.1978.tu56 Index: media/os8/al-5586c-sa-os8-ext-3.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-5586c-sa-os8-ext-3.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-5596d-ba-fr4-v3d-2.1978.tu56 Index: media/os8/al-5596d-ba-fr4-v3d-2.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-5596d-ba-fr4-v3d-2.1978.tu56 cannot compute difference between binary files ADDED media/os8/al-5642a-ba-macrel-linker.1978.tu56 Index: media/os8/al-5642a-ba-macrel-linker.1978.tu56 ================================================================== --- /dev/null +++ media/os8/al-5642a-ba-macrel-linker.1978.tu56 cannot compute difference between binary files ADDED media/os8/init.tx.in Index: media/os8/init.tx.in ================================================================== --- /dev/null +++ media/os8/init.tx.in @@ -0,0 +1,11 @@ + +PiDP-8/I @VERSION@ - OS/8 V3D - KBM V3Q - CCL V1F +CONFIGURED BY @BUILDUSER@ ON @BUILDDATE@ + +RESTART ADDRESS =07600 + +TYPE: + .DIR - TO GET A LIST OF FILES ON DSK: + .DIR SYS: - TO GET A LIST OF FILES ON SYS: + .R PROGNAME - TO RUN A SYSTEM PROGRAM + .HELP FILENAME - TO TYPE A HELP FILE ADDED media/os8/local.tu56 Index: media/os8/local.tu56 ================================================================== --- /dev/null +++ media/os8/local.tu56 cannot compute difference between binary files DELETED media/os8/os8.rk05 Index: media/os8/os8.rk05 ================================================================== --- media/os8/os8.rk05 +++ /dev/null cannot compute difference between binary files ADDED media/os8/subsys/README.md Index: media/os8/subsys/README.md ================================================================== --- /dev/null +++ media/os8/subsys/README.md @@ -0,0 +1,59 @@ +# Software Subsystems + + +## Directory Contents + +The files in this directory are used during the PiDP-8/I software build +process along with the DEC original tape images in [our parent +directory](/files/media/os8) to build the `os8v3d-bin.rk05` disk image +used by boot options IF=0 and IF=7. + +These files are here rather than one level up because they are not part +of OS/8 *per se*, but one way or another, they do currently require +OS/8. Some of the data on these tapes could potentially be used with +other PDP-8 operating systems, but at minimum, it would require +translating the data from OS/8 tape format. + + +| DECtape Image File Name | Content Description +| ---------------------------------------------------------------------------- +| `advent.tu56` | [OS/8 Adventure][os8a] v2.4 +| `ba.tu56` | Several BASIC programs in OS/8 \*.BA file format from DEC's book ["101 BASIC Computer Games"][bcg] +| `cc8.tu56` | Ian Schofield's [CC8 OS/8 C compiler system][cc8] +| `k12.tu56` | [Kermit-12][k12] for OS/8, OS/78, OS/278, and OS/12 +| `music.tu56` | [RFI-based][rfi] music playback programs + + +## Controlling the Build Process + +Most of these files are merged into the OS/8 binary disk image by +default, but can be excluded by giving a `--disable-os8-NAME` flag to +the `configure` script, where `NAME` is the file name above without the +`.tu56` extension. (e.g. `--disable-os8-k12` excludes Kermit-12.) + +Only one of the files above is currently excluded by default, that being +`music.tu56`, because we have not yet received any report of reliable +playback. We believe this is because the PiDP-8/I realization does not +lend itself to creation of suitable AM frequency RFI. These programs +were written for real PDP-8 hardware which had much longer wires backed +by much stronger drivers than a PiDP-8/I, and which ran at lower +frequencies than a Raspberry Pi. These problems are not insurmountable, +so someone interested in the project may force inclusion of these files +on the OS/8 RK05 boot disk with + + $ ./configure --enable-os8-music + +Solving this problem may require hardware modifications. If so, we'll +still exclude these programs by default since not all PiDP-8/I machines +will have these modifications. + +See the [top-level `README.md` file][tlrm] for further information about +the `--enable-os8-*` and `--disable-os8-*` configuration options. + + +[bcg]: https://archive.org/details/bitsavers_decBooks10Mar75_26006648 +[cc8]: https://groups.google.com/d/msg/pidp-8/ycs_KOI4vdg/Zr0bifJxAgAJ +[k12]: http://www.columbia.edu/kermit/pdp8.html +[os8a]: http://www.rickmurphy.net/advent/ +[rfi]: https://en.wikipedia.org/wiki/Electromagnetic_interference +[tlrm]: /doc/trunk/README.md ADDED media/os8/subsys/advent.tu56 Index: media/os8/subsys/advent.tu56 ================================================================== --- /dev/null +++ media/os8/subsys/advent.tu56 cannot compute difference between binary files ADDED media/os8/subsys/ba.tu56 Index: media/os8/subsys/ba.tu56 ================================================================== --- /dev/null +++ media/os8/subsys/ba.tu56 cannot compute difference between binary files ADDED media/os8/subsys/cc8.tu56 Index: media/os8/subsys/cc8.tu56 ================================================================== --- /dev/null +++ media/os8/subsys/cc8.tu56 cannot compute difference between binary files ADDED media/os8/subsys/focal69.tu56 Index: media/os8/subsys/focal69.tu56 ================================================================== --- /dev/null +++ media/os8/subsys/focal69.tu56 cannot compute difference between binary files ADDED media/os8/subsys/k12.tu56 Index: media/os8/subsys/k12.tu56 ================================================================== --- /dev/null +++ media/os8/subsys/k12.tu56 cannot compute difference between binary files ADDED media/os8/subsys/music.tu56 Index: media/os8/subsys/music.tu56 ================================================================== --- /dev/null +++ media/os8/subsys/music.tu56 cannot compute difference between binary files ADDED media/os8/subsys/uwfocal-v4e-1.tu56 Index: media/os8/subsys/uwfocal-v4e-1.tu56 ================================================================== --- /dev/null +++ media/os8/subsys/uwfocal-v4e-1.tu56 cannot compute difference between binary files ADDED media/os8/subsys/uwfocal-v4e-2.tu56 Index: media/os8/subsys/uwfocal-v4e-2.tu56 ================================================================== --- /dev/null +++ media/os8/subsys/uwfocal-v4e-2.tu56 cannot compute difference between binary files Index: tools/bosi ================================================================== --- tools/bosi +++ tools/bosi @@ -72,10 +72,13 @@ fi set -x apt-get update && apt-get -y upgrade || true + + apt-get install python-pip || true + pip install pexpect test -f /usr/include/curses.h || apt-get -y install libncurses-dev test -h /media/usb || apt-get -y install usbmount if [ -z "$(type -p fossil)" ]