Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch clean-os8-packs Excluding Merge-Ins
This is equivalent to a diff from c5a2a1eb11 to e95de3f1a6
2017-10-08
| ||
18:46 | Merged clean-os8-packs into trunk. check-in: faee651fc4 user: tangent tags: trunk | |
18:44 | Replaced the "Hello, world!" init message with one based loosely on the "FIELD SERVICE PDP-8 DIAGNOSTIC SYSTEM" message, but showing information more likely to be useful to our end users. Closed-Leaf check-in: e95de3f1a6 user: tangent tags: clean-os8-packs | |
18:16 | Sketched in the initial implementation of the --enable-os8-init feature, which creates INIT.TX and INIT.CM on the OS/8 bin disk just before finishing up. If the option is set, it calls SET SYS INIT to make it run on boot, but it always does get created, so the user can turn it on later without rebuilding their bin disk. This version just says "HELLO, WORLD!" We'll be doing something more substantial next. check-in: 3f8551641f user: tangent tags: clean-os8-packs | |
2017-10-06
| ||
11:33 | Merged scanswitch serial mod fix in from trunk check-in: dce9086a45 user: tangent tags: clean-os8-packs | |
11:26 | Removed a bunch of the init code from scanswitch.c: calling the new gpio-common module init functions instead. If nothing else, this allows scanswitch to work correctly for the serial mod cases for the first time! check-in: c5a2a1eb11 user: tangent tags: trunk | |
11:25 | Moved more of the gpio-common module's internal init code out as public functions, and hid away some of those now only called from within the module. check-in: fc42d83843 user: tangent tags: trunk | |
︙ | ︙ | |||
64 65 66 67 68 69 70 | December 2016 into the PiDP-8/I simulator. This is the basis for the current automatic upstream feature merge capability, which is why 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 | | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | December 2016 into the PiDP-8/I simulator. This is the basis for the current automatic upstream feature merge capability, which is why 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 `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, curated the tape collection we ship as `media/.../*.tu56`, created some of those tapes, and more. He has also contributed to other |
︙ | ︙ |
︙ | ︙ | |||
43 44 45 46 47 48 49 50 51 52 53 54 55 56 | -DHAVE_REGEX_H -DHAVE_GLOB -DSIM_GIT_COMMIT_ID=$(SGCID) \ -D_GNU_SOURCE -U__STRICT_ANSI__ \ -I @srcdir@/src -I @srcdir@/src/PDP8 -I src SIM = bin/pidp8i-sim BINS = bin/palbart $(SIM) bin/pidp8i-test bin/ptp2txt libexec/scanswitch BUILDDIRS = bin libexec obj/PDP8 INSTDIRS = bin etc libexec share/boot share/media share/man/man1 OBJS = \ obj/gpio-common.o \ obj/PDP8/pdp8_df.o \ | > > > > > > > > > > > > > > > > | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | -DHAVE_REGEX_H -DHAVE_GLOB -DSIM_GIT_COMMIT_ID=$(SGCID) \ -D_GNU_SOURCE -U__STRICT_ANSI__ \ -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 = \ obj/gpio-common.o \ obj/PDP8/pdp8_df.o \ |
︙ | ︙ | |||
115 116 117 118 119 120 121 122 123 | @srcdir@/boot/2.script.in \ @srcdir@/boot/3.script.in \ @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@/src/gpio-common.c.in \ @srcdir@/src/PDP8/pidp8i.c.in \ | > | > > > > > | > > | | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | @srcdir@/boot/2.script.in \ @srcdir@/boot/3.script.in \ @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 \ $(MKOS8_DIN) PRECIOUS_INFILES = \ @srcdir@/Makefile.in \ @srcdir@/examples/Makefile.in \ @srcdir@/src/Makefile.in \ @srcdir@/src/PDP8/Makefile.in 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) $(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 @rm -f $(PRECIOUS_OUTFILES) \ config.log \ autosetup/jimsh0 \ src/config.h ctags tags: @ctags -R @srcdir@ @srcdir@/libexec/mkos8 ifeq (@HAVE_PROG_CSCOPE@, 1) @cscope -bR -s@srcdir@ endif install: all @echo Installing to @prefix@... |
︙ | ︙ | |||
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | -e 's/kgdboc\=[a-zA-Z0-9]+,[0-9]+ //' -i $(CLTXT) \ ) || true @# Install palbart stuff @INSTALL@ -m 755 bin/palbart @prefix@/bin @INSTALL@ -m 644 @srcdir@/palbart/palbart.1 @prefix@/share/man/man1 mediainstall: @echo "[Re]installing OS and program media..." @cd @srcdir@ ; \ find media \( \ -name \*.bin -o \ -name \*.dsk -o \ -name \*.rk05 -o \ -name \*.tu56 \ | > > > > > > > > > > > > > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | -e 's/kgdboc\=[a-zA-Z0-9]+,[0-9]+ //' -i $(CLTXT) \ ) || true @# 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 -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: $(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! obj/%.o: @srcdir@/src/%.c $(CC) -c $(CFLAGS) @srcdir@/src/$*.c -o obj/$*.o |
︙ | ︙ |
︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | your user has full write access to that directory. * 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: $ ./configure && make && sudo make install If you've checked out a new version of the source code and the `make` 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 directory on the Pi with this command: $ ./configure --prefix=$HOME/pidp8i && sudo make install 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. #### --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 in the original PDP-8/I. (We call this the ILS for short.) This feature currently takes too much CPU power to run on anything but a multi-core Raspberry Pi, currently limited to the Pi 2 and Pi 3 series. If you configure the software on a single-core Pi — models A+, B+, and Zero — the simulator uses the original low-CPU-usage LED driving method instead. (a.k.a. NLS for short, named after this configuration option.) Those on a multi-core host who want this low-CPU-usage LED driving method can give the `--no-lamp-simulator` option to `configure`. This 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. If you do not give this flag at `configure` time with these hardware modifications in place, the front panel will not work correctly, and trying to run the software may even crash the Pi. 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 these modifications to the PiDP-8/I circuit design. See the linked mailing list thread for details. As with `--serial-mod`, you should only enable this flag if you have actually done the mods as specified by James L-W. #### --throttle 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. #### --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: 1. **The binary PDP-8 media files**, such as the RK05 disk image that | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | your user has full write access to that directory. * 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` <a name="configuring"></a> ## 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: $ ./configure && make && sudo make install If you've checked out a new version of the source code and the `make` 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. <a name="options"></a> ### 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: <a name="prefix"></a> #### --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 directory on the Pi with this command: $ ./configure --prefix=$HOME/pidp8i && sudo make install 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. <a name="lowercase"></a> #### --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 <a name="nls"></a> #### --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 in the original PDP-8/I. (We call this the ILS for short.) This feature currently takes too much CPU power to run on anything but a multi-core Raspberry Pi, currently limited to the Pi 2 and Pi 3 series. If you configure the software on a single-core Pi — models A+, B+, and Zero — the simulator uses the original low-CPU-usage LED driving method instead. (a.k.a. NLS for short, named after this configuration option.) Those on a multi-core host who want this low-CPU-usage LED driving method can give the `--no-lamp-simulator` option to `configure`. This 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]. <a name="serial-mod"></a> #### --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. If you do not give this flag at `configure` time with these hardware modifications in place, the front panel will not work correctly, and trying to run the software may even crash the Pi. 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. <a name="alt-serial-mod"></a> #### --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 these modifications to the PiDP-8/I circuit design. See the linked mailing list thread for details. As with `--serial-mod`, you should only enable this flag if you have actually done the mods as specified by James L-W. #### --throttle 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. <a name="disable-os8"></a> #### --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. <a name="enable-os8"></a> #### --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. <a name="overwrite-setup"></a> ## 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: 1. **The binary PDP-8 media files**, such as the RK05 disk image that |
︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 178 179 180 181 182 | mediainstall` command after `sudo make install`. 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 $ pidp8i-test | > | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | mediainstall` command after `sudo make install`. 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! <a name="testing"></a> ## Testing You can test your PiDP-8/I LED and switch functions with these commands: $ sudo systemctl stop pidp8i $ pidp8i-test |
︙ | ︙ | |||
190 191 192 193 194 195 196 197 198 199 200 201 202 203 | the PiDP-8/I simulator back up with: $ 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. The largest user-visible difference between the two software | > | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | the PiDP-8/I simulator back up with: $ sudo systemctl start pidp8i See [its documentation][test] for more details. <a name="using"></a> ## 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. The largest user-visible difference between the two software |
︙ | ︙ |
︙ | ︙ | |||
31 32 33 34 35 36 37 | define defaultprefix /opt/pidp8i use cc use cc-lib options { | > | > | > > > > > > > > > > > > > | < | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | define defaultprefix /opt/pidp8i use cc use cc-lib options { 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 } 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 } if {[opt-bool debug-mode]} { 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 "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] set cores [exec $srcdir/tools/corecount] # 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" } 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 # 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 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" } 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 } else { 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 cc-check-functions clock_nanosleep nanosleep usleep 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." } elseif {![cc-check-function-in-lib initscr ncurses]} { 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 } 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." } } else { 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] # Canonicalize some paths which may be relative and generate others from them define ABSPREFIX [file-normalize [get-define prefix]] define BOOTDIR "[get-define ABSPREFIX]/share/boot" define MEDIADIR "[get-define ABSPREFIX]/share/media" # Remember the name and primary group of the user who installed this, since # 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!" } 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." # Can we use any nonstandard flags here? We don't bother including # flags that both GCC and Clang support. The ones inside the "if" # block are those that Clang will accept in an autosetup test but # 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 } } # 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 # fall back on some lame "UNKNOWN" version string because that would # mask a real problem that needs to be diagnosed. |
︙ | ︙ | |||
222 223 224 225 226 227 228 229 230 231 | user-error "$tool failed to get checkout version from $path" } else { user-error "$tool failed: $path does not exist." } } define VERSION $version # 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"] | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 | user-error "$tool failed to get checkout version from $path" } else { 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" 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_*} 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 make-template boot/4.script.in 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" |
1 | ; This script initializes a populated OS/8 environment on an | | | 1 2 3 4 5 6 7 8 9 | ; This script initializes a populated OS/8 environment on an ; 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:". ; ; See 3.script if you want to load OS/8 via DECtape instead. |
︙ | ︙ | |||
23 24 25 26 27 28 29 | ; reset echo Loading OS/8 from the RK05 cartridge disk... set cpu 32k set cpu noidle set df disabled @SET_THROTTLE@ | > > > > > > > > > > > > > > | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | ; reset echo Loading OS/8 from the RK05 cartridge disk... set cpu 32k set cpu noidle set df disabled @SET_THROTTLE@ @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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 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 *OUTPUT<INPUT,INFO(OPTIONS) ## Command Line Interpretation 1. If no input and no output specified then delete <kbd>DSK:DCPLS.TM<kbd> 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 *DEASS<DEASS.V if DEASS.SM does not exist, and to *DEASS<DEASS.SV,DEASS.SM otherwise. In this case a previous version of the output file is deleted first (if necessary). 4. If the output file has no explicit extension then add .DC if a source is produced, and .LS otherwise (One would expect .PA instead of .DC but that proved dangerous.) ## Options Affecting Interpretation of Command Line | /B | Expect .BN rather than .SV format in first inputfile | | | This changes the default extension into .BN if no | | | input is specified. | | /L | Produce .LS rather than .DC output | | /X | Chain to CREF.SV | | | (1st output becomes input and 2nd output becomes output) | | | This option implies the options /L and /T | e.g. .R DCP *DEASS,TTY:</X/B is equivalent to .R PIP *DEASS.LS</D$ .R DCP *DEASS.LS<DEASS.BN,DEASS.SM/L/T/B .R CREF *TTY:<DEASS.LS also DCP * *DEAS.SV,SPECS1,SPECS2,SPECS3/S means .R PIP *DEASS.TM</D$ .R DCP *DCPLS.TM<DEASS.SV,SPECS1,SPECS2,SPECS3/L/T .R CREF *DCPLS.TM ## Options | /A | Do not generate a 'START' label. | | (By default a label 'START' is generated when decoding | | core image file. This is possible since the core control | | contains the starting address.) | /B | Expect .BN instead of .SV input. | /C | The info file after the output | /D | 'JMP .-3', 'JMP I .+1' instructions | | for each reference a tag is generated) | /H | Do not generate literals. | /K | allow modification of literals. | | (Normally an instruction like 1377 will be translated as | | 'TAD (1234' but 2377 as 'ISZ A177' since no decent programmer | | ever writes 'ISZ (1234'. It was found however that several | | DEC programs contain such constructs.) | /L | Produce output in .LS format. | /N | Do not generate table of undefined symbols. | /S | Generate table of all symbols. | /T | Convert tabs into spaces. | /W | Do not interpret 6141 as the PDP12 'LINC' instruction. | /X | Chain to CREF.SV. | /(F) | (Where F designates a digit between 0 and 7.) | | Translate field F of the program (default: /0) | =NNNNMMMM | The = OPTION can be used to specify a part of the program to be decoded. NNN gives begin and MMMM end+1 of the range. (Note that if begin>3777 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 $ . $\<CR\> 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 | |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 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 ## <a id="starting" name="stopping"></a>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 <kbd>Ctrl-C</kbd>. ## <a id="loading" name="saving"></a>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. ### <a id="ls-pasting"></a>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. ### <a id="ls-pip"></a>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.DA<TTY: ⇠ use default extension for O I 01.10 TYPE "Hello, world!"! ^Z ⇠ Ctrl-Z is the EOF marker in OS/8 *^C ⇠ return to OS/8 from PIP .R UWF16K ⇠ run U/W FOCAL *O I HELLO ⇠ open text file for input; "types" pgm in for us _G ⇠ EOF seen, program started Hello, world! ⇠ and it runs! That is, we use OS/8's `PIP` command to accept text input from the terminal (a.k.a. TTY = teletype) and write it to a text file. Then we load that text in as program input using commands we'll explain in detail [below](#ls-write). [mfte]: https://duckduckgo.com/?q=%22my+favorite+text+editor%22 ### <a id="ls-punch"></a>The `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. ### <a id="ls-library"></a>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. ### <a id="ls-write"></a>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. ### <a id="ls-hard-way"></a>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.FD<TEST.FD #R ? #1L *W #1D #/L *OUTPUT CLOSE #/D #L C U/W-FOCAL: 16K-V4 NO/DA/TE 01.10 T "HELLO",! #E The [previous method](#ls-write) avoids all of that `EDIT` ugliness. Now let's load it back up into U/W FOCAL and try to run it: .R UWF16K *OPEN INPUT TEST,ECHO *C U/W-FOCAL: 16K-V4 NO/DA/TE * *01.10 T "HELLO",! *_ ⇠ hit Enter *GO HELLO Success! The `*_` pair above is the asterisk prompt printed by the FOCAL command interpreter signifying that it is ready for input followed by the underscore printed by the file handler signifying that it hit the end of file for `TEST.FD`. The hint above to hit <kbd>Enter</kbd> 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 ## <a id="lowercase"></a>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 <kbd>CAPS LOCK</kbd> 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 ## <a id="output-format"></a>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. ## <a id="ascii"></a>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` | <kbd>Ctrl-J</kbd> | | `FORM FEED` | <kbd>Ctrl-L</kbd> | ## <a id="front-panel"></a>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. ### <a id="clear-regs"></a>`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. ### <a id="if-df"></a>`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. ### <a id="sw-dir"></a>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. ### <a id="sw-order"></a>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`. ## <a id="miss-hw"></a>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. ## <a id="diffs"></a>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). ## <a id="converting"></a>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. ## <a id="license"></a>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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 | # U/W FOCAL Manual V4E, October 1978 <code> 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 </code> ## 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. ### <a id="hardware"></a>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. ### <a id="loading"></a>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. ### <a id="starting"></a>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'. <a id="opt-switch"></a> | 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. ### <a id="ctrl-keys"></a>UWF's Control Keys UWF recognizes the following control keys: 1. <kbd>CTRL/F</kbd> is the master program break key: it will restart UWF at any time, assuming that the program is still running. 2. <kbd>CTRL/C</kbd> is the monitor break key. It will eventually trap to a resident 'ODT' package which is not yet implemented. 3. <kbd>CTRL/S</kbd> (`XOFF`) stops output to the terminal. This provides users with video terminals time to inspect their results. 4. <kbd>CTRL/Q</kbd> (`XON`) resumes output to the terminal. Some terminals issue `XON`/`XOFF` codes automatically when the screen fills up. 5. The <kbd>RETURN</kbd> key is used to terminate all command lines. UWF will not recognize a command until the RETURN key is typed. 6. The <kbd>RUBOUT</kbd> or <kbd>DELETE</kbd> 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 <kbd>LINE FEED</kbd> key is used to retype a command line *before* typing <kbd>RETURN</kbd> 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 <kbd>CTRL/F</kbd>. This is accomplished by holding down the <kbd>CTRL</kbd> 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 ## <a id="dir-ind-cmd"></a>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. ### <a id="line-numbers"></a>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. ### <a id="punctuation"></a>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`. ## <a id="numbers-variables" name="numbers"></a>Numbers and Variables UWF can handle up to 10-digit numbers with a magnitude range of 10<sup>615</sup>. 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. ### <a id="var-names" name="variables"></a>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. ### <a id="symbol-table"></a>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. ### <a id="protected-vars"></a>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. ### <a id="subscripted-vars"></a>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)`. <a id="dsub"></a> 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. ## <a id="operators"></a>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). ### <a id="operator-priority"></a>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`. ### <a id="enclosures"></a>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*<B=2+3>-5]^2 is evaluated as 'four hundred' with `A`=20 and `B`=5. ## <a id="abbreviations"></a>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. ## <a id="editing"></a>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 <kbd>DELETE</kbd> 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 <kbd>LINE FEED</kbd> 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 <kbd>RUBOUT</kbd>, the <kbd>BACKARROW</kbd> (or <kbd>UNDERLINE</kbd> 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 <kbd>RETURN</kbd> although you may wish to do so to get back to the left margin again. Note that <kbd>LINE FEED</kbd> will not echo a blank link and <kbd>RUBOUT</kbd> will stop echoing a `\` when it reaches the beginning of the line. The use of <kbd>BACKARROW</kbd> as a 'line-kill' character necessarily means that this character (and <kbd>RUBOUT</kbd>, 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 <kbd>RETURN</kbd> 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. ### <a id="write"></a>`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! ### <a id="erase"></a>`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. ### <a id="modify" name="move"></a>`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 | | ----------------------------------------- | ---- | ------------------------------------------------- | | <kbd>CTRL/F</kbd> | — | Aborts the command — the line is unchanged | | <kbd>CTRL/G</kbd> | BELL | Rings bell and waits for a new search character | | <kbd>CTRL/J</kbd> | LF | Copies the rest of the line without changes | | <kbd>CTRL/L</kbd> | FF | Looks for the next occurrence of search character | | <kbd>CTRL/M</kbd> | CR | Terminates the line at this point | | <kbd>BACKARROW</kbd>/<kbd>UNDERLINE</kbd> | — | Deletes all chars to the left, except line number | | <kbd>RUBOUT</kbd>/<kbd>DELETE</kbd> | — | Deletes previous character, as in command mode | The last two operations are similar to those available during command mode except that <kbd>BACKARROW</kbd> or <kbd>UNDERLINE</kbd> 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 <kbd>BACKARROW</kbd> or <kbd>UNDERLINE</kbd> and then hit the <kbd>LINE FEED</kbd> key. <kbd>CTRL/G</kbd> and <kbd>CTRL/L</kbd> 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 <kbd>BELL</kbd> to change to the character you really wish to find. Or you can simply keep hitting the <kbd>FORM FEED</kbd> key to advance through the line. In case your terminal happens to respond to a <kbd>FF</kbd>, 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 <kbd>LF</kbd> as the initial search character. If you are adding new commands in the middle of a line, be sure to use the <kbd>LF</kbd> key — not the <kbd>RETURN</kbd> 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. ## <a id="text" name="look"></a>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). ## <a id="io-cmds" name="io-operators"></a>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 <kbd>RETURN</kbd> without a <kbd>LINE FEED</kbd> | | `$` | 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 <kbd>FORM FEED</kbd>, should that be desirable. The remaining operators will now be discussed in greater detail. ### <a id="bang"></a>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. ### <a id="quote"></a>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. ### <a id="symbol-table-dump"></a>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. ### <a id="formatting"></a>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. | ### <a id="tab"></a>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. ### <a id="ask-type-summary"></a>`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 <kbd>RUBOUT</kbd> 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 <kbd>BACKARROW</kbd> or <kbd>UNDERLINE</kbd> just as it is during command input. If you do attempt to use <kbd>RUBOUT</kbd>, no `\` will be echoed which serves as a reminder that this key is ignored during an `ASK` command. ### <a id="input-terminators"></a>Input Terminators UWF allows a variety of characters to serve as input terminators. In addition to the <kbd>RETURN</kbd> key, one may use a <kbd>SPACE</kbd> — 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 <kbd>COMMA</kbd>, <kbd>SEMICOLON</kbd>, or other punctuation marks such as a <kbd>QUESTION MARK</kbd> or <kbd>COLON</kbd>. A <kbd>PERIOD</kbd> 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 <kbd>/</kbd> and <kbd>-</kbd> 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`, <kbd>RUBOUT</kbd> and <kbd>LINE FEED</kbd> or <kbd>FORM FEED</kbd> 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 <kbd>ALTMODE</kbd> or <kbd>ESCAPE</kbd> 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 <kbd>ALTMODE</kbd> response will merely confirm the expected value. ## <a id="arithmetic"></a>Arithmetic Processing Commands There are four commands in this group: `SET`, `XECUTE`, `YNCREMENT` and `ZERO`. ### <a id="set"></a>`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). ### <a id="zero"></a>`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. ### <a id="yncrement"></a>`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. ### <a id="xecute"></a>`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. ## <a id="branch-control"></a>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. ### <a id="goto" name="considered-harmful"></a>`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`. ### <a id="do"></a>`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`. ### <a id="return"></a>`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. ### <a id="if"></a>`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 <W> .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. ### <a id="on"></a>`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. ### <a id="jump"></a>`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. ### <a id="link"></a>`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. ### <a id="quit"></a>`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. ## <a id="loops"></a>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. ### <a id="for"></a>`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! ### <a id="next"></a>`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. ### <a id="break"></a>`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. ## <a id="misc"></a>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. ### <a id="comment"></a>`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. ### <a id="hesitate"></a>`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 ### <a id="punch"></a>`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 <kbd>RETURN</kbd>. 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). ### <a id="open"></a>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. ### <a id="io-sel"></a>I/O Device Selection The Input and Output devices are always reset to the terminal when you hit <kbd>CTRL/F</kbd>. 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 <kbd>BACKARROW</kbd> or <kbd>UNDERLINE</kbd> 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 <kbd>CTRL/F</kbd>. <a id="write-ascii-tape"></a> 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 <kbd>RETURN</kbd> 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 <kbd>RETURN</kbd> 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 <kbd>RETURN</kbd> 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. ### <a id="echo-option" name="input-echo"></a>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 <kbd>RUBOUT</kbd> key you do not get this character printed, but rather a backslash (`\`), or on a video terminal, a three character sequence: <kbd>Backspace</kbd>, <kbd>Space</kbd>, <kbd>Backspace</kbd>, 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 <kbd>O</kbd>, <kbd>Space</kbd>, <kbd>I</kbd>, <kbd>Comma</kbd> <kbd>E</kbd> and hit <kbd>RETURN</kbd> 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 <kbd>CTRL/F</kbd> 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. ### <a id="leader-trailer"></a>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 <kbd>BACKARROW</kbd> or <kbd>UNDERLINE</kbd> 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). ### <a id="kontrol"></a>`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. | ## <a id="error-messages"></a>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). ## <a id="trace"></a>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. ## <a id="commands"></a>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` | ## <a id="internal-functions"></a>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. ### <a id="transcendentals"></a>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. #### <a id="flog"></a>`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 #### <a id="fexp"></a>`FEXP` `FEXP(x)` returns the value of *e<sup>x</sup>* 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, X<sup>3.5</sup> 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. #### <a id="fsin" name="fcos"></a>`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 <sup>1</sup>/<sub>2π</sub> 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 #### <a id="ftan" name="fatn"></a>`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 <sup>-π</sup>/<sub>2</sub> (-90°) to <sup>+π</sup>/<sub>2</sub> (+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 #### <a id="fsqt"></a>`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 #### <a id="fabs"></a>`FABS` `FABS` returns the absolute value of the argument: *TYPE FABS(-1), FABS(1)! 1.000000000E+00 1.000000000E+00 #### <a id="fsgn"></a>`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 #### <a id="fitr"></a>`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 #### <a id="frac" name="me"></a>`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. #### <a id="fmin" name="fmax"></a>`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. #### <a id="fran"></a>`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. ## <a id="char-io"></a>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. ### <a id="fin"></a>`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. ### <a id="fout"></a>`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 <kbd>ESCAPE</kbd> 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! ### <a id="find"></a>`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 <kbd>CTRL/Z</kbd>. 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. ### <a id="ftrm"></a>`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"! ### <a id="fbuf" name="fcom"></a>`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 ±2<sup>23</sup>. 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. ### <a id="fsr"></a>`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). ### <a id="fmq">`FMQ`</a> 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. ### <a id="fdin"></a>`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. ## <a id="pdfs"></a>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. <a id="named-pdf"></a>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. ## <a id="pdf-examples"></a>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. <a id="pdf-transcendentals"></a>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 e<sup>2X</sup>=(e<sup>X</sup>)<sup>2</sup>. 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. ## <a id="function-summary"></a>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 e<sup>X</sup> 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 Ⅰ ### <a id="character-codes" name="ascii-table"></a>Decimal Values for All Character Codes | Code | Character | Name | Code | Char | Code | Char | Code | Char | | ---- | --------- | ---- | ---- | ------- | ---- | ---- | ---- | ----------- | | 128 | `Ctrl/@` | NULL | 160 | `SPACE` | 192 | `@` | 224 | <code>\`</code> | | 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 | <code>\|</code> | | 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 <kbd>RETURN</kbd>/<kbd>LINE FEED</kbd> while `FOUT(13)` will just do a <kbd>RETURN</kbd>. Codes 225 through 255 are lower case letters, some of which serve other functions on keyboards without lower case. Many keyboards use <kbd>SHIFT/K</kbd> for `[`, <kbd>SHIFT/L</kbd> for `\`, and <kbd>SHIFT/M</kbd> for `]` and corresponding combinations for the control codes following <kbd>CTRL/Z</kbd>. 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. ## <a id="patches" name="we don’ need no steenkin’ patches!"></a>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<br> `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<br> `12367/ 5205 1754; 1754` — little bit more (8K only) ## <a id="error-codes"></a>Error Codes for UWF (V4E) October 1978 | Code | Meaning | | -----: | --------------------------------------------------------------------- | | ? | Keyboard interrupt (<kbd>CTRL/F</kbd>) 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` | | <span style="white-space: nowrap;">← or \_</span> | End of input sensed, I/O switched back to the terminal ## <a id="fpal"></a>`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 32<sub>10</sub> 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 7777<sub>8</sub> 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<br> Seattle, Washington 98125<br> All rights reserved (JvZ) Edits © 2017 by Warren Young<br> Released under the terms of the [SIMH License](https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md) |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 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 <style type="text/css"> @media print { h2 { page-break-before: always; } h1, h2, h3 { page-break-after: avoid; } p { page-break-before: avoid; } div.header, div.mainmenu, div.footer { display: none; visibility: hidden; } } </style> ## <a id="card1"></a>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 ## <a id="commands" name="card2"></a>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 \<OS/8 block number\> can be substituted for the name: `LTA1:<20*BN+7>`. Expressions in square brackets indicate the size: `TINY[1]`, `<LOC>[SIZE]`. ### <a id="variables"></a>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. ### <a id="fsf"></a>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) ## <a id="misc" name="card3"></a>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. ### <a id="ascii"></a>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)` ## <a id="errors" name="card4"></a>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 |
︙ | ︙ | |||
101 102 103 104 105 106 107 | echo "Bad return value $script from $scanswitch!" exit 1 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 | | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | echo "Bad return value $script from $scanswitch!" exit 1 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/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 status=$? log_end_msg $status |
︙ | ︙ |
︙ | ︙ | |||
28 29 30 31 32 33 34 | ## How to Use the BASIC Examples To use the example BASIC program, simply transcribe it into OS/8 BASIC: .R BASIC | | | | | | | | | | | | | | | | | | | | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | ## 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 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 a spoiler for the Project Euler site. |
︙ | ︙ | |||
164 165 166 167 168 169 170 | `EDIT` or MACRO-8 over PAL8 — but the idea is the same regardless. 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 | | | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | `EDIT` or MACRO-8 over PAL8 — but the idea is the same regardless. 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<TTY: Now you can simply copy the assembly language text in your desktop PC's text editor, paste it into the SSH window, and then hit Ctrl-Z to tell `PIP` that the text input from the terminal (`TTY:`) is finished. This is not only a smidge simpler than doing the same thing via `EDIT`, it also avoids a certain limitation of `EDIT` that starts to bite you once your program text exceeds about 5,600 characters. |
︙ | ︙ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # -*- coding: utf-8 -*- ######################################################################## # __init__.py - mkos8 module initialization # # Copyright © 2017 by Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## __all__ = [ 'argparser', 'dirs' ] |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | #!/usr/bin/python # -*- coding: utf-8 -*- ######################################################################## # argparse.py - Extend ArgumentParser to add mkos8 bits. # # Copyright © 2017 by Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## import argparse class ArgParser (argparse.ArgumentParser): def __init__ (self, allowed_acts): argparse.ArgumentParser.__init__ (self, description = 'Build OS/8 RK05 disk images') self.add_bool ('-v', '--verbose', help = 'verbose SIMH output instead of progress messages') self.add_bool ('--enable-music', help = 'add *.MU files to binary disk') self.add_bool ('--disable-ba', help = 'leave BASIC games and demos off binary disk') self.add_bool ('--disable-cc8', help = 'leave CC8 off binary disk') self.add_bool ('--disable-dcp', help = 'leave DCP disassembler off binary disk') self.add_bool ('--disable-focal', help = 'leave FOCAL 69 and U/W FOCAL off binary disk') self.add_bool ('--enable-focal69', help = 'install FOCAL 69 on the binary disk') self.add_bool ('--disable-uwfocal', help = 'leave U/W FOCAL (only) off binary disk') self.add_bool ('--disable-init', help = 'suppress display of the INIT message on OS/8 boot') self.add_bool ('--disable-k12', help = 'leave 12-bit Kermit off binary disk') self.add_bool ('--enable-vtedit', help = 'install and enable TECO VTEDIT mode') self.add_bool ('--disable-crt', help = 'console is a printing terminal and does not use ' + 'character overwrite on rubout') self.add_bool ('--disable-lcmod', help = 'disable the OS/8 command upcasing patch; best set ' + 'when SIMH is set to tti ksr mode') self.add_bool ('--disable-advent', help = 'leave game of Adventure off binary disk') self.add_bool ('--disable-chess', help = 'leave CHEKMO-II off binary disk') self.add_argument ( 'what', choices = allowed_acts, help = 'select which RK05 media gets built; default is "all"', nargs = argparse.REMAINDER) self.args = self.parse_args() if len (self.args.what) == 0: self.args.what = [ 'all' ] def add_bool (self, *args, **kwargs): kwargs['action'] = 'store_true' kwargs['default'] = False self.add_argument (*args, **kwargs) |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | # -*- coding: utf-8 -*- ######################################################################## # dirs.py.in - Declare constants for directory names subbed in by # autosetup, partly so we don't have to do this in multiple modules # but also so that those files don't keep getting touched whenever # other *.in files get touched, thus forcing an OS/8 RK05 rebuild. # # Copyright © 2017 by Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## import os # Anchor directories. These have to be statically defined for the # development vs installation cases because chances are high that both # trees exist on the disk, and these are absolute paths, so we can't # just do a "path exists" test to determine which we should be using. # So, autosetup subs in the proper values into the .py version of this # file in the development tree and the install script overwrites these # for the installation tree. build = "@builddir@" src = "@abs_top_srcdir@" # Derived directories. Where it matters, these are the development tree # paths, overridden or adjusted below if we're installed. bin = build + "/bin/" log = build + "/obj/" media = src + "/media/os8/" os8 = bin # Adjust paths for the "installed" case if not os.path.exists(log): # The obj/ dir doesn't exist in the install tree log = "/tmp/" if not os.path.exists(media): # We bury media one extra level deeper in the install tree media = media.replace('/media/', '/share/media/') # mkos8 outputs *.rk05 into bin/ at build time, but then they're # copied next to the os8/*.tu56 source tapes for installation. # If we're building or modifying the OS/8 media in the installation # tree, though, we need to write straight to the media directory. os8 = media |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 | #!/usr/bin/python # -*- coding: utf-8 -*- ######################################################################## # mkos8 - Build bin/os8v3d-*.rk05 from media/*/*.tu56 by scripting # commands to SIMH and OS/8. # # Copyright © 2017 by Jonathan Trites, William Cattey, and Warren Young. # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## # Bring in just the basics so we can bring in our local modules import os import sys sys.path.insert (0, os.path.dirname (__file__) + '/../lib') # Our local modules from mkos8 import * # 3rd party dependencies import pkg_resources import pexpect # Remaining Python core modules from shutil import copyfile import subprocess import time #### globals and constants ############################################# child = None # Flag set when -v is *not* given. Causes make_*() and the functions # called thereby to print progress messages to the console since SIMH # and OS/8 output is not being sent there to clue the user into the # script's progress. progmsg = True # kludgy flag to add more verbose debug output. debug = False _bin_rk05 = "os8v3d-bin.rk05" _src_rk05 = "os8v3d-src.rk05" #### simh_send ######################################################### # Wait for a SIMH command prompt and then send the given line def simh_send (send_line): global child child.expect("sim> $") 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] + "<DTA0:" + file_copyin[1]) else: os8_pmt_send("\.", "COPY " + copyin0[0] + "<DTA0:*.*") if copyin1: if progmsg: print copyin1[2] if copyin1[3]: # We have specific files to do. for file_copyin in copyin1[3]: os8_pmt_send("\.", "COPY " + file_copyin[0] + "<DTA1:" + file_copyin[1]) else: os8_pmt_send("\.", "COPY " + copyin1[0] + "<DTA1:*.*") back_to_simh("\.") if copyin0: simh_send("detach dt0") if copyin1: simh_send("detach dt1") #### do_all_copyins #################################################### def do_all_copyins (copyins): pair_idx = 0 pair_ct = int(len(copyins) / 2) while pair_idx < pair_ct: copyin_pair(copyins[pair_idx * 2], copyins[pair_idx * 2 + 1]) pair_idx += 1 if pair_ct * 2 < len(copyins): copyin_pair(copyins[len(copyins) - 1], None) #### make_bin ########################################################## # Top-level driver for the "make binary OS/8 RK05 disk image" process. # # If there is already an RK05 binary destination image, moves it aside # to a ".save" instance, overwriting the previous .save if it exists. # # One of the input images is used as a bootable DECtape. # That DECtape gets written on. # It needs stuff from tape #2 # So the first two DECtapes # are treated separately and specially. # All the other DECtape images used are read only. def make_bin (args): ro_boot_tape = "al-4711c-ba-os8-v3d-1.1978.tu56" ro_boot_tape_path = dirs.media + ro_boot_tape driver_tape = "al-4712c-ba-os8-v3d-2.1978.tu56" driver_tape_path = dirs.media + driver_tape local_tape = "local.tu56" local_tape_path = dirs.media + local_tape boot_dt_path = dirs.media + "bootable-al-4711-c-ba-os8-v3d-1.1978.tu56" special_bin_copyins = [ ["", ro_boot_tape, "", None], ['RKA0:', driver_tape, "Device Drivers...", None], ] music_copyin = ['RKB0:', 'subsys/music.tu56', "Copying in Music score files and source code...", None] ba_copyin = ['RKB0:', 'subsys/ba.tu56', "Installing *.BA BASIC games and demos...", None] cc8_files = [['SYS:', '*.SV'], ['RKB0:', '*.SV/V']] cc8_copyin = ['RKB0:', 'subsys/cc8.tu56', "Installing Ian Schofield's CC8 compiler...", cc8_files] k12_copyin = ['RKA0:', 'subsys/k12.tu56', "Installing Kermit-12...", None] focal69_files = [['RKB0:', 'FOCAL.BN'], ['RKB0:','4WORD.BN'], ['RKB0:', '4KVT.BN'], ['RKB0:', '8KVT.BN'], ['RKB0:', '8KNOVT.BN'] ] focal69_copyin = ['RKA0:', 'subsys/focal69.tu56', "Installing FOCAL 69...", focal69_files] uwfocal_files = [['SYS:', 'UWF16K.SV']] uwfocal_copyin = ['RKA0:', 'subsys/uwfocal-v4e-2.tu56', "Installing U/W FOCAL...", uwfocal_files] advent_copyin = ['RKB0:', 'subsys/advent.tu56', "Installing ADVENT...", None] bin_copyins = [ ['RKA0:', "al-4761c-ba-os8-v3d-ext.1978.tu56", "Copying in OS/8 V3D extensions...", None], ['RKA0:', "al-4549d-ba-fr4-v3d-1.1978.tu56", "Copying in FORTRAN IV tape 1 of 2...", None], ['RKA0:', "al-5596d-ba-fr4-v3d-2.1978.tu56", "Copying in FORTRAN IV tape 2 of 2...", None], ['RKA0:', "al-5642a-ba-macrel-linker.1978.tu56", "Installing MACREL...", None] ] local_files = [] local_stat_str = "Performing copyins from " + local_tape + ":\n" # Case Conversion scripts from local.tu56. local_stat_str += " Case Conversion batch scripts.\n" local_files.append(['RKA0:', '?CSYS.BI']) local_files.append(['RKA0:', '?CBAS.BI']) if not args.disable_chess: local_stat_str += " CHESS.SV binary and CHESS.TX documentation...\n" local_files.append(['RKA0:', 'CHESS.*']) if args.enable_vtedit: local_stat_str += " TECO VTEDIT setup...\n" local_files.append(['RKA0:', 'VTEDIT.*']) local_files.append(['RKA0:', 'TECO.IN']) if not args.disable_dcp: # Should we also install DCP.WU on RKB0:? local_stat_str += " DCP Disassembler: DCP24.SV, and DCP16.SV as DCP.SV.\n" local_files.append(['RKA0:', 'DCP24.SV']) local_files.append(['RKA0:DCP.SV', 'DCP16.SV']) if args.enable_music: bin_copyins.append(music_copyin) if not args.disable_ba: bin_copyins.append(ba_copyin) if not args.disable_cc8: bin_copyins.append(cc8_copyin) if not args.disable_k12: bin_copyins.append(k12_copyin) if not args.disable_advent: bin_copyins.append(advent_copyin) if local_files != []: local_copyins = ['RKA0:', local_tape, local_stat_str, local_files] bin_copyins.append(local_copyins) if args.enable_focal69 and not args.disable_focal: bin_copyins.append(focal69_copyin) if not args.disable_uwfocal and not args.disable_focal: bin_copyins.append(uwfocal_copyin) check_exists(special_bin_copyins) check_exists(bin_copyins) print "Generating " + _bin_rk05 + " from " + str(len(bin_copyins) + 2) + \ " source tapes..." image_path = dirs.os8 + _bin_rk05 if os.path.isfile(image_path): save_path = dirs.media + _bin_rk05 + ".save" print "Pre-existing " + _bin_rk05 + " found. Saving as " + _bin_rk05 + ".save" if os.path.isfile(save_path): print "Overwriting old " + _bin_rk05 + ".save" os.remove(save_path) os.rename(image_path, save_path) global progmsg if progmsg: print "Building initial OS/8 system..." if progmsg: print "Making a writeable copy of boot DECtape..." copyfile(ro_boot_tape_path, boot_dt_path) simh_send("attach rk0 " + image_path) simh_send("attach dt0 " + boot_dt_path) simh_send("attach -r dt1 " + driver_tape_path) if progmsg: print "Performing config with BUILD..." simh_send("boot dt0") os8_pmt_send("\.", "SET SYS NO INIT") # initially, don't run INIT.CM os8_pmt_send("\.", "RUN SYS BUILD") os8_pmt_send("\$", "LOAD DTA1:RK8ESY.BN") os8_pmt_send("\$", "LOAD DTA1:PT8E.BN") os8_pmt_send("\$", "DELETE SYS") os8_pmt_send("\$", "SYSTEM RK8E") os8_pmt_send("\$", "DELETE RXA1") os8_pmt_send("\$", "INSERT PT8E,PTR") os8_pmt_send("\$", "INSERT PT8E,PTP") os8_pmt_send("\$", "DELETE RKA0") os8_pmt_send("\$", "DELETE RKB0") os8_pmt_send("\$", "INSERT RK8E,RKA0,RKB0") os8_pmt_send("\$", "INSERT RK05,RKA2,RKB2") os8_pmt_send("\$", "DELETE DTA0") os8_pmt_send("\$", "INSERT TC,DTA0") os8_pmt_send("\$", "DSK RK8E:RKB0") os8_pmt_send("\$", "PRINT") os8_pmt_send("\$", "BOOT") os8_pmt_send("WRITE ZERO DIRECT\?", "Y") os8_pmt_send("\.", "SAVE SYS BUILD") back_to_simh("\.") if progmsg: print "Copying OS/8 system files from TU56 source to RK05 image..." if progmsg: print "Copying in system tape 1 of 2..." simh_send("boot dt0") os8_pmt_send("\.", "COPY RKA0:<DTA0:*.*") # There are not enough directory entries to put everything # Including all device drivers on the running packs. # So we DONT copy in the device drivers, nor TDINIT.SV, # nor the two TD8E-based DECtape system area files. # CCL.PA and KL8E.PA will be on the source disk. # However DECtape 2 has HELP.HL and .RL files that ARE needed. # if progmsg: print "Copying in system tape 2 of 2..." # os8_pmt_send("\.", "COPY RKA0:<DTA1:*.*") if progmsg: print "Copying in Fortran II libraries and help files from tape 2 of 2" os8_pmt_send("\.", "COPY RKA0:<DTA1:*.RL") os8_pmt_send("\.", "COPY RKA0:<DTA1:*.HL") os8_pmt_send("\.", "ZERO RKB0:") # must precede subsys/* copies back_to_simh("\.") if progmsg: print "Deleting bootable copy of DECtape image and" if progmsg: print "rebooting into freshly-built RK05 OS/8 system..." simh_send("detach dt0") simh_send("detach dt1") simh_send("boot rk0") # must do: boot media just went away back_to_simh("\.") os.remove(boot_dt_path) if progmsg: print "Performing remaining copies/installs..." do_all_copyins(bin_copyins) # Any further initialization of installed software is done here. simh_send("boot rk0") if not args.disable_crt: # NO SCOPE mode is the default on distribution tapes. if progmsg: print "Configuring scope-style rubout processing..." os8_pmt_send("\.", "SET TTY SCOPE") # Make sure Scripts are included in local_files array! # Or this will fail. if not args.disable_lcmod: if progmsg: print "Patching OS/8 to upcase commands only; SIMH is set not to auto-upcase." os8_pmt_send("\.", "SUBMIT SYS:LCSYS.BI") if progmsg: print "Patching OS/8 BASIC to cope with lower case input" os8_pmt_send("\.", "SUBMIT SYS:LCBAS.BI") # Create a banner message and optionally set it to show on boot. # This message is always upcased, even if you do this before calling # LCSYS.BI. Is it a limitation of EDIT? if progmsg: print "Setting INIT message..." os8_pmt_send("\.", "CREATE INIT.TX") os8_pmt_send("#", "A") # append text to file with open(dirs.media + "/init.tx", "r") as f: for line in f: os8_send_line(line) os8_send_ctrl('L') # return to EDIT command mode os8_pmt_send("#", "E") # save and exit os8_pmt_send("\.", "CREATE INIT.CM") os8_pmt_send("#", "A") os8_send_line("TYPE INIT.TX") os8_send_ctrl('L') os8_pmt_send("#", "E") if not args.disable_init: # Set the message to display only if the user did not suppress it. # We do it this way so the user can turn it on later without # rebuilding their OS/8 media. os8_pmt_send("\.", "SET SYS INIT") # Finish up if progmsg: print "Cleaning up..." back_to_simh("\.") simh_send("detach rk0") #### make_src ########################################################## # Source-disk version of make_bin() above. def make_src (args): src_copyins = [ ["RKA1:", "al-4691c-sa-os8-v3d-1.1978.tu56", "...part 1 of 7...", None], ["RKA1:", "al-4692c-sa-os8-v3d-2.1978.tu56", "...part 2 of 7...", None], ["RKA1:", "al-4693d-sa-os8-v3d-3.1978.tu56", "...part 3 of 7...", None], ["RKA1:", "al-4694c-sa-os8-v3d-4.1978.tu56", "...part 4 of 7...", None], ["RKB1:", "al-4695c-sa-os8-v3d-5.1978.tu56", "...part 5 of 7...", None], ["RKB1:", "al-4696c-sa-os8-v3d-6.1978.tu56", "...part 6 of 7...", None], ["RKB1:", "al-4697c-sa-os8-v3d-7.1978.tu56", "...part 7 of 7...", None], ["RKB1:", "al-4759c-sa-os8-ext-1.1978.tu56", "extensions part 1 of 3...", None], ["RKB1:", "al-4760c-sa-os8-ext-2.1978.tu56", "...part 2 of 3...", None], ["RKA1:", "al-5586c-sa-os8-ext-3.1978.tu56", "...part 3 of 3...", None], ] check_exists(src_copyins) print "Generating " + _src_rk05 + " from " + str(len(src_copyins)) + \ " source tapes..." bin_path = dirs.os8 + _bin_rk05 if (not os.path.isfile(bin_path)): print _bin_rk05 + " is needed to build src. Creating..." make_bin(args) src_path = dirs.os8 + _src_rk05 if os.path.isfile(src_path): save_path = src_path + ".save" print "Pre-existing " + _src_rk05 + " found. Saving as " + _src_rk05 + ".save" if os.path.isfile(save_path): print "Overwriting old " + _src_rk05 + ".save" os.remove(save_path) os.rename(src_path, save_path) if progmsg: print "Copying OS/8 source distribution:" simh_send("attach rk0 " + bin_path) simh_send("attach rk1 " + src_path) simh_send("boot rk0") os8_pmt_send("\.", "ZERO RKA1:") os8_pmt_send("\.", "ZERO RKB1:") back_to_simh("\.") do_all_copyins(src_copyins) if progmsg: print "Cleaning up..." simh_send("detach rk0") simh_send("detach rk1") #### main ############################################################## # Program entry point. Parses the command line and drives the above. def main (): # Parse the command line allowed_acts = ["all", "bin", "src"] ap = argparser.ArgParser (allowed_acts) global progmsg progmsg = not ap.args.verbose # Initialize our acts dict. acts = {} first_act = None for this in allowed_acts: acts[this] = False for act in ap.args.what: if act not in allowed_acts: print "Invalid act: " + act + " ignored." continue if first_act == None: first_act = act if act == "all": for this in allowed_acts: acts[this] = True break acts[act] = True # Start the simulator instance we'll use to build the media images global child child = pexpect.spawn(dirs.build + '/bin/pidp8i-sim') # Turn off pexpect's default inter-send() delay. We add our own as # necessary. The conditional is due to an API change between 3 and 4. pev4 = (pkg_resources.get_distribution("pexpect").parsed_version > 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() |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 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 |
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 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 |
cannot compute difference between binary files
cannot compute difference between binary files
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 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 |
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
︙ | ︙ | |||
70 71 72 73 74 75 76 77 78 79 80 81 82 83 | echo exit 1 fi set -x apt-get update && apt-get -y upgrade || true 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)" ] then bn=fossil-release | > > > | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | echo exit 1 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)" ] then bn=fossil-release |
︙ | ︙ |