PiDP-8/I Software

Changes On Branch clean-os8-packs
Log In

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

Changes to AUTHORS.md.
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 `tools/mkos8.in`, 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







|







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
Changes to Makefile.in.
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
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
	@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 \
	@srcdir@/tools/simh-update.in

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))





CLTXT = /boot/cmdline.txt

.PHONY: tags
.PRECIOUS: $(PRECIOUS_OUTFILES)

all: $(OUTFILES) $(PRECIOUS_OUTFILES) $(BUILDDIRS) $(BINS) $(BOOTSCRIPTS) $(LISTINGS) $(ASM_PTS) $(EX_PTS)
	@chmod 755 bin/pidp8i

clean:
	@rm -f $(BINS) $(BOOTSCRIPTS) $(ASM_PTS) $(EX_PTS) $(LISTINGS) $(OBJS) $(OUTFILES) \

		tags \
		obj/*.d \
		obj/*.o \

		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@
ifeq (@HAVE_PROG_CSCOPE@, 1)
	@cscope -bR -s@srcdir@
endif

install: all
	@echo Installing to @prefix@...








>


|
>










>
>
>
>





|




>



>











|







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
238

239
240
241
242
243
244
245
246
247
248
249
250
251
252




























253
254
255
256
257
258
259
		    -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 \
	\) -exec @INSTALL@ -D -m 664 -g @INSTGRP@ {} @ABSPREFIX@/share/{} \;

	@INSTALL@ -m 664 -g @INSTGRP@ boot/*.script @BOOTDIR@

reconfig:
	@AUTOREMAKE@

release: all
	@srcdir@/tools/mkrel

run:
	$(SIM) @srcdir@/boot/0.script

simh-update simh-update-f:
	@@srcdir@/tools/simh-update $(subst simh-update,,$@)






























# 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







>
>
>
>
>
>
>
>
>
>
>
>
>








|
>
|







|





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
Changes to README.md.
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
Changes to auto.def.
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

define defaultprefix /opt/pidp8i

use cc
use cc-lib

options {

	debug-mode        => "create a debug build (default is release)"

	no-lamp-simulator => "use simple LED driver instead of incandescent lamp simulator"













	serial-mod        => "use GPIO drive scheme suitable for Oscar Vermeulen's serial mod method"
	alt-serial-mod    => "use GPIO drive scheme suitable for James L-W's serial mod method"
	throttle:         => "override the throttle values in the boot scripts"
}

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}


























































































}

# 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.







>
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
|



|
|
|



|
|
|



|
|

|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











|
|

|
|
|
|




|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|








|







|
|
|

|
|
|











|

|









|

|
|
|
|
|

|

















|
|














|
|
|
|
|
|
|
|







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
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
        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"]
	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 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"







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|








|
|











>
>






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"
Changes to boot/0.script.in.
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 10 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.

|







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














30
31
;
reset
echo Loading OS/8 from the RK05 cartridge disk...
set cpu 32k
set cpu noidle
set df disabled
@SET_THROTTLE@














att rk0 @MEDIADIR@/os8/os8.rk05
boot rk0







>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

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
Added doc/dcp_wu.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
# 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                 |
Added doc/uwfocal-manual-supp.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
# 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&iuml;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&times;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
Added doc/uwfocal-manual.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&aelig;. 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 &plusmn;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;">&larr; 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)
Added doc/uwfocal-refcards.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
Changes to etc/pidp8i-init.in.
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/mk-os8-rk05s 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







|







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
Changes to examples/README.md.
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.








|
|

|
|
|
|
|
|
|
|
|
|
|
|

|
|

|

|

|
|







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
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.







|







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.
Added lib/mkos8/__init__.py.
































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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' ]
Added lib/mkos8/argparser/__init__.py.














































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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)
Added lib/mkos8/dirs.py.in.






































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
Added libexec/mkos8.






















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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()
Added media/os8/README.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
# OS/8 Media


## Bootable Systems

`os8v3d-bin.rk05` — Bootable OS/8 system made from the binary DECtapes
of OS/8 V3D in the Willem van der Mark Archives described below plus
other tapes in the [`subsys` subdirectory](/file/media/os8/subsys)
during the PiDP-8/I software build process. This is the disk image used
by boot options IF=0 and IF=7. See the [top-level `README.md`
file][tlrm] for instructions on controlling what goes into this image.

`os8.tu56` — Bootable OS/8 DECtape image used by boot option IF=3.
Primarily intended to demonstrate the uncommon "boot and run from tape"
experience offered by early DEC systems.


## Data Disks

`os8v3d-src.rk05` — OS/8 Source RK05 pack made from the source DECtapes
of OS/8 V3D in the Willem van der Mark Archives, described below. This
is not a bootable OS/8 system. It is merely a convenience for use with a
bootable OS/8 system, so you can avoid mounting the seven "Source"
distribution tapes in succession.

If you give the following command to SIMH:

    att rk1 media/os8/os8v3d-src.rk05
   
...this disk will appear as RKA1: and RKB1: under OS/8.


## Willem van der Mark Archives

Willem van der Mark wrote a really cool [PDP-8 emulator in Java][vdms].
He has a well organized archive of DEC media.  The following DECtapes
were copied from his [OS8-V3D software archive][vdms].

The OS/8 Binary DECtapes were compared against those in [Dave Gesswein's
Archive][dga] and found to be nearly identical.  Of the three tapes
reviewed, a total of 2 words differed. Further comparison is possible as
a project for an interested person.

| DECtape Image File Name               | Content Description
| ----------------------------------------------------------------------------
| `al-4711c-ba-os8-v3d-1.1978.tu56`     | DEC OS/8 V3D **Binary** Distribution  1/2
| `al-4712c-ba-os8-v3d-2.1978.tu56`     | DEC OS/8 V3D **Binary** Distribution  2/2
| `al-4761c-ba-os8-v3d-ext.1978.tu56`   | DEC OS/8 V3D Extensions **Binary** Distribution  1/1
| `al-4549d-ba-fr4-v3d-1.1978.tu56`     | DEC OS/8 V3D FORTRAN IV **Binary** Distribution  1/2
| `al-5596d-ba-fr4-v3d-2.1978.tu56`     | DEC OS/8 V3D FORTRAN IV **Binary** Distribution  2/2
| `al-5642a-ba-macrel-linker.1978.tu56` | DEC OS/8 V3D MACREL/LINKER **Binary** Distribution 
| `al-4691c-sa-os8-v3d-1.1978.tu56`     | DEC OS/8 V3D **Source** Distribution  1/7
| `al-4692c-sa-os8-v3d-2.1978.tu56`     | DEC OS/8 V3D **Source** Distribution  2/7
| `al-4693d-sa-os8-v3d-3.1978.tu56`     | DEC OS/8 V3D **Source** Distribution  3/7
| `al-4694c-sa-os8-v3d-4.1978.tu56`     | DEC OS/8 V3D **Source** Distribution  4/7
| `al-4695c-sa-os8-v3d-5.1978.tu56`     | DEC OS/8 V3D **Source** Distribution  5/7
| `al-4696c-sa-os8-v3d-6.1978.tu56`     | DEC OS/8 V3D **Source** Distribution  6/7
| `al-4697c-sa-os8-v3d-7.1978.tu56`     | DEC OS/8 V3D **Source** Distribution  7/7
| `al-4759c-sa-os8-ext-1.1978.tu56`     | DEC OS/8 V3D Extensions **Source** Distribution  1/3
| `al-4760c-sa-os8-ext-2.1978.tu56`     | DEC OS/8 V3D Extensions **Source** Distribution  1/3
| `al-5586c-sa-os8-ext-3.1978.tu56`     | DEC OS/8 V3D Extensions **Source** Distribution  1/3


## Other Files

| File Name       | Content Description
| ----------------------------------------------------------------------------
| `LICENSE.md`    | License provided by DEC that makes our use of OS/8 legal
| `local.tu56`    | Files created for or vetted by the PiDP-8/I project; used at build time

[dga]:  http://www.pdp8online.com/images/images/misc_dectapes.shtml
[tlrm]: /doc/trunk/README.md
[vdms]: http://vandermark.ch/pdp8/index.php?n=OS8.OS8-V3D
[vdma]: http://vandermark.ch/pdp8/index.php
Added media/os8/al-4549d-ba-fr4-v3d-1.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4691c-sa-os8-v3d-1.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4692c-sa-os8-v3d-2.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4693d-sa-os8-v3d-3.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4694c-sa-os8-v3d-4.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4695c-sa-os8-v3d-5.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4696c-sa-os8-v3d-6.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4697c-sa-os8-v3d-7.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4711c-ba-os8-v3d-1.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4712c-ba-os8-v3d-2.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4759c-sa-os8-ext-1.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4760c-sa-os8-ext-2.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-4761c-ba-os8-v3d-ext.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-5586c-sa-os8-ext-3.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-5596d-ba-fr4-v3d-2.1978.tu56.

cannot compute difference between binary files

Added media/os8/al-5642a-ba-macrel-linker.1978.tu56.

cannot compute difference between binary files

Added media/os8/init.tx.in.






















>
>
>
>
>
>
>
>
>
>
>
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
Added media/os8/local.tu56.

cannot compute difference between binary files

Deleted media/os8/os8.rk05.

cannot compute difference between binary files

Added media/os8/subsys/README.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
# Software Subsystems


## Directory Contents

The files in this directory are used during the PiDP-8/I software build
process along with the DEC original tape images in [our parent
directory](/files/media/os8) to build the `os8v3d-bin.rk05` disk image
used by boot options IF=0 and IF=7.

These files are here rather than one level up because they are not part
of OS/8 *per se*, but one way or another, they do currently require
OS/8. Some of the data on these tapes could potentially be used with
other PDP-8 operating systems, but at minimum, it would require
translating the data from OS/8 tape format.


| DECtape Image File Name | Content Description
| ----------------------------------------------------------------------------
| `advent.tu56`           | [OS/8 Adventure][os8a] v2.4
| `ba.tu56`               | Several BASIC programs in OS/8 \*.BA file format from DEC's book ["101 BASIC Computer Games"][bcg]
| `cc8.tu56`              | Ian Schofield's [CC8 OS/8 C compiler system][cc8]
| `k12.tu56`              | [Kermit-12][k12] for OS/8, OS/78, OS/278, and OS/12
| `music.tu56`            | [RFI-based][rfi] music playback programs


## Controlling the Build Process

Most of these files are merged into the OS/8 binary disk image by
default, but can be excluded by giving a `--disable-os8-NAME` flag to
the `configure` script, where `NAME` is the file name above without the
`.tu56` extension. (e.g. `--disable-os8-k12` excludes Kermit-12.)

Only one of the files above is currently excluded by default, that being
`music.tu56`, because we have not yet received any report of reliable
playback. We believe this is because the PiDP-8/I realization does not
lend itself to creation of suitable AM frequency RFI. These programs
were written for real PDP-8 hardware which had much longer wires backed
by much stronger drivers than a PiDP-8/I, and which ran at lower
frequencies than a Raspberry Pi. These problems are not insurmountable,
so someone interested in the project may force inclusion of these files
on the OS/8 RK05 boot disk with

     $ ./configure --enable-os8-music

Solving this problem may require hardware modifications. If so, we'll
still exclude these programs by default since not all PiDP-8/I machines
will have these modifications.

See the [top-level `README.md` file][tlrm] for further information about
the `--enable-os8-*` and `--disable-os8-*` configuration options.


[bcg]:  https://archive.org/details/bitsavers_decBooks10Mar75_26006648
[cc8]:  https://groups.google.com/d/msg/pidp-8/ycs_KOI4vdg/Zr0bifJxAgAJ
[k12]:  http://www.columbia.edu/kermit/pdp8.html
[os8a]: http://www.rickmurphy.net/advent/
[rfi]:  https://en.wikipedia.org/wiki/Electromagnetic_interference
[tlrm]: /doc/trunk/README.md
Added media/os8/subsys/advent.tu56.

cannot compute difference between binary files

Added media/os8/subsys/ba.tu56.

cannot compute difference between binary files

Added media/os8/subsys/cc8.tu56.

cannot compute difference between binary files

Added media/os8/subsys/focal69.tu56.

cannot compute difference between binary files

Added media/os8/subsys/k12.tu56.

cannot compute difference between binary files

Added media/os8/subsys/music.tu56.

cannot compute difference between binary files

Added media/os8/subsys/uwfocal-v4e-1.tu56.

cannot compute difference between binary files

Added media/os8/subsys/uwfocal-v4e-2.tu56.

cannot compute difference between binary files

Changes to tools/bosi.
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