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/2π 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)" ]