PiDP-8/I Software

Check-in Differences
Log In

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From 2a4f4cec29a53c13 To 20a61bf4e6ef46c8

2017-12-16
06:38
Better description of the CC8 example programs in the CC8 docs. check-in: 442d9cff50 user: tangent tags: trunk
06:26
cc8-tu56-update no longer copies basic.c to the tape image: the OS/8 compiler isn't currently able to compile it. check-in: 20a61bf4e6 user: tangent tags: trunk
06:25
The CC8 cross-compiler now writes out *.sb instead of *.s to match the OS/8 file naming convention for SABR output, and to avoid the confusing use of *.s for some POSIX assembly languages. It was using *.s simply because it could replace *.c on the input file name with a single character replacement. If the input file name is exactly at the file name buffer's limit (20 bytes, including the trailing null), we still write out *.s rather that mess with resizing buffers or reserving one byte, complicating all the other uses of that buffer size. check-in: 857b3fafc0 user: tangent tags: trunk
2017-09-15
07:09
"make clean" now removes bin/*rk05* check-in: b0d9bf39ba user: tangent tags: clean-os8-packs
07:07
Moved media/subsys underneath media/os8, since everything in here currently requires OS/8 in one way or another. Doing so simplifies the path name building code in tools/mkos8 considerably. It also swaps out an explanation in media/subsys/README.md for why it is outside the os8 tree for an explanation of why it's in a subdirectory of that tree, a distinct improvement. check-in: 2a4f4cec29 user: tangent tags: clean-os8-packs
2017-09-14
13:41
Comment typo fix check-in: a5ecc26447 user: tangent tags: clean-os8-packs

Added .agignore.


1
2
+
+
inst
test
Changes to .fossil-settings/crlf-glob.

1
2
3









4
5
1



2
3
4
5
6
7
8
9
10
11
12
+
-
-
-
+
+
+
+
+
+
+
+
+


examples/*.fc
src/scp.*
src/sim_*.[ch]
src/sim_*.in

src/*.[ch]
src/*.in

src/cc8/*/*.[ch]
src/cc8/include/*
src/cc8/os8/*.sb
src/cc8/os8/bldcc8.bi

src/PDP8/pdp8_*.[ch]
src/PDP8/pidp8i.c.in
Changes to .fossil-settings/ignore-glob.
1

1
2

+
doc/simh/*.pdf
test/*
Added .ignore.

1
+
.agignore
Changes to AUTHORS.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







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

-
+



















-
-
+
+

-
+


-
-
+
+


-
-
-
+
+
+
-
-
+

-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-
+
+
-

-
+
-
-
-
+
+
+
+
+
+

-
+
-
-
-
+
+

-
-
-
+
+
+
+
+

-
-
+
+
+
+
+
+
# Creators and Major Contributors to the PiDP-8/I Project
# Creators of and Major Contributors to the PiDP-8/I Project

*   **Oscar Vermeulen <oscar.vermeulen@hotmail.com>**:
*   **[Oscar Vermeulen](mailto:oscar.vermeulen@hotmail.com)**:

    -   Creator of the project (both hardware and software)
    
    -   Author of the initial modifications to the SIMH PDP-8 simulator
        necessary to make it use the PiDP-8/I front panel hardware

    -   Curator of the default set of binary demo media

    -   Author of the simulator setup scripts

    -   Initiator of much else in the project

    -   Author of the bulk of the documentation

    -   Host and major contributor to the PiDP-8/I support forum on
        Google Groups

    -   Hardware kit assembler and distributor

*   **Robert M Supnik** Primary author of the SIMH PDP-8 simulator upon
    which this project is based.
*   **Robert M Supnik** is the primary author of the SIMH PDP-8
    simulator upon which this project is based.

*   **Mike Barnes** Ported Oscar Vermeulen's SIMH 3.9 based PiDP-8/I
*   **Mike Barnes** ported Oscar Vermeulen's SIMH 3.9 based PiDP-8/I
    simulator to the new SIMH 4.0 code base.  (September 2015.)

*   **Dylan McNamee** Ported the software to Buildroot for the official
    2015.12.15 binary OS images, and helped to merge the James L-W
*   **Dylan McNamee** ported the software to Buildroot for the official
    2015.12.15 binary OS images and helped to merge the James L-W
    "alt-serial" mode in.

*   **Mark G. Thomas** Creator of the installation scripts for the
    2015.12.15 release, which were folded into the `make install`
    handler within `Makefile.in`. Also wrote the version of the SysV
*   **Mark G. Thomas** wrote the installation scripts for the 2015.12.15
    release, which were folded into the `make install` handler within
    the current `Makefile.in`. He also wrote the version of the SysV
    init script that came with that release as `rc.pidp8`, shipped here
    as `pidp8i-init`.
    init script shipped here as `etc/pidp8i-init.in`.

*   **Ian Schofield <isysxp@gmail.com>** Modified the LED lamp driving
    code in the simulator to better simulate the incandescent lamps in
    the original PDP-8/I hardware.  (The bulk of his original code has
    since been rewritten, but the core idea remains, and it is doubtful
    whether the current method would exist without his instigation.)
*   **[Ian Schofield](mailto:isysxp@gmail.com)** modified the LED lamp
    driving code in the simulator to better simulate the incandescent lamps
    in the original PDP-8/I hardware.  (The bulk of his original code
    has since been rewritten, but the core idea remains, and it is
    doubtful whether the current method would exist without his
    instigation.)

*   **Henk Gooijen <henk.gooijen@boschrexroth.nl>** Pushed the PDP-8
    simulator's internal EAE step counter value down into the PiDP-8/I's
    LED manipulation code, without which the step counter LEDs remain
    dark even when using the EAE.
*   **[Henk Gooijen](mailto:henk.gooijen@boschrexroth.nl)** pushed the
    PDP-8 simulator's internal EAE step counter value down into the
    PiDP-8/I's LED manipulation code, without which the step counter
    LEDs remain dark even when using the EAE.

*   **Paul R. Bernard <prb@downspout.ca>** wrote `src/test.c` and the
    core of what now appears as `doc/pidp8i-test.md`. (The program builds
    and installs as `pidp8i-test`.)  He also provided a one-line fix
    that completes the work of Henk Gooijen's step counter patch.
*   **[Paul R. Bernard](mailto:prb@downspout.ca)** wrote `src/test.c`
    and the core of what now appears as `doc/pidp8i-test.md`. (The program
    builds and installs as `pidp8i-test`.)  He also provided a one-line
    fix that completes the work of Henk Gooijen's step counter patch.

*   **[Rick Murphy](mailto:k1mu.nospam@gmail.com)** is the current
    maintainer of [OS/8 Adventure][advent] which we've included in our
    OS/8 disk image. He's also provided several other files which have
    landed in the distribution such as the [VTEDIT][vtedit] feature. He
    also optimized the `pep001.pal` example so that it fits into a
    single page of PDP-8 core.

*   **[Tony Hill](mailto:hill.anthony@gmail.com)** merged all the
    upstream SIMH changes produced between late September 2015 and late
    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].

*   **Rick Murphy <k1mu.nospam@gmail.com>** optimized the `pep001.pal`
*   **[Jonathan Trites](mailto:tritesnikov@gmail.com)** wrote the
    example so that it fits into a single page of PDP-8 core, and
    provided several useful files in his OS/8 disk images that have
    initial version of the script now called `libexec/mkos8`, which
    builds the OS/8 disk images from source tapes.
    managed to land in this software distribution's OS/8 disk image.

*   **Tony Hill <hill.anthony@gmail.com>** Merged all the upstream SIMH
*   **[Bill Cattey](mailto:bill.cattey@gmail.com)** is the project lead
    changes produced between late September 2015 and late December 2016
    into the PiDP-8/I simulator.  (Since then, this merge process is
    automated as `tools/simh-update`.)
    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
    areas of the software project. His contributions are made to the
    project [as `poetnerd`][pncomm].

*   **Bill Cattey <bill.cattey@gmail.com>** (a.k.a. poetnerd) did the
*   **[Warren Young](mailto:tangentsoft@gmail.com)** Did everything
    bulk of the work on automatic generation of OS/8 system pack images
    from original source media, replacing the hand-assembled and -hacked
    `os8.rk05` image.
    listed in [the change log][cl] that is not attributed to anyone
    else.

*   **Jonathan Trites <tritesnikov@gmail.com>** wrote the original
    version of what has become `tools/mkos8s.in`, the script that
    generates `bin/os8v3d-*.rk05` from source tapes.
    His contributions are made to the project [as `tangent`][wycomm],
    though keep in mind that some of those are commits of external
    contributions made by people who do not have commit rights on our
    software repository. The changelog provides proper attribution for
    these where the checkin comments do not.

*   **Warren Young <tangentsoft@gmail.com>** Did everything listed in
    `ChangeLog.md` that is not attributed to anyone else.
[advent]: http://www.rickmurphy.net/advent/
[cl]:     https://tangentsoft.com/pidp8i/doc/trunk/ChangeLog.md
[pncomm]: https://tangentsoft.com/pidp8i/timeline?u=poetnerd
[thcomm]: https://tangentsoft.com/pidp8i/timeline?u=tony
[vtedit]: https://tangentsoft.com/pidp8i/wiki?name=Using+VTEDIT
[wycomm]: https://tangentsoft.com/pidp8i/timeline?u=tangent
Changes to COPYING.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
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







-
+














-
+











-
+







-
+


-
+

-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+








-
+










+


-
+







# Licenses

The PiDP-8/I software distribution is an agglomeration of software from
multiple sources.  Several different licenses apply to its parts.  This
file guides you to those individual licenses.


## SIMH License
## <a id="simh"></a>SIMH License

Most of the files in this software distribution are released under the
terms of the SIMH license, a copy of which typically appears at the top
of each file it applies to. This includes not only SIMH proper but also
several files written by PiDP-8/I software project contributors who
chose to license their contributions under the same license.

For a few files, textual inclusion of the license inside the file itself
was impractical, so this license is applied by reference to [a file
included with the distribution][sl].

[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md


## PiDP-8/I Design Files
## <a id="hw"></a>PiDP-8/I Design Files

The PiDP-8/I design files in [`hardware/pidp8i`][hwp] were released
under the Creative Commons [Attribution-NonCommercial-ShareAlike 4.0
International][ccl] license [on the mailing list][pdp8il] by their
author, Oscar Vermeulen.

[ccl]: https://creativecommons.org/licenses/by-nc-sa/4.0/
[hwp]: https://tangentsoft.com/pidp8i/dir?name=hardware/pdp8i&ci=trunk
[pdp8il]: https://groups.google.com/d/msg/pidp-8/bcIH9uEB_kU/zg9uho7NDAAJ


## autosetup License
## <a id="autosetup"></a>autosetup License

The `configure` script and the contents of the `autosetup` directory are
released under the FreeBSD license given in [`autosetup/LICENSE`][as].

[as]: https://tangentsoft.com/pidp8i/doc/trunk/autosetup/LICENSE


## palbart License
## <a id="palbart"></a>palbart License

The `palbart` program and its manual page are released under the terms
of the license given in [`palbart/LICENSE.md`][pl].
of the license given in [`src/palbart/LICENSE.md`][pl].

[pl]: https://tangentsoft.com/pidp8i/doc/trunk/palbart/LICENSE.md
[pl]: https://tangentsoft.com/pidp8i/doc/trunk/src/palbart/LICENSE.md


## <a id="d8tape"></a>d8tape License

The `d8tape` program is distributed under the license given in
[`src/d8tape/LICENSE.md`][d8tl].

[d8tl]: https://tangentsoft.com/pidp8i/doc/trunk/src/d8tape/LICENSE.md


## <a id="cc8"></a>CC8 Compiler License

The license for the `src/cc8` subtree is messy as it comes to us from
multiple authors over many years.

There are two compilers here.

First we have the OS/8 "native" compiler in `src/cc8/os8`, which is
entirely Ian Schofield's work, released under the terms of the [GNU
General Public License version 3][gpl3].

Then we have the CC8 cross-compiler which is based on Ron Cain's
[Small-C][smc], originally published in [Dr.  Dobbs' Journal][ddj].
Wikipedia describes Small-C as "copyrighted but sharable," which I take
to mean that we cannot claim it as our exclusive property, but we can
modify it and distribute those modifications to others, which is what
we're doing here.

Ian Schofield then took the Small-C source base and added a SABR
back-end, `code8.c`, which is also distributed under the [GPLv3][gpl3].

There is [another PDP-8 C compiler project][smsc] based on Small-C by
Vincent Slyngstad, which uses an entirely different approach for code
generation.  Ian Schofield took some of the library routines from this
implementation.

[ddj]:  https://en.wikipedia.org/wiki/Dr._Dobb%27s_Journal
[gpl3]: https://tangentsoft.com/pidp8i/doc/trunk/src/cc8/LICENSE.txt
[smc]:  https://en.wikipedia.org/wiki/Small-C
[smsc]: http://so-much-stuff.com/pdp8/C/C.php


## OS/8 License
## <a id="os8"></a>OS/8 License

The OS/8 media images included with this software distribution are
released under the Digital License Agreement presented in
[`media/os8/LICENSE.md`][dla].

[dla]: https://tangentsoft.com/pidp8i/doc/trunk/media/os8/LICENSE.md


## Other DEC Software
## <a id="dec"></a>Other DEC Software

The other files in the [`media`][md] and [`examples`][ed] directories
that originate from Digital Equipment Corporation are believed to fall
under the [public domain license][pdp8pd] DEC released all their PDP-8
software under after it stopped being ecomonmically viable. Documented
releases for specific software (e.g. TSS/8) may be difficult to come by,
however.

[md]: https://tangentsoft.com/pidp8i/dir?ci=trunk&name=media
[ed]: https://tangentsoft.com/pidp8i/dir?ci=trunk&name=examples
[pdp8pd]: http://mailman.trailing-edge.com/pipermail/simh/2017-January/016164.html


## ETOS License
## <a id="etos"></a>ETOS License

ETOS was a commercial product produced outside of DEC. No public
documented declaration of license is known to be available for it, but
we have [a third-hand report][el] that its creators are fine with ETOS
being redistributed.

[el]: http://mailman.trailing-edge.com/pipermail/simh/2017-January/016169.html
Changes to ChangeLog.md.
1












































































































































































































































































































































































2
3
4
5
6
7
8
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

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







# PiDP-8/I Changes

## Version 2017.12.eh? — The "Languages and Custom OS/8 Disk Packs" release

*   All prior versions were shipping `os8.rk05`, a "Field Service
    Diagnostic" OS/8 disk pack image with uncertain provenance,
    configuration, and modification history.  We have replaced that with
    a script run at build time that programmatically assembles a set of
    clean OS/8 RK05 disk images from curated, pristine, tested sources
    based on the user's chosen configuration options.

    This provides the following features and benefits as compared to the
    old `os8.rk05`:

    -   The PiDP-8/I software build process now builds up to three RK05
        disk images:

        -   `os8v3d-bin.rk05` is a bootable OS/8 V3D disk configured
            according to your chosen configuration options, which are
            described below and in [`README.md`][tlrm].  It is made from
            the pristine DECtape images shipped by DEC for OS/8 V3D plus
            several third-party tapes curated for and built by the
            project's maintainers.  See [the OS/8 media `README.md`
            file][os8rm] for more details.

        -   `os8v3d-patched.rk05` is a copy of the `bin` disk with
            [most][os8p] of the patches DEC published over the years for
            OS/8 V3D applied.  That set of patches was chosen and tested
            for applicability to our modern PiDP-8/I world and for
            mutual compatibility.

            This is the boot disk used for the IF=0 and IF=7 cases
            unless you give `--disable-os8-patches` to the `configure`
            script, in which case these boot options use the `bin` disk.

        -   `os8v3d-src.rk05` is a non-bootable disk containing the
            contents of the OS/8 V3D source code tapes plus the source
            code for the extensions to OS/8 V3D.  The *ten* TU56 tape
            images used as input to this process are also included among
            the PiDP-8/I software — see `media/os8/al-*-sa-*.tu56` — but
            we find it much more convenient to explore the sources on a
            single RK05 disk than to repeatedly attach and detach the
            TU56 tapes.

            You can suppress building this with `--disable-os8-src`.

        Default versions of these disk images are also now published on
        the project's home page for the benefit of those not running our
        PiDP-8/I software.  There are quite a few OS/8 RK05 disk images
        floating around on the Internet these days, and many of them
        have bugs and breakage in them that we've fixed.  It would
        completely fail to break our hearts if these images were used by
        many people outside the PiDP-8/I project.

    -   U/W FOCAL V4E is installed on SYS: by default. Start with our
        [U/W FOCAL Manual Supplement for the PiDP-8/I][uwfs], then
        follow links from there to further information.
        
        The primary linked source is the [U/W FOCAL Manual][uwfm] by Jim
        van Zee (creator of U/W FOCAL) converted from scanned and OCR'd
        PDF form to Markdown format, which Fossil renders nicely for us
        on the web.

        This is a fascinating programming language, well worth studying!

    -   Ian Schofield's CC8 OS/8 C compiler is installed on `SYS:` by
        default, and its examples and other files are on `DSK:`.  We
        have also merged in his `cc8` host-side cross-compiler.  See
        [the CC8 `README`][cc8rm] for details.
        
        This is a considerably improved compiler relative to what
        was distributed on the mailing list in August 2017.  Ian has
        been working within the PiDP-8/I project since that initial
        public release, which we are now distributing publicly for
        the first time.  We thank him for trusting us to host and
        distribute his project.

    -   The MACREL v2 macro assembler and its associated FUTIL V8B tool
        are installed by default.  Not only is this new functionality
        relative to prior releases of the PiDP-8/I software, it is a
        considerable upgrade over to the original MACREL and FUTIL V7
        that are more commonly found on the net.

    -   DCP disassembler is installed by default.

    -   John Comeau's CHECKMO-II chess program is installed by default.

    -   By default, SIMH no longer folds lowercase input and output to
        uppercase.  Instead, we apply patches to OS/8's command
        processor and its BASIC implementation to up-case input, since
        neither OS/8 nor BASIC can cope with lowercase input.

        All other programs are left to fend for themselves, which
        often works out fine.  U/W FOCAL, Adventure, and TECO all handle
        lowercase input to some extent, for example, and all three can
        emit lowercase text if given it.  With the prior SIMH setting,
        you could not use lowercase in these programs at all.

        This default can be overridden.  See the documentation for the
        new `--lowercase` configuration option in `README.md`.

    -   The `INIT.TX` message displayed by default on OS/8 boot is now
        more informative than the old `FIELD SERVICE PDP-8 DIAGNOSTIC
        SYSTEM` message.  It also now uses lowercase unless you built
        the simulator to force uppercase with `--lowercase=upper`.

        Those that do not want any boot message can disable it at
        configuration time with the `--disable-os8-init` flag.

        The message can be modified by editing `media/os8/init.tx.in`
        and saying `make`, which will rebuild the OS/8 media.

    -   All of the above features can be disabled if not wanted, as can
        several features present on the old `os8.rk05` disk: Adventure,
        FORTRAN IV, FORTRAN II, Kermit-12, and the BASIC game and demo
        programs.

        You can disable each feature above with a `--disable-os8-*`
        option to the `configure` script, or you can disable all of them
        collectively with the `--os8-minimal` option, which gets you a
        nearly bare-bones OS/8 installation with lots of spare disk
        space with which you can do what *you* want.

    -   Replaced the mismatched FORTRAN compiler and runtime with
        matched versions from the distribution DECtapes, and ensured that
        Adventure runs under this version of the FORTRAN Run Time System
        (FRTS). At various points in the past, either FORTRAN or
        Adventure has been broken.

    -   Repaired several broken BASIC programs on `RKB0:` by going back
        to primary sources.  Either the `os8.rk05` disk image was corrupted
        at some point or it is an image of a real RK05 disk pack that
        was corrupted, causing several of these BASIC programs to not
        run properly.

    -   The `*.MU` and music player files are left off of `RKB0:` by
        default, since they apparently do not cause sufficient RFI on
        the PiDP-8/I hardware to be picked up by normal AM radios.  This
        saves space for things that *do* work.

    -   No longer installing the `VTEDIT` macros for TECO by default.
        Although some may enjoy this alternative way of running TECO, it
        was decided that we should offer stock TECO by default for its
        historical value.  If you want VTEDIT back, it can be re-enabled
        with a `configure` script option.

    -   In the old `os8.rk05` disk image, both `SYS:` and `DSK:` were
        set to `RKA0:`, which meant that to address anything on `RKB0:`, you
        had to specify the device path explicitly or manually change the
        default in OS/8 with an `ASSIGN RKB0 DSK` command or similar.

        In the new disk pack, programs run with the OS/8 `R` command are
        installed on `RKA0:` which is the `SYS:` disk, and user data
        files, FOCAL and BASIC programs, etc. are on `RKB0:` which is
        assigned as `DSK:`. This means OS/8 and its programs are now far
        more likely to find files without an explicit device name,
        because files are installed where OS/8 looks for them by
        default.  Example:

            .R FRTS                   ⇠ loads FRTS from SYS: (RKA0:)
            *ADVENT                   ⇠ loads ADVENT.LD from DSK: (RKB0:)
            *[Esc]
            Welcome to Adventure!!

        Notice that no device names had to be specified.  OS/8 did the
        right thing by default here, even though the files involved are
        on two separate OS/8 devices.

        To a very rough approximation, `SYS:` on these new RK05 disk
        packs acts like the Unix `PATH` and `DSK:` acts like your user's
        home directory.

        The idea for this came from Ian Schofield's `cc8.rk05` disk
        image, which we are also shipping in this release.

    -   OS/8 has a limit on the number of devices it can support, and we
        made different choices than the creator of `os8.rk05`.

        Briefly, we replaced the second floppy (`RXA1:`) with a third
        RK05 disk, that being deemed a more useful configuration for
        this hard disk based OS/8 configuration.  A dual-floppy
        configuration implies that you are booting from floppy and need
        the second one for user files and such.  In our RK05 based
        configuration, users should need floppy disk support rarely, and
        then primarily to get data on and off of the attached hard
        disk(s).

        We chose to stick with the dual TU56 tape drive setup of the
        prior version as we found the ability to mount two tapes very
        helpful, particularly during the `mkos8` build process.

        The difference in the `RESORC` output between the versions is:

            Old: RKA0,RKB0,RKA1,RKB1,          RXA0,RXA1,DTA0,DTA1,TTY,LPT,PTP,PTR
            New: RKA0,RKB0,RKA1,RKB1,RKA2,RKB2,RXA0,     DTA0,DTA1,TTY,LPT,PTP,PTR

    This automatic OS/8 media build feature was suggested by Jonathan
    Trites who wrote the initial version of the script that is now
    called `libexec/mkos8`.  That script was then extended and factored
    into its current form by Bill Cattey and Warren Young.

    Warren thinks Bill did most of the hard work in the items above.

    The source media used by the `mkos8` script comes from many sources
    and was curated for the PiDP-8/I project by Bill Cattey.  See the
    [OS/8 media README][os8rm] for more details.

    See the [the top-level `README`][tlrm] for information on modifying
    the default OS/8 configuration.  Pretty much everything above can be
    disabled if it's enabled by default, and vice versa.

*   Added several new wiki articles covering the above:

    *   More Project Euler Problem #1 solutions in:

        *   [C][pe1c]
        *   [FORTRAN IV][pe1f4]
        *   [FORTRAN II][pe1f2]
        *   [U/W FOCAL][pe1u]

    *   [Demos in BASIC][dibas], deescribing `DSK:*.BA`

    *   [OS/8 Console TTY Setup][os8ct], describing how we have
        modified the stock behavior of OS/8 to behave appropriately
        with a glass terminal or SSH on its console, as opposed to
        its default behavior, which assumes a teletype.

    *   [OS/8 LCSYS.BI Disassembled][os8lc], a symbolic
        disassembly of the `LCSYS.BI` patch we distribute with the
        system, which is widely available online elsewhere. That script
        is a raw binary patch, which makes its operation a mystery
        unless you happen to be able to read PDP-8 machine code.

*   Added Bill Cattey's `txt2ptp` program which converts plain ASCII
    text files files to the paper tape format used by SIMH, which eases
    transfer of text data into the simulator.  That program is also
    linked to `ptp2txt`, which makes it perform the inverse function:
    given a SIMH paper tape image file, produce an ASCII text file on
    the host machine with its contents.

    This program was written to ease the movement of FOCAL program text
    between SIMH and its host OS, but they should prove useful for other
    similar tasks.

*   Integrated Robert Krten's `d8tape` PDP-8 host-side disassembler.
    This is distinct from the OS/8 DCP disassembler, which runs inside
    the simulator.  It is intended as a companion to `palbart`, which we
    integrated last year.

*   Added a new "blinkenlights" demo program called `bin/teco-pi-demo`
    which drives SIMH from the outside, running a TECO macro under OS/8
    to calculate *pi* to 248 digits at a very slow rate, producing a
    display that manages to land somewhere between the random default
    display of [Deeper Thought][dt2vk] and the clear, boring patterns of
    our preexisting IF=5 demo script.

    Why 248 digits?  Because at 249, TECO8 runs out of memory, aborting
    the demo early.  At the default execution rate, it would take over
    17 years to complete this demo, making it a good choice to run on
    PiDP-8/I units primarily being used as *objets d'art*.  The demo has
    a finite run time, but your Raspberry Pi is likely to die before it
    completes. `;)`

    This script is also included as a demonstration of how the end user
    can reuse the technology that we developed to automatically build
    the custom OS/8 disk images described above to achieve different
    ends.  Perhaps you have some other program you'd like to run within
    SIMH in an automated fashion?  This shows one way how, and
    demonstrates a pre-built and tested set of tools for achieving it.

    We have also written [a tutorial][csd] explaining how
    `bin/teco-pi-demo` works and how to reuse the components it is built
    atop for your own ends.

    This demo also has a benchmark mode (command line option `-b`) which
    has two purposes:

    1.  It lets you see how much faster your host system runs PDP-8 code
        than a Raspberry Pi Model B+ running the PiDP-8/I simulator.

    2.  Given that information, the benchmark overrides a hardcoded
        timing value in the source code as distributed which prevents
        programs like `teco-pi-demo` from spamming the OS/8 terminal
        input handler.  The default is for the slowest host we support
        this software on, that same Model B+ referred to above, but if
        we know you're running on a faster host, we can shorten this
        delay and remain reliable.
        
    If you run the demo in benchmark mode twice, you'll notice that the
    TECO script is input nearly instantaneously the second time, whereas
    you can see the demo "type" the script in very quickly the first
    time.  (Remove `lib/pidp8i/ips.py`, say `make reconfig` and run the
    demo again to see the difference.)

*   The `DF` + `SING_STEP` feature for automatically attaching binary
    media images to the simulator from files on USB sticks now looks
    at all directories under `/media`, not just `usb0` through `usb7`
    so that it works with several other common Linux USB automounting
    schemes, such as the one Raspbian Desktop uses.

*   Fixed the order of initialization in the GPIO setup code for the
    James L-W serial mod case.  Fix by Dylan McNamee.

*   The helper program that selects which boot script to run when the
    PiDP-8/I boots based on the IF switch settings broke at some point
    in the past, apparently because it was using its own idiosyncratic
    GPIO handling code, and thus did not track our evolving GPIO
    handling methods.  Now it shares the same code used by `pidp8i-sim`
    and `pidp8i-test`, so it works properly again.

*   The SysV init script that starts `pidp8i-sim` under GNU Screen on
    the PiDP-8/I now sets the working directory to `$prefix/share/media`
    on start, so relative paths given to SIMH commands (e.g. `ATTACH`)
    are more likely to do what you want.  In prior releases, you
    generally had to give absolute paths to attach media and such
    because CWD would be set somewhere unhelpful.

*   The Fetch LED is no longer lit when in STOP or single-step mode.  In
    real hardware, it can be either on or off in this mode, depending
    on various conditions, but it is most often off, so while it is not
    perfectly correct now, it is less wrong.  Most of the investigation
    into this issue is by Bill Cattey, with the current partial fix by
    me.  A more precise fix may come later.  (See ticket [347ae45403] if
    you care to know the details.)

*   The Pause LED state was over-counted in the LED sub-sampling scheme
    so that it would tend to be brighter than it should have been.
    Problem noticed by Ian Schofield.

*   The MB row's state was not showing the right thing.  The problem was
    noticed in comparison to real PDP-8/I hardware by Vincent Slyngstad
    and verified by William Cattey.  Ian Schofield suggested the current
    fix.

*   Updated SIMH to upstream checkin ID 27f9fc3c3, December 11, 2017.
    There have been no substantial changes to the PDP-8 simulator since
    the last update, 8 months ago, but there have been a lot of bug
    fixes to the SCP core program.

*   Updated for Raspbian Stretch, released in September 2017.  It should
    still run on Raspbian Jessie, however.

*   Assorted portability, build system, and documentation improvements.

*   **TODO**

    -   Create udev script replacement for usbmount so that `DF` +
        `SING_STEP` works on Stretch.

[apt]:   https://linux.die.net/man/8/apt
[cc8rm]: https://tangentsoft.com/pidp8i/doc/trunk/src/cc8/README.md
[csd]:   https://tangentsoft.com/pidp8i/doc/trunk/doc/class-simh.md
[dibas]: https://tangentsoft.com/pidp8i/wiki?name=Demos+in+BASIC
[dt2vk]: https://github.com/VentureKing/Deeper-Thought-2
[os8ct]: https://tangentsoft.com/pidp8i/wiki?name=OS/8+Console+TTY+Setup
[os8lc]: https://tangentsoft.com/pidp8i/wiki?name=OS/8+LCSYS.BI+Disassembled
[os8p]:  https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-patching.md
[os8rm]: https://tangentsoft.com/pidp8i/doc/trunk/media/os8/README.md
[pe1c]:  https://tangentsoft.com/pidp8i/wiki?name=PEP001.C
[pe1f2]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.FT#fortran-ii
[pe1f4]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.FT#fortran-iv
[pe1u]:  https://tangentsoft.com/pidp8i/wiki?name=PEP001.FC
[uwfm]:  https://tangentsoft.com/pidp8i/doc/trunk/doc/uwfocal-manual.md
[uwfs]:  https://tangentsoft.com/pidp8i/doc/trunk/doc/uwfocal-manual-supp.md


## Version 2017.04.04

*   Removed the PDP-8 CPU idle detection feature.  Oscar Vermeulen
    reports that it also interfered with NLS LED driving mode in his
    last version, and we have no reason to believe our NLS code is
    sufficiently different to avoid the problem.
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
383
384
385
386
387
388
389

390
391
392
393
394
395
396
397







-
+







    percentage style throttle rates.  We now explicitly set the throttle
    rate to "50%" which not only achieves an even higher throttle rate
    than in earlier releases, it's reliable in the face of varying
    background CPU usage.  See the single-core section of
    `README-throttle.md` for details.


## Version 2017.04.01 The "I May Be a Fool, but I am *Your* Fool" Release
## Version 2017.04.01 The "I May Be a Fool, but I am *Your* Fool" release

*   Added the `configure --alt-serial-mod` option to change the GPIO
    code to work with [James L-W's alternative serial mod][sm2].

*   Increased the stock CPU throttle from 0.67 MIPS to 0.85 MIPS on most
    Pi 1 class devices, except for the Pi Zero which is a bit faster and
    so is able to run at 1.25 MIPS.
529
530
531
532
533
534
535
536

537
538
539
540
541
542
543
893
894
895
896
897
898
899

900
901
902
903
904
905
906
907







-
+







*   The simulator now reports the upstream Git commit ID it is based on
    in its version string, so that if you report bugs upstream to the
    SIMH project, you can give them a version number that will be
    meaningful to them.  (They don't care about our vYYYYMMDD release
    numbers or our Fossil checkin IDs.)


## Version 2016.12.26 (The Boxing Day release)
## Version 2016.12.26 The Boxing Day release

*   Tony Hill updated SIMH to the latest upstream version.

    This change represents about 15 months worth of work in the
    [upstream project][simh] — plus a fair bit of work by Tony to merge
    it all — so I will only summarize the improvements affecting the
    PDP-8 simulator here:
735
736
737
738
739
740
741
742

743
744
745
746
747
748
749
1099
1100
1101
1102
1103
1104
1105

1106
1107
1108
1109
1110
1111
1112
1113







-
+







[p1saga]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.PA


## Version 2016.11.28

*   Added an intelligent, powerful build system, replacing the
    bare-bones `Makefile` based build system in the upstream version.
    See [`README.md`][readme] for more info on this.
    See [`README.md`][tlrm] for more info on this.

*   The installation is now completely relocatable via `./configure
    --prefix=/some/other/place`. The upstream version would not work if
    installed somewhere other than `/opt/pidp8` due to many hard-coded
    absolute paths.  (This is enabled by the new build system, but
    fixing it was not simply a matter of swapping out the build system.)

806
807
808
809
810
811
812
813

814
815
816
817
818
819
820
1170
1171
1172
1173
1174
1175
1176

1177
1178
1179
1180
1181
1182
1183
1184







-
+







    In particular, the software now builds under Mac OS X, though it
    does not yet run properly.  (The modified SimH PDP-8 simulator
    currently misbehaves if the PiDP-8/I panel is not present.  Fixing
    this is on the radar.)

*   Fixed a bunch of bugs!

[readme]:  https://tangentsoft.com/pidp8i/doc/trunk/README.md
[tlrm]:    https://tangentsoft.com/pidp8i/doc/trunk/README.md
[dupatch]: https://groups.google.com/forum/#!topic/pidp-8/fmjt7AD1gIA
[dudis]:   https://tangentsoft.com/pidp8i/tktview?name=e06f8ae936
[wiki]:    https://tangentsoft.com/pidp8i/wcontent
[ex]:      https://tangentsoft.com/pidp8i/doc/trunk/examples/README.md
[art]:     https://tangentsoft.com/pidp8i/dir?c=trunk&name=labels
[tix]:     https://tangentsoft.com/pidp8i/tickets

Changes to HACKERS.md.
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
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







-
-
-
-
+
+
+
+
+

-
-
+
-
-
-
+
-
-
-






-
-
-
-
-
-
+
+
+
+
+
+









-





-
-
-
-
+
+
+
+


-
+







You must use Fossil version 2.1 or higher with our repository, or you
will get an error.

If you started with one of our PiDP-8/I binary OS images made in or
after April 2017, Fossil 2.x is already installed.

If you're starting from some other OS, you either won't have Fossil
installed at all, or you'll most likley be using an older version, since
the Debian project is still shipping version 1.37 and likely will
continue to do so until 2020 or so.  You'll have to build Fossil from
source:
installed at all, or you'll most likley be using an older version,
since the Debian project is still shipping version 1.37 and likely
will continue to do so until 2020 or so.  You could build Fossil from
source, or you could just go grab a prebuilt binary we keep on the
project site:

    $ sudo apt install libssl-dev
    $ wget -O fossil-release.tar.gz \
    $ wget https://tangentsoft.com/pidp8i/uv/fossil-raspbian-9.1-stretch
      https://fossil-scm.org/index.html/tarball/fossil-release?uuid=release
    $ tar xvf fossil-release.tar.gz
    $ cd fossil-release
    $ sudo install -m 755 fossil-* /usr/local/bin/fossil
    $ ./configure
    $ make
    $ sudo make install

Fossil is also available for all common desktop platforms.  One of [the
official binaries][fbin] may work on your system.  If you're getting
binaries from a third party, be sure it is Fossil 2.1 or higher.


[fbin]:   http://fossil-scm.org/index.html/uv/download.html
[dvcs]:   http://en.wikipedia.org/wiki/Distributed_revision_control
[fbook]:  http://www.fossil-scm.org/schimpf-book/home
[fml]:    http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/fossil-users
[fossil]: http://fossil-scm.org/
[fqsg]:   http://fossil-scm.org/index.html/doc/trunk/www/quickstart.wiki
[fbin]:   https://fossil-scm.org/index.html/uv/download.html
[dvcs]:   https://en.wikipedia.org/wiki/Distributed_revision_control
[fbook]:  https://www.fossil-scm.org/schimpf-book/home
[fml]:    https://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/fossil-users
[fossil]: https://fossil-scm.org/
[fqsg]:   https://fossil-scm.org/index.html/doc/trunk/www/quickstart.wiki
[ggml]:   https://groups.google.com/forum/#!forum/pidp-8


Fossil Anonymous Access
----

To clone the code repository anonymously, say:

    $ mkdir -p ~/museum ~/src/pidp8i/trunk
    $ sudo apt install fossil
    $ fossil clone https://tangentsoft.com/pidp8i ~/museum/pidp8i.fossil
    $ cd ~/src/pidp8i/trunk
    $ fossil open ~/museum/pidp8i.fossil

The `clone` command gets you a file called `pidp8i.fossil` containing
the full history of PiDP-8/I from the upstream 2015.12.15 release
onward.  You can call that clone file anything you like and put it in
any directory you like.  Even the `.fossil` extension is largely a
convention, not a requirement.
the full history of the PiDP-8/I software project from the upstream
2015.12.15 release onward.  You can call that clone file anything you
like and put it in any directory you like.  Even the `.fossil` extension
is largely a convention, not a requirement.


Working With Existing Tags and Branches
Working with Existing Tags and Branches
----

The directory structure shown in the commands above is more complicated
than strictly necessary, but it has a number of nice properties.

First, it collects other software projects under a common top-level
directory, which I'm calling `~/src`, but you are free to use any scheme
109
110
111
112
113
114
115
116
117
118
119




120
121
122
123
124
125
126
103
104
105
106
107
108
109




110
111
112
113
114
115
116
117
118
119
120







-
-
-
-
+
+
+
+







our scope here.  See the [Fossil Quick Start Guide][fqsg] and the
documents it links to for more details.)

This directory scheme shows an important difference between Fossil and
Git: with Git, the checkout and the clone are intermingled in the same
directory tree, but in Fossil, they are strictly separate.  Git can
emulate Fossil's normal working style through its [worktree][gitwt]
feature, but it's a kind of lash-up using symlinks and such, whereas
with Fossil, there is no confusion: the repository clone is a single
SQLite database file — here, `pidp8i.fossil` — and the checkouts are
made from the contents of that database.
feature, but it employs some trickery that causes some unwanted side
effects that don't affect Fossil by design: the repository clone is a
single SQLite database file — here, `pidp8i.fossil` — and the checkouts
are made from the contents of that database.

Another important difference relative to Git is that with Fossil, local
checkins attempt to automatically sync checked-in changes back to the
repository you cloned from.  (This only works if you have a login on the
remote repository, the subject of the next section.)  This solves a
number of problems with Git, all stemming from the fact that Git almost
actively tries to make sure every clone differs from every other in some
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
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







-
+

+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
+
+














-
+








Fossil Developer Access
----

If you have a developer account on tangentsoft.com's Fossil instance, just
add your username to the URL like so:

    $ fossil clone http://username@tangentsoft.com/pidp8i pidp8i.fossil
    $ fossil clone https://username@tangentsoft.com/pidp8i pidp8i.fossil

If you've already cloned anonymously, you don't have to clone again to
inform Fossil about your developer account.  Just do a manual sync,
changing the URL to include the user name:

    $ fossil sync https://username@tangentsoft.com/pidp8i

Fossil will ask you for the password for `username` on the remote Fossil
instance, and it will offer to remember it for you.  If you let it
remember the password, operation from then on is scarcely different from
working with an anonymous clone, except that on checkin, your changes
will be sync'd back to the repository on tangentsoft.com if you're
online at the time.
Either way, Fossil will ask you for the password for `username` on the
remote Fossil instance, and it will offer to remember it for you.  If
you let it remember the password, operation from then on is scarcely
different from working with an anonymous clone, except that on checkin,
your changes will be sync'd back to the repository on tangentsoft.com if
you're online at the time, and you'll get credit under your developer
account name for the checkin.

If you're working offline, Fossil will still do the checkin, but you'll
be able to sync with the central repoisitory once you get back online.
If you're working offline, Fossil will still do the checkin locally, and
it will sync up with the central repoisitory after you get back online.
It is best to work on a branch when unable to use Fossil's autosync
feature, as you are less likely to have a sync conflict when attempting
to send a new branch to the central server than in attempting to merge
your changes to the tip of trunk into the current upstream trunk, which
may well have changed since you went offline.

You can purposely work offline by disabling autosync mode:

    $ fossil set autosync 0

Until you re-enable it (`autosync 1`) Fossil will stop trying to sync
your local changes back to the central repo.  In this mode, Fossil works
more like Git's default mode, buying you many of the same problems that
go along with that working style.  I recommend disabling autosync mode
only when you are truly going to be offline, and don't want Fossil
only when you are truly going to be offline and don't want Fossil
attempting to sync when you know it will fail.


Getting Developer Access
----

The administrator of this repository is Warren Young, whose email you
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
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







-
+





-
-
-
-
+
+
+
+
+
+
















-
-
-
-
-
-
-
+
+
+
+
+
+
+
-




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







Manipulating the Build System Source Files
----

The [autosetup build system][asbs] is composed of these files and
directories:

     auto.def
     autosetup/
     autosetup/*
     configure
     Makefile.in

Unlike with GNU Autoconf, which you may be familiar with, the
`configure` script is not output from some other tool.  It is just a
driver for the Tcl and C code under the `autosetup` directory.  If you
have to modify any of these files to get some needed effect, you should
try to get that change into the upstream project, then merge that change
down into the local copy when it lands upstream.
driver for the Tcl and C code under the `autosetup` directory.

If you have to modify any of the files in `autosetup/` to get some
needed effect, you should try to get that change into the upstream
[Autosetup][asbs] project, then merge that change down into the local
copy when it lands upstream.

The bulk of the customization to the build system is in `auto.def`,
which is a Tcl script run by `autosetup` via the `configure` script.
Some knowledge of [Tcl syntax][tcldoc] will therefore be helpful in
modifying it.

If you do not have Tcl installed on your system, `configure` builds a
minimal Tcl interpreter called `jimsh0`, based on the [Jim Tcl][jim]
project.  Developers working on the build system are encoruaged to use
this stripped-down version of Tcl rather than "real" Tcl because Jim Tcl
is more or less a strict subset of Tcl, so any changes you make that
work with the `jimsh0` interpreter should also work with "real" Tcl, but
not vice versa.  If you have Tcl installed and don't really need it,
consider uninstalling it to force `autosetup` to build and use `jimsh0`.

The `Makefile.in` file is largely a standard [GNU `make`][gmake] file
excepting only that it has variables substituted into it by
[`autosetup`][asbs] using its `@VARIABLE@` syntax.  At this time, we do
not attempt to achieve compatibility with other `make` programs, though
in the future we may need it to work with [BSD `make`][bmake] as well,
so if you are adding features, you might want to stick to the common
subset of features implemented by both the GNU and BSD flavors of
`make`.  We do not anticpate any need to support any other `make`
excepting only that it has variables substituted into it by `autosetup`
using its `@VARIABLE@` syntax.  At this time, we do not attempt to
achieve compatibility with other `make` programs, though in the future
we may need it to work with [BSD `make`][bmake] as well, so if you are
adding features, you might want to stick to the common subset of
features implemented by both the GNU and BSD flavors of `make`.  We do
not anticpate any need to support any other `make` flavors.
flavors.

(This, by the way, is why we're not using some heavy-weight build system
such as the GNU Autotools, CMake, etc.  The primary advantage of GNU
Autotools is that you can generate source packages that will configure
and build on weird and ancient flavors of Unix; we don't need that.
Cross-platform build systems such as CMake ease building the same
software on multiple disparate platforms straightforward, but the
PiDP-8/I software is built primarily on and for a single operating
system, Rasbpian Linux.  It also happens to build and run on other
modern Unix and Linux systems, for which we also do not need the full
power of something like CMake.  `autosetup` and GNU `make` suffice for
our purposes here.)

[asbs]:   http://msteveb.github.io/autosetup/
[bmake]:  https://www.freebsd.org/doc/en/books/developers-handbook/tools-make.html
[gmake]:  https://www.gnu.org/software/make/
[jim]:    http://jim.tcl.tk/
[tcldoc]: http://wiki.tcl.tk/11485


<a id="dirs"></a>
Directory Structure
----

The directory structure of the PiDP-8/I project is as follows:

*   <b>`.`</b> - Top level, occupied only by the few files the end user
    of the source code needs immediately at hand on first unpacking the
    project: the top level build system files, the top-level
    `README*.md` files, and licensing information. If a given file *can*
    be buried deeper, it *should* be buried to reduce clutter at this
    most precious level of the hierarchy.

*   <b>`.fossil-settings`</b> - Versioned settings for the Fossil build
    system.  Say `fossil help set` at the command line for more on this.
    Such settings are intended to be correct for all users of the
    system; rather than expressing defaults, they express *policy*.

    Any setting whose value may vary between users of the Fossil
    repository should be done locally with a `fossil set` command.

*   <b>`autosetup`</b> - The bulk of the [Autosetup build system][asbs].
    These are generic files, not modified by the project itself. We
    occasionally run `tools/autosetup-update` to merge in upstream
    changes.

*   <b>`bin`</b> - Programs run both in development and after
    installation.  Some files here are created directly by the project's
    developers, while others are outputs of the build system. The
    content of this directory is copied to `$prefix/bin` at installation
    time, which is added to the user's `PATH` by the installer.

*   <b>`boot`</b> - SIMH initialization scripts. The `*.script.in` files
    are written by the project developers but have build-time values
    substituted in by the `configure` script to produce a `*.script`
    version. Some of the remaining `*.script` files are hand-written and
    as such are checked into Fossil directly. The remainder are outputs
    of `tools/mkbootscript`, which produces them from `palbart` assembly
    listings.

    All of these `*.script` files are installed to `$prefix/share/boot`
    regardless of their origin.

*   <b>`doc`</b> - Documentation files not immediately important enough
    to a new user of the software that they do not have to be at the top
    level of the project tree.

    Fossil allows us to treat the contents of `doc` much like the wiki,
    so how do we decide whether to put a given document into `doc` or
    the wiki?  The rule is simple: is the document's history tied to the
    history of the PiDP-8/I project itself?  If so, it goes in `doc`,
    else it goes in the wiki.  When checking out older versions of the
    PiDP-8/I software, you expect to roll back to contemporaneous
    versions of the project documentation; such files go into `doc`.
    Documents which are independent of the PiDP-8/I project history go
    into the wiki.

    (The wiki does also have history, but rolling back to a prior
    version of the PiDP-8/I repository and then saying `fossil ui` will
    show you the current version of the wiki documents, not the versions
    as they existed at the time of the historical checkin you rolled
    back to.)

    The `doc/graphics` subdirectory holds JPEGs and SVGs displayed
    inline within wiki articles.

*   <b>`etc`</b> - Files which get copied to `/etc` or one of its
    subdirectories at installation time.

*   <b>`examples`</b> - Example programs for the end user's edification.
    Many of these are referenced by documentation files.

*   <b>`hardware`</b> - Schematics and such for the PiDP-8/I board or
    associated hardware.

*   <b>`labels`</b> - Graphics intended to be printed out and used as
    labels for removable media.

*   <b>`lib`</b> - Library routines used by other programs.

*   <b>`libexec`</b> - A logical extension of `lib`, these are
    standalone programs that nevertheless are intended to be run
    primarily by other programs. Whereas a file in `lib` might have its
    interface described by a programmer's reference manual, the
    interface of a program in `libexec` is described by its usage
    message. Examples:

    *   <b>`mkos8`</b> - Run by the build system.
    
        <p>It is sometimes run by hand in development, but primarily
        only to further its development. Once it runs correctly after
        adding some feature, we let <code>make</code> run it for us.</p>

    *   <b>`scanswitch`</b> - Run by `etc/pidp8i`.
    
        <p>As with <code>mkos8</code>, it is generally run by hand only
        by developers modifying its behavior.</p>

    Programs in `libexec` are installed to `$prefix/libexec`, which is
    *not* put into the user's `PATH`, on purpose. If a program should
    end up in the user's `PATH`, it belongs in `bin`. Alternately, a
    wrapper may be put in `bin` which calls a `libexec` program as a
    helper.

*   <b>`media`</b> - Binary media images used either by SIMH directly or
    by tools like `mkos8` to produce media used by SIMH.

    The contents of this tree are installed to `$prefix/share/media`.

*   <b>`obj`</b> - Intermediate output directory used by the build
    system.  It is safe to remove this directory at any time, as its
    contents may be recreated by `make`. No file checked into Fossil
    should be placed here.

    (Contrast `bin` which does have some files checked into Fossil; all
    of the *other* files that end up in `bin` can be recreated by
    `make`, but not these few hand-written programs.)

*   <b>`src`</b> - Source code for the project's programs, especially
    those that cannot be used until they are built. The build system's
    output directories are `bin`, `boot`, `libexec`, and `obj`.

    Programs that can be used without being "built", example programs,
    and single-file scripts are placed elsewhere: `bin`, `examples`,
    `libexec`, `tools`, etc. Basically, we place such files where the
    build system *would* place them if they were built from something
    under `src`.

    The top level of `src` is for the SIMH core, with the PDP-8
    simulator specific bits in the `PDP8` subdirectory.

    The subdirectories of `src` are for other programs' source code.

*   <b>`test`</b> - Output directory used by `tools/test-*`.

*   <b>`tools`</b> - Programs run only during development and not
    installed.

    If a program is initially created here but we later decide that it
    should be installed for use by end users of the PiDP-8/I system, we
    move it to either `bin` or `libexec`, depending on whether it is run
    directly at the command line or run from some other program that is
    also installed, respectively.


<a id="patches"></a>
Submitting Patches
----

If you do not have a developer login on the PiDP-8/I software
repository, you can still send changes to the project.

The simplest way is to say this after developing your change against the
362
363
364
365
366
367
368
369

370
371
372
373
374
375
376
510
511
512
513
514
515
516

517
518
519
520
521
522
523
524







-
+








Please make your patches or experimental branch bundles against the tip
of the current trunk.  PiDP-8/I often drifts enough during development
that a patch against a stable release may not apply to the trunk cleanly
otherwise.

[osil]:  https://opensource.org/licenses
[repo]:  http://tangentsoft.com/pidp8i/
[repo]:  https://tangentsoft.com/pidp8i/
[simhl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md
[viral]: https://en.wikipedia.org/wiki/Viral_license


The PiDP-8/I Software Project Code Style Rules
----

Changes to Makefile.in.
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
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







-
+

+
+
+
-
-
-
-
+
+
+
+
+

+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

-
+

-
+







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

# Git commit ID of the latest version of the SIMH 4 project on GitHub 
# that has been merged into this source base.
SGCID=17903827bdb294f7e60d4c7f172bd6a1a71dfbd5
SGCID=27f9fc3c3e7ff105c6102dfffc6da7fb838d1e8f

# C build flags for src/*.c and PDP8/*.c, containing the SIMH core
# modules and the SIMH PDP-8 simulator, respectively.
TOP_CFLAGS := @CFLAGS@ @BUILDMODE@ \
CFLAGS = @CFLAGS@ -Wno-unused-result -Wno-parentheses @BUILDMODE@ \
	-DUSE_READER_THREAD -DHAVE_DLOPEN=so -DPIDP8I -DSIM_ASYNCH_IO \
	-DHAVE_REGEX_H -DHAVE_GLOB -DSIM_GIT_COMMIT_ID=$(SGCID) \
	-D_GNU_SOURCE -U__STRICT_ANSI__ \
	-Wno-unused-result -Wno-parentheses \
	-DUSE_READER_THREAD -DHAVE_DLOPEN=$(subst .,,@SH_SOEXT@) -DPIDP8I \
	-DSIM_ASYNCH_IO -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
PDP8_CFLAGS = $(TOP_CFLAGS)

# Greatly stripped-down build options for the cc8 cross-compiler
# primarily because it's K&R C.  Building under TOP_CFLAGS spews
# pages of warnings.  The only thing we share is whether to build
# a debug or optimized version, from BUILDMODE, set by auto.def.
CC8_CROSS_CFLAGS = -w @BUILDMODE@

# Krten's d8tape doesn't need much in the way of options.  It builds
# without warnings under the supported version of Raspbian.  On some
# other OS types, it complains about one of the printf() format
# specifiers, which we cannot fix portably, so we suppress that one.
D8TAPE_CFLAGS = -Wno-format @BUILDMODE@

# Ditto palbart.
PALBART_CFLAGS = @BUILDMODE@

SIM = bin/pidp8i-sim
BINS = $(SIM) @CC8_CROSS@ \
	bin/d8tape \
	bin/palbart \
	bin/pidp8i-test \
	bin/ptp2txt \
BINS = bin/palbart $(SIM) bin/pidp8i-test libexec/scanswitch
	libexec/scanswitch
BIN_SCRIPTS = \
	bin/pidp8i \
	@srcdir@/bin/teco-pi-demo

# Some *.py files are generated by Autosetup, so they need different
# handling than the other libraries for the out-of-tree build case.
PIDP8I_DIRS := lib/pidp8i/dirs.py
PIDP8I_DIN  := @srcdir@/$(PIDP8I_DIRS).in
GENNED_PY   := \
	lib/pidp8i/__init__.py \
	lib/pidp8i/ips.py \
	$(PIDP8I_DIRS)

SIMH_PY     := lib/simh.py
SIMH_PY_SRC := @srcdir@/$(SIMH_PY)

MKOS8         := @srcdir@/libexec/mkos8
MKOS8_LIB     := @srcdir@/lib/mkos8
MKOS8_PY      := \
	$(MKOS8_LIB)/__init__.py \
	$(MKOS8_LIB)/argparser.py \
	lib/mkos8/opts.py
MKOS8_PY_ALL  := $(GENNED_PY) $(MKOS8_PY) $(SIMH_PY_SRC)
MKOS8_SRCS    := $(MKOS8) $(MKOS8_PY_ALL) $(PIDP8I_DIN)

BUILDDIRS = bin libexec obj/PDP8
BUILDDIRS := bin libexec obj/cc8/cross obj/d8tape obj/palbart obj/PDP8

INSTDIRS = bin etc libexec share/boot share/media share/man/man1
INSTDIRS := bin etc lib/mkos8 lib/pidp8i libexec share/boot share/media share/include share/man/man1

OBJS = \
SIM_OBJS := \
	obj/gpio-common.o \
	obj/PDP8/pdp8_df.o \
	obj/PDP8/pdp8_cpu.o \
	obj/PDP8/pdp8_clk.o \
	obj/PDP8/pdp8_ct.o \
	obj/PDP8/pdp8_dt.o \
	obj/PDP8/pdp8_fpp.o \
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+









-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








+



-
-
-
-
+
+
-
-
-
-
-
-
+
+


+
+


-
+




+
+
-
+


-
+
-


-
-
-
-
+
+
+
+
+
+
-
+

+



+
-
+





-
+




-
+


-
-
-

+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
+







	obj/sim_serial.o \
	obj/sim_sock.o \
	obj/sim_tape.o \
	obj/sim_timer.o \
	obj/sim_tmxr.o \
	obj/sim_video.o

CC8_OBJS := \
	obj/cc8/cross/code8.o \
	obj/cc8/cross/data.o \
	obj/cc8/cross/error.o \
	obj/cc8/cross/expr.o \
	obj/cc8/cross/function.o \
	obj/cc8/cross/gen.o \
	obj/cc8/cross/io.o \
	obj/cc8/cross/lex.o \
	obj/cc8/cross/main.o \
	obj/cc8/cross/preproc.o \
	obj/cc8/cross/primary.o \
	obj/cc8/cross/stmt.o \
	obj/cc8/cross/sym.o \
	obj/cc8/cross/while.o

D8TAPE_OBJS := \
	obj/d8tape/dasm.o \
	obj/d8tape/flow.o \
	obj/d8tape/main.o \
	obj/d8tape/version.o

MISC_OBJS := \
	obj/ptp2txt.o \
	obj/scanswitch.o \
	obj/test.o

PALBART_OBJS := obj/palbart/palbart.o

ifeq (@BUILD_DEEPER_THOUGHT@, 1)
BINS += bin/deeper
endif

LIBS = -lm -ldl -lpthread

ASM_PTS     := $(wildcard @srcdir@/asm/*.pal)
ASM_PTS     := $(subst @srcdir@/asm,bin,$(ASM_PTS))
ASM_PTS     := $(ASM_PTS:.pal=.pt)
EX_PTS      := $(wildcard @srcdir@/examples/*.pal)
EX_PTS      := $(subst @srcdir@/examples,bin,$(EX_PTS))
EX_PTS      := $(EX_PTS:.pal=.pt)
LISTINGS    := $(ASM_PTS:.pt=.lst) $(EX_PTS:.pt=.lst)
ASM_PTS     := $(wildcard @srcdir@/src/asm/*.pal)
ASM_PTS     := $(subst @srcdir@/src/asm,bin,$(ASM_PTS))
ASM_PTS     := $(ASM_PTS:.pal=-pal.pt)
FC_EX_PTS   := $(wildcard @srcdir@/examples/*.fc)
FC_EX_PTS   := $(subst @srcdir@/examples,bin,$(FC_EX_PTS))
FC_EX_PTS   := $(FC_EX_PTS:.fc=-focal.pt)
PAL_EX_PTS  := $(wildcard @srcdir@/examples/*.pal)
PAL_EX_PTS  := $(subst @srcdir@/examples,bin,$(PAL_EX_PTS))
PAL_EX_PTS  := $(PAL_EX_PTS:.pal=-pal.pt)
LISTINGS    := $(ASM_PTS:-pal.pt=.lst) $(PAL_EX_PTS:-pal.pt=.lst)
LISTINGS    := $(subst bin/,obj/,$(LISTINGS))
BOOTSCRIPTS := $(LISTINGS:.lst=.script)
BOOTSCRIPTS := $(subst obj/,boot/,$(BOOTSCRIPTS)) \
	boot/1.script \
	boot/5.script

# List of *.in files from auto.def file, except for this present file
# (Makefile.in) which is handled separately.  This list should only
# change when the list of "make-template" calls in auto.def changes.
#
# If the first file listed below changes, change the AUTOREBUILD rule
# near the end of this file to match!
# 
# The MKOS8_INFILES set of files are those which, if changed, require
# rebuilding the OS/8 media.  Touching the combined set merely causes
# a reconfig and build.
#
# The PRECIOUS set are those whose outfiles we want make(1) to treat as
# "precious", meaning it won't delete files generated by a target if the
# rule being processed to create that target fails.
# 
# The rest have no special treatment.
MKOS8_INFILES = \
	@srcdir@/lib/pidp8i/__init__.py.in \
	@srcdir@/lib/pidp8i/ips.py.in \
	@srcdir@/media/os8/init.tx.in \
	@srcdir@/src/PDP8/pidp8i.c.in \
	$(PIDP8I_DIN)
PRECIOUS_INFILES = \
	@srcdir@/Makefile.in \
    @srcdir@/examples/Makefile.in \
    @srcdir@/src/Makefile.in \
    @srcdir@/src/cc8/Makefile.in \
    @srcdir@/src/PDP8/Makefile.in
INFILES = \
	@srcdir@/bin/pidp8i.in \
	@srcdir@/boot/0.script.in \
	@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@/boot/run.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/mkos8.in \
	@srcdir@/tools/simh-update.in
PRECIOUS_INFILES = \
	@srcdir@/tools/simh-update.in \
	$(MKOS8_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))
MKOS8_OUTFILES := $(subst @srcdir@/,,$(MKOS8_INFILES))
MKOS8_OUTFILES := $(subst .in,,$(MKOS8_OUTFILES))
PRECIOUS_OUTFILES := $(subst @srcdir@/,,$(PRECIOUS_INFILES))
PRECIOUS_OUTFILES := $(subst .in,,$(PRECIOUS_OUTFILES))
OUTFILES := $(subst @srcdir@/,,$(INFILES))
OUTFILES := $(subst .in,,$(OUTFILES))

OS8_BIN_RK05 = bin/os8v3d-bin.rk05
OS8_SRC_RK05 = bin/os8v3d-src.rk05
OS8_SRC_RK05 = @OS8_SRC_RK05@
OS8_RK05S = $(OS8_BIN_RK05) $(OS8_SRC_RK05)

CLTXT = /boot/cmdline.txt

ADF := adrules.mk

.PHONY: tags
.PHONY: all tags
.PRECIOUS: $(PRECIOUS_OUTFILES)

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

clean:
	@rm -f $(BINS) $(BOOTSCRIPTS) $(ASM_PTS) $(EX_PTS) $(LISTINGS) $(OBJS) $(OUTFILES) \
		tags \
		obj/*.d \
		obj/*.o \
	@rm -f $(BINS) $(BOOTSCRIPTS) $(ASM_PTS) $(PAL_EX_PTS) $(LISTINGS) \
		$(OUTFILES) $(ADF) \
		config.log cscope.out tags \
		bin/*.pt bin/*.rk05 bin/*.save bin/txt2ptp \
		lib/*.pyc lib/*/*.pyc lib/mkos8/opts.py \
		obj/*.log obj/*.pt obj/mkos8.opts \
		obj/PDP8/*.d \
		src/config.h \
		@srcdir@/examples/*.err
	@find obj \( -name \*.o -o -name \*.d \) -delete
	@-rmdir -p $(BUILDDIRS) 2> /dev/null || true

distclean: clean
	@rm -f \
	@rm -f $(PRECIOUS_OUTFILES) \
		$(PRECIOUS_OUTFILES) \
		config.log \
		autosetup/jimsh0 \
		src/config.h

ctags tags:
	@ctags -R @srcdir@
	ctags -R @srcdir@/src @srcdir@/lib @srcdir@/libexec/mkos8
ifeq (@HAVE_PROG_CSCOPE@, 1)
	@cscope -bR -s@srcdir@
endif

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

	@# Create any missing install tree directories
	for d in $(INSTDIRS) ; do @INSTALL@ -m 755 -d @prefix@/$$d ; done

	@# Install files into those dirs and set their perms
	@for f in $(BINS) ; do \
		dest=@prefix@/bin/$$(basename $$f) ; \
		echo "Installing binary $$dest..." ; \
	for f in $(BINS) ; do @INSTALL@ -m 755 -D -s $$f @prefix@/$$f ; done
	@INSTALL@ -m 755 @srcdir@/bin/pidp8i @prefix@/bin
	-for f in @prefix@/bin/pidp8i-* ; do setcap 'cap_sys_nice=eip' $$f ; done || true
	test -e @MEDIADIR@/os8/os8.rk05 || $(MAKE) mediainstall
		@INSTALL@ -m 755 -D -s $$f $$dest ; \
	done
	@for f in $(BIN_SCRIPTS) ; do \
		dest=@prefix@/bin/$$(basename $$f) ; \
		echo "Installing script $$dest..." ; \
		@INSTALL@ -m 755 -D $$f $$dest ; \
	done
	@( cd @prefix@/bin ; \
	   echo "Installing txt2ptp symlink..." ; \
	   ln -f ptp2txt txt2ptp ; \
    )
	@(test -x /sbin/setcap && \
		for f in @prefix@/bin/pidp8i-* ; do \
			echo "Setting real-time priority capabilities on $$(basename $$f)..." ; \
			/sbin/setcap 'cap_sys_nice=eip' $$f ; \
		done \
	) || true
	@test -e @MEDIADIR@/os8/os8.rk05 || echo "Installing media..." && $(MAKE) mediainstall

	@# If this is a Debian-type system, install needed helper programs
	@test -x /usr/bin/apt-get -a ! -h /media/usb && apt-get -y install usbmount || true
	@test -x /usr/bin/apt-get -a ! -x /usr/bin/screen && apt-get -y install screen || true

	@# Disable competing services if this is a Raspberry Pi
	@(test -x /bin/systemctl && /bin/systemctl disable deeper || true)
	@(test -x /bin/systemctl && /bin/systemctl disable pidp8  || true)

	@# Install the init script if this system is systemd based.
	@INSTALL@ -m 755 @srcdir@/etc/pidp8i-init @prefix@/etc
	@@INSTALL@ -m 755 @srcdir@/etc/pidp8i-init @prefix@/etc
	@(  test -w /etc/init.d -a -x /bin/systemctl && \
		ln -sf @ABSPREFIX@/etc/pidp8i-init /etc/init.d/pidp8i && \
		/bin/systemctl enable pidp8i \
	) || true

	@# Give the install user permission to use GPIO if done on a Pi
	@grep -q '^gpio:' /etc/group && usermod -a -G gpio @INSTUSR@ || true
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







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







+
-
+










+
+
+
+
+
+

-
-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+








-
-
+
+
+
+
+
+
+
+
+
+







-
-
+
+




+
+

+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-


+
+
+
-
-
+
+



+
+
+
-
+
-

-
-
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+

-
-
+
+


-
+


-
+
+
+
+
+
+













-
-
-
-



-
+

+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+






+
+
+
+
+
+
+
+

-
+


-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+







+
+
-
-
+
+
+
+
+
+
+
	done) || true

	@# Ditto for MANPATH
	@(for p in .profile .bash_profile ; do \
		test -n "$$SUDO_USER" -a -w "/home/$$SUDO_USER/$$p" && \
			! grep -qF "@ABSPREFIX@/share/man" "/home/$$SUDO_USER/$$p" && \
			echo "export MANPATH=\$$MANPATH:@ABSPREFIX@/share/man" >> "/home/$$SUDO_USER/$$p" ; \
		done \
	done) || true
	) || true

	@# If serial mod is disabled, turn off serial console and kgdb stuff
	@# in case they were enabled previously, else they will fight with
	@# our use of GPIO.
	@(  test -z "@PCB_SERIAL_MOD_ANY@" -a -r $(CLTXT) && ! -w $(CLTXT) && \
		cp -p $(CLTXT) "$(CLTXT)"_orig && \
		sed -e 's/console\=[a-zA-Z0-9]+,[0-9]+ //' \
		    -e  's/kgdboc\=[a-zA-Z0-9]+,[0-9]+ //' -i $(CLTXT) \
	) || true

	@# Install CC8 stuff if built
	@test -n "@CC8_CROSS@" && \
		echo "Installing cc8 cross-compiler..." ; \
		@INSTALL@ -m 755 bin/cc8 @prefix@/bin && \
		@INSTALL@ -m 644 @srcdir@/src/cc8/include/* @prefix@/share/include

	@# Install palbart stuff
	@INSTALL@ -m 755 bin/palbart @prefix@/bin
	@INSTALL@ -m 644 @srcdir@/palbart/palbart.1 @prefix@/share/man/man1
	@@INSTALL@ -m 755 bin/palbart @prefix@/bin
	@@INSTALL@ -m 644 @srcdir@/src/palbart/palbart.1 @prefix@/share/man/man1

	@# Install mkos8 and its dependencies
	@echo "Installing mkos8..."
	@@INSTALL@ -m 775 -g @INSTGRP@ $(MKOS8) @prefix@/libexec
	@( for src in $(MKOS8_PY_ALL) ; do \
		test -e $$src || src=@srcdir@/$$src ; \
		dest=@prefix@/$$(echo $$src | sed -e 's_^@srcdir@/__') ; \
		echo "Installing $$src to $$dest..." ; \
	    @INSTALL@ -m 644 -g @INSTGRP@ -D $${src}  $${dest}  ; \
	    @INSTALL@ -m 644 -g @INSTGRP@ -D $${src}c $${dest}c ; \
	  done \
	)
	@sed -e 's#^build =.*#build = "@ABSPREFIX@"#' \
	     -e 's#^media =.*#media = os.path.join (build, "share/media/")#' \
	     -e 's#^os8mo =.*#os8mo = os8mi#' \
		 < $(PIDP8I_DIRS) > @prefix@/$(PIDP8I_DIRS)
	@chgrp @INSTGRP@ @prefix@/$(PIDP8I_DIRS)

instdirs:
	@echo "Creating installation directory tree..."
	@for d in $(INSTDIRS) ; do @INSTALL@ -m 755 -d @prefix@/$$d ; done

mediainstall:
mediainstall: instdirs
	@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@
	@@INSTALL@ -m 644 -o @INSTUSR@ -g @INSTGRP@ bin/os8v3d-*.rk05 @ABSPREFIX@/share/media/os8
	@@INSTALL@ -m 664 -o @INSTUSR@ -g @INSTGRP@ boot/*.script @BOOTDIR@

# No-dependencies versions of the bin/os8v3d-*.rk05 targets used by
# tools/test-mkos8, because there's no point rebuilding the simulator
# on each iteration.
os8-bin:
	$(MKOS8)@MKOS8_OPTS@ bin
os8-patched:
	$(MKOS8)@MKOS8_OPTS@ bin patch

reconfig:
	@AUTOREMAKE@

release: all
	@srcdir@/tools/mkrel

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

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

test-mkos8:
	tools/test-mkos8


# Build the OS/8 binary media needed by 0.script
OS8_BIN_SRCS = \
# Build the OS/8 binary media needed by 0.script.
#
# We use order-only prerequisites for the simulator here because we only
# care that it *exists*, not whether it is newer than the previously
# built RK05 media or not.
#
# Also notice that the init.tx file is not a prerequisite: we purposely
# do not rebiuld the RK05 media just because the configure script was
# re-run, which *always* regenerates the init.tx file because the
# timestamp always changes from one run to the next.  (Until computers
# get fast enough to do a complete re-configure in under a second,
# anyway!)  The thing is, we only want the RK05 bin media rebuilt when
# the configure --*-os8-* options change.  *That* is when we care about
# the updated init.tx file, not before.  We needn't even make it an
# order-only prereq because configure and the INFILES rules above ensure
# that it always exists.
OS8_BIN_SRCS := $(MKOS8_SRCS) \
	@srcdir@/tools/mkos8.in \
	@srcdir@/media/os8/al-*-ba-*.tu56 \
	@srcdir@/media/os8/subsys/*.tu56
ifneq (@MKOS8_BIN_PATCHES@,)
	OS8_BIN_SRCS += @srcdir@/media/os8/patches/patch_list.txt
endif
$(OS8_BIN_RK05): $(SIM) $(OS8_BIN_SRCS)
	tools/mkos8@MK_OS8_RK05_OPTS@ bin
$(OS8_BIN_RK05): $(OS8_BIN_SRCS) | $(SIM) $(MKOS8_OUTFILES)
	$(MKOS8)@MKOS8_OPTS@ bin @MKOS8_BIN_PATCHES@

# Also build an OS/8 source disk, as a convenience to avoid the 
# need to mount up the 7 source tapes in succession.
#
# Using an order-only dependency for the simulator and the bin disk: we
# only need *a* version of each, they don't have to be recent!
OS8_SRC_SRCS = \
OS8_SRC_SRCS = $(MKOS8_SRCS) \
	@srcdir@/tools/mkos8.in \
	@srcdir@/media/os8/al-*-sa-*.tu56
$(OS8_SRC_RK05): $(OS8_SRC_SRCS)
	tools/mkos8@MK_OS8_RK05_OPTS@ src
$(OS8_SRC_RK05): $(OS8_SRC_SRCS) | $(SIM) $(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
	$(CC) -MM $(CFLAGS) @srcdir@/src/$*.c > obj/$*.d
	@mv -f obj/$*.d obj/$*.d.tmp
	@sed -e 's|.*:|obj/$*.o:|' < obj/$*.d.tmp > obj/$*.d
	@sed -e 's/.*://' -e 's/\\$$//' < obj/$*.d.tmp | fmt -1 | \
		sed -e 's/^ *//' -e 's/$$/:/' >> obj/$*.d
	@rm -f obj/$*.d.tmp

# Near-duplicate of above rule for those *.c files generated from *.c.in
# files in the srcdir.  Needed when building out-of-tree.
obj/%.o: src/%.c
	$(CC)  -c $(CFLAGS) src/$*.c -o obj/$*.o
	$(CC) -MM $(CFLAGS) src/$*.c > obj/$*.d
	@mv -f obj/$*.d obj/$*.d.tmp
	@sed -e 's|.*:|obj/$*.o:|' < obj/$*.d.tmp > obj/$*.d
	@sed -e 's/.*://' -e 's/\\$$//' < obj/$*.d.tmp | fmt -1 | \
		sed -e 's/^ *//' -e 's/$$/:/' >> obj/$*.d
	@rm -f obj/$*.d.tmp

# Rule for building PAL assembly language programs in asm/*.pal.
obj/%.lst bin/%.pt: @srcdir@/asm/%.pal bin/palbart
# Rule for building PAL assembly language programs in src/asm/*.pal.
obj/%.lst bin/%-pal.pt: @srcdir@/src/asm/%.pal bin/palbart
	bin/palbart -lr $< || cat obj/$*.err
	mv @srcdir@/asm/$*.lst obj
	mv @srcdir@/asm/$*.rim bin/$*.pt
	mv @srcdir@/src/asm/$*.lst obj
	mv @srcdir@/src/asm/$*.rim bin/$*-pal.pt

# Ditto for those in examples/*.pal.
obj/%.lst bin/%.pt: @srcdir@/examples/%.pal bin/palbart
obj/%.lst bin/%-pal.pt: @srcdir@/examples/%.pal bin/palbart
	bin/palbart -lr $< || cat obj/$*.err
	mv @srcdir@/examples/$*.lst obj
	mv @srcdir@/examples/$*.rim bin/$*.pt
	mv @srcdir@/examples/$*.rim bin/$*-pal.pt

# Rule for converting ASCII FOCAL examples in examples/*.fc to
# bin/*-focal.pt.
bin/%-focal.pt: @srcdir@/examples/%.fc bin/txt2ptp
	bin/txt2ptp < $< > bin/$*-focal.pt

# Rule for translating PAL assembly language program listings to SIMH
# boot scripts.
boot/%.script: obj/%.lst
	@srcdir@/tools/mkbootscript $<

# Rules for making aliases of named example programs translated to boot
# scripts as special numbered boot scripts
boot/1.script: boot/hs-rim-loader.script
	ln -f $< $@
boot/5.script: boot/ac-mq-blinker.script
	ln -f $< $@

bin/palbart: @srcdir@/palbart/palbart.c
	$(CC) -Wall -Wno-strict-prototypes @BUILDMODE@ $< -o $@
	$(CC) -MM $< -o obj/palbart.d

$(BUILDDIRS):
	mkdir -p $@

$(SIM): $(OBJS) obj/gpio-@LED_DRIVER_MODULE@ls.o
$(SIM): $(SIM_OBJS) obj/gpio-@LED_DRIVER_MODULE@ls.o
	$(CC) -o $@ $^ $(LIBS)

bin/cc8: $(CC8_OBJS)
	$(CC) -o $@ $^ $(LIBS)

bin/d8tape: $(D8TAPE_OBJS)
	$(CC) -o $@ $^

bin/palbart: $(PALBART_OBJS)
	$(CC) -o $@ $^

bin/pidp8i-test: obj/test.o obj/gpio-nls.o obj/gpio-common.o
	$(CC) -o $@ $^ $(LIBS) -lncurses

bin/ptp2txt: obj/ptp2txt.o
	$(CC) -o $@ $^
	ln -f bin/ptp2txt bin/txt2ptp

bin/txt2ptp: bin/ptp2txt
	ln -f bin/ptp2txt bin/txt2ptp

bin/deeper: obj/deeper.o obj/gpio-@LED_DRIVER_MODULE@ls.o obj/gpio-common.o
	$(CC) -o $@ $^ $(LIBS)

libexec/scanswitch: obj/scanswitch.o obj/gpio-nls.o obj/gpio-common.o
	$(CC) -o $@ $^ $(LIBS)

# Reconfigure whenever one of the *.in or autosetup files changes unless
# this is "make clean".
# 
# We purposely list only one of the OUTFILES on the left hand side
# because to list them all is to invite Make to run N copies of the
# configure script in parallel up to the limit of -j or the number of
# files in INFILES, whichever is lower.  Order-only prerequisites can't
# help here (|) as that only affects the right hand side.
ifeq ($(findstring clean,$(MAKECMDGOALS)),)
Makefile: @srcdir@/Makefile.in @srcdir@/auto.def $(INFILES) $(PRECIOUS_INFILES) @AUTODEPS@
media/os8/init.tx: $(INFILES) $(PRECIOUS_INFILES) @AUTODEPS@
	@AUTOREMAKE@ && $(MAKE)

# Rebuild simulator whenever one of its *.in files change.
#
# This is trickier than it appears.  If you simply make $(OUTFILES)
# Also do it if the autodep tool is newer than its output, suggesting
# depend on $(INFILES), make(1) thinks it can build all of $(OUTFILES)
# in parallel if given -jN, which causes ./configure --args to run N
# times in parallel, which blows the build system up right good and
# proper; bits everywhere.  We purposely list only the first file in
# $(INFILES) here to prevent that.  The other files in $(INFILES) will
# also be built, which confuses make(1) somewhat, but it figures things
# out on the subsequent $(MAKE) call.
#
# We also list files like tools/version which, when they change, trigger
# a rebuild of $(OUTFILES) because their change may affect what gets
# generated.  This happened when we moved from 8 to 10 character Fossil
# that if re-run, it would generate different output.
# artifact IDs in the version string to match Fossil timeline's default.
bin/pidp8i: @srcdir@/bin/pidp8i.in @srcdir@/tools/version
$(ADF): @srcdir@/tools/mkadrules
	@AUTOREMAKE@ && $(MAKE)
endif

# Rebuild simulator if the version string tool changes, since its output
# may have changed.
src/gpio-common.c: @srcdir@/tools/version

# Pull in *.d files generated by the autodependency mechanism.  See the
# header comment of tools/mkadrules.
-include $(OBJS:.o=.d) obj/scanswitch.d

-include \
	$(SIM_OBJS:.o=.d) \
	$(CC8_OBJS:.o=.d) \
	$(D8TAPE_OBJS:.o=.d) \
	$(MISC_OBJS:.o=.d) \
	$(PALBART_OBJS:.o=.d)
-include $(ADF)
Changes to 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
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








-
-
+
+
+
+

+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
-
-
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
-
+


-
+



-
+



-
-
-
+
+
+
+
+
+
+



-
-
+
+
+
-
+
+
+


-
+







# Getting Started with the PiDP-8/I Software

## Prerequisites

*   A Raspberry Pi with the 40-pin GPIO connector.  That rules out the
    first-generation Raspberry Pi model A and B boards which had a
    26-pin GPIO connector.

*   An SD card containing Raspbian or something sufficiently close.
    This software is currently tested with the Jessie Lite distribution.
*   An SD card containing [a compatible OS][os].

*   This software distribution, unpacked somewhere convenient within the
    filesystem on the Raspberry Pi.

    We recommend that you unpack it somewhere your user has read/write
    access like `$HOME/src/pidp8i`. Since it installs as a system
    service, you might prefer `/usr/local/src` or `/opt/src`, though
    you'll have to adjust permissions for that.
    Ideally, you will install a fresh OS image onto an unused SD card
    rather than use this software to modify an existing OS installation,
    but there is currently no known hard incompatibilty that prevents
    you from integrating this software into an existing OS.

    The [old stable 2015.12.15 release][osd] required that you unpack
    the software into `/opt/pidp8`, but we now neither require nor
    recommend that.

*   We require several tools and libraries that aren't always installed:

    *   A working C compiler and other standard Linux build tools,
        such as `make(1)`.

    *   Python's `pexpect` library
*   This software distribution, unpacked somewhere convenient within the
    Raspberry Pi filesystem.

    Unlike with the [upstream 2015.12.15 release][upst], this present
    release of the software should *not* be unpacked into `/opt/pidp8`.
    I recommend that you unpack it into `$HOME/src`, `/usr/local/src` or
    similar, but it really doesn't matter where you put it, as long as
    your user has full write access to that directory.

    *   The `usbmount` tool
    
        This is provides two things:
        
        *   USB stick auto-mounting on stripped-down OSes like Raspbian
            Lite so you can use the PiDP-8/I `SING_STEP` + `DF` feature
            without having to manually mount the USB stick first.
        
        *   A known directory structure that allows the PiDP-8/I
            software to find the media image files on those sticks.
            (`*.pt`, `*.dt`, `*.rk`, etc.)

        Full-blown GUI OSes tend to have USB auto-mounting set up
        already, though they won't meet the second criteria unless they
        use the same directory layout as `usbmount`: `/media/usbN`,
        where `N` is a number from 0 to 7, depending on the order
        you attached the USB stick.  Many Linuxes use `/media/LABEL`
        instead, for example, where `LABEL` is the partition's label;
        the PiDP-8/I software won't find the files on those USB sticks
        in that case.

    *   The `ncurses` development libraries
*   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`

    To install all of this on a Raspbian type OS, say:

        $ sudo apt update
        $ sudo apt install build-essential
        $ sudo apt install libncurses-dev python-pip usbmount
        $ sudo pip install pexpect

[os]: https://tangentsoft.com/pidp8i/wiki?name=OS+Compatibility


<a id="unpacking"></a>
## Getting the Software onto Your Pi

If you're reading this file within an unpacked distribution of the
PiDP-8/I software, you should skip this section, because you have
already achieved its aim.

If you are reading this [online][this] and have not yet downloaded and
unpacked the software source code onto your Pi, this section will get
you going.

[this]: https://tangentsoft.com/pidp8i/doc/trunk/README.md


<a id="transferring"></a>
### Transferring the File to the Pi

The first step is to get the tarball (`*.tar.gz` file) or Zip file onto
the Pi. There are many options:

1.  **Copy the file to the SD card** you're using to boot the Pi.
    When inserted into a Mac or Windows PC, typically only the `/boot`
    partition mounts as a drive your OS can see.  (There's a much
    larger partition on the SD card, but most PCs cannot see it.)
    There should be enough free space left in this small partition to
    copy the file over.  When you boot the Pi up with that SD card,
    you will find the tarball or Zip file in `/boot`.

2.  **Pull the file down to the Pi** over the web, directly to the Pi:

        $ wget -O pidp8i.tar.gz https://goo.gl/JowPoC

    That will get you a file called `pidp8i.tar.gz` in the current
    working directory.

3.  **SCP the file over** to a running Pi from another machine.
    If your Pi has OpenSSH installed and running, you can use
    [WinSCP][wscp], [Cyberduck][cd], [FileZilla][fz] or another SCP
    or SFTP-compatible file transfer program to copy the file to the
    Pi over the network.

[cd]:   https://cyberduck.io/
[fz]:   https://filezilla-project.org/
[wscp]: https://winscp.net/eng/

4.  **Clone the Fossil repository** using the instructions in the
    [`HACKERS.md` file][hack]. (Best for experts or those who wish to
    become experts.)

[hack]: https://tangentsoft.com/pidp8i/doc/trunk/HACKERS.md

5.  **Switch to the binary OS installation images** available from the
    [top-level project page][cprj].  These are default installations of
    Raspbian Lite with the PiDP-8/I software already downloaded, built,
    and installed.  These images were produced in part using option #4
    above, so you can use Fossil to update your software to the current
    version at any time, as long as the Pi is connected to the Internet.


<a id="unpacking"></a>
### Unpacking the Software on Your Pi

Having transferred the distribution file onto your Pi, you can unpack it
with one of the two following commands.

If you grabbed the tarball:

    $ tar xvf /path/to/pidp8i-VERSION.tar.gz

If you grabbed the Zip file instead:

    $ unzip /path/to/pidp8i-VERSION.zip

The file name will vary somewhat, depending on when and how you
transferred the file.  After unpacking it, you will have a new
directory beginning with `pidp8i`.  `cd` into that directory, then
proceed with the [configuration](#configuring) steps below.


<a id="help"></a>
### If You Need More Help

If the above material is not sufficient to get you started, you might
want to look at [the documentation][rpfd] provided by the Raspberry
Pi Foundation.  In particular, we recommend their [Linux][rpfl] and
[Raspbian][rpfr] guides.  The book "[Make: Linux for Makers][lfm]"
by Aaron Newcomb is also supposed to be good.


[rpfd]: https://www.raspberrypi.org/documentation/
[rpfl]: https://www.raspberrypi.org/documentation/linux/
[rpfr]: https://www.raspberrypi.org/documentation/raspbian

[lfm]:  https://www.makershed.com/products/make-linux-for-makers


<a name="configuring"></a>
<a id="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

The `configure` step is generally needed only the first time you build
the software in a new directory.  You may want to add options after it,
as described [below](#options).
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.

After that initial configuration, the software normally auto-reconfigures
itself on updates using the same options you gave before, but occasionally
we make some change that prevents this from happening.  If you get a
build error after updating to a new version of the software, try saying:

    $ make reconfig

...and then continuing with the `make && sudo make install` steps before
reporting a build error.

If `make reconfig` also fails, you can try running the `configure`
script again manually.


<a id="running"></a>
### Running the Software

For the most part, this is covered in the documentation linked from the
[Learning More](/#learning) section of the project home page.

The only tricky bit is that if this is the first time you have
configured, built and installed the software as above on a given system,
you will have to log out and back in before commands like `pidp8i` will
be in your user's `PATH`.


<a name="options"></a>
<a id="options"></a>
### Configure Script Options

You can change a few things about the way the software is built and
You can change many things about the way the software is built and
installed by giving options to the `configure` script:


<a name="prefix"></a>
<a id="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:
which lets you override the default installation directory, `/opt/pidp8i`.
There are many good reasons to change where the software gets installed,
but the default is also a good one, so unless you know for a fact that
you want to change this default, leave it alone.

For example, you might prefer that the installer put the built software
under your home directory.  This will do that:

    $ ./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
You might think that installing to a directory your user has complete
control over would remove the need for installing via `sudo`, but that
is not the case, since the installation script needs root privileges to
process does other things that do require `root` access.
mark a few of the executables as having permission to run at high priority
levels, which improves the quality of the display, particularly with the
[incandescent lamp simulator][ils] feature enabled.


<a name="lowercase"></a>
<a id="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
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
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







-
+




















-
+















-
+




















+
+
+
+
+
+
+
+
+
+
+
-
+


-
-
+
+

-
+










-
-
+
+


-
+
+
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+


-
-
-
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+




-
+

+
+
+
+
+
+
-
-
+
+
+
+
+
+

+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+













+
+
+
+
+
+
+












-
+




-
-
-
-
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+











-
-
+
+




















-
+


-
-
+
+







    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>
<a id="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>
<a id="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>
<a id="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.


#### --disable-cc8-cross

Give this option if you do not want to build Ian Schofield's `cc8` C
cross-compiler on the host.

Note that this is different from `--disable-os8-cc8`, which disables the
*native OS/8* C compiler. They are two different C compilers: one runs
outside the SIMH PDP-8 simulator and the other runs inside the simulator
under OS/8.


<a name="disable-os8"></a>
<a id="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:
Several default components of the [OS/8 RK05 disk image](#os8di) 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-advent** — Leave out the [Adventure][advent] 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-chess** — Leave out John Comeau's
    [CHECKMO-II chess implementation][chess].

*   **--disable-os8-cc8** - Leave out Ian Schofield's native OS/8 CC8
    compiler normally installed to `RKA0:`
    compiler normally installed to `SYS:`.  This option is implicitly
    given if you give `--disable-os8-fortran-ii` because the output of
    the OS/8 CC8 compiler is a SABR file, and SABR is part of the
    FORTRAN II subsystem.

*   **--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-focal** - Do not install any version of FOCAL on the
    OS/8 system disk. This option sets `--disable-os8-uwfocal` and
    overrides `--enable-os8-focal69`, both discussed below.

*   **--disable-os8-fortran-ii** - Leaves out the FORTRAN II compiler,
    SABR, the linking loader (`LOADER`), the `LIBSET` tool, and the
    `*.RL` library files.

*   **--disable-os8-fortran-iv** - Leave the FORTRAN IV compiler out.

*   **--disable-os8-init** - Generate `RKB0:INIT.TX` but do not display
    it on OS/8 boot.  Rather than disable the default on-boot init
    message, you may want to edit `media/os8/init.tx.in` to taste
    and rebuild.

    (We still build the file when you give this option in case you
    later decide you want to enable the boot message, or you need to
    call up configuration information stored in `INIT.TX`.)

*   **--disable-os8-k12** - Leave out the Kermit-12 implementation
    normally installed to `RKA0:`

*   **--disable-os8-macrel** - Leave the MACREL v2 assembler and its
    associated FUTIL V8B tool out.

*   **--disable-os8-patches** - Do not apply any of the OS/8 V3D
    patches published by DEC.  See the [documentation][os8p] for this
    option for more information.

*   **--disable-os8-src** - Do not build the `os8v3d-src.rk05` disk
    image from the OS/8 source tapes.  This is not controlled by
    `--os8-minimal` because that only affects `os8v3d-bin.rk05`.

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

[advent]: http://www.rickmurphy.net/advent
[chess]:  https://chessprogramming.wikispaces.com/CHEKMO-II
[os8p]:   https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-patching.md


<a name="enable-os8"></a>
<a id="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:
There are a few file sets not normally installed to the [OS/8 RK05 disk
image](#os8di) 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-music** — The `*.MU` music scores and Rich Wilson's
    associated compiler (`MUSIC.PA`) and player overlay (`PLAYOV.PA`)
    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.
    commands][uvte] not normally recognized by TECO.

    This feature is disabled by default because the VTEDIT macro pack
    changes the way TECO operates, and many people want TECO to behave
    like *TECO*. VTEDIT was first created during the PDP-8's commercial
    lifetime, so enabling this option is not an anachronism, but TECO is
    much older and had a much more wide-reaching impact in history, so
    we choose to provide unvarnished TECO by default.
    This feature is currently disabled because it is not yet fully
    tested by the person in charge of the OS/8 disk building process.

    That having been said, people don't go to a ren fair and expect to
    experience the historical ubiquity of typhoid fever, so do not feel
    guilty if you choose to try this option.

[uvte]: https://tangentsoft.com/pidp8i/wiki?name=Using+VTEDIT

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

    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/trunk/doc/uwfocal-manual-supp.md#diffs


#### --os8-minimal

If you set this flag, it sets all `--enable-os8-*` flags to false and
all `--disable-os8-*` flags to true. If you explicitly give any of these
flags to the `configure` script, this flag overrides them.

This flag only affects the optional installs made after the `BUILD`
step: it does not remove optional features of OS/8 itself, such as its
BASIC interpreter.

Giving this option therefore gets you an empty OS/8 `DSK:` device and
nothing in `SYS:` beyond what was left after the OS/8 `BUILD` step.

There are a few exceptions:

1.  This option does not affect the `--lowercase` option because that
    affects only OS/8's command interpreter and OS/8's BASIC
    implementation, so we deem it to be orthogonal to the purpose of the
    `--os8-minimal` flag, which only affects the optional post-`BUILD`
    features. If you want a *truly* pristime OS/8 disk, you should
    therefore also give `--lowercase=none`.

2.  This option does not affect `--disable-os8-src`, because it only
    suppresses optional features in the "bin" media.  If you want a
    minimal OS/8 bin disk and no src disk, give that option as well.


#### --help

Run `./configure --help` for more information on your options here.


## <a id="os8di"></a>OS/8 Disk Images

For the first several years of the PiDP-8/I project, the OS/8 RK05 disk
image included with the PiDP-8/I software (called `os8.rk05`) was based
on an image of a real RK05 disk pack that someone allegedly found in a
salvaged PDP-8 system.  Parts of the image were corrupt, and not all of
the pieces of software on the disk worked properly with the other parts.
It was also a reflection of the time it was created and used out in the
world, which was not always what we would wish to use today.

In late 2017 [several of us][aut] created the `mkos8` tool, which takes
the `--*-os8-*` options documented above and generates the
`os8v3d-*.rk05` RK05 disk image files with your chosen configuration
options.

This set of disk images entirely replaces the old `os8.rk05` disk image,
in that all features of the old disk image are still available, though
not necessarily in the default configuration. In some cases, we have
disabled some features that were included in the stock `os8.rk05` disk
image, and in other cases we have changed the behavior of features.
Mostly, though, the new disk images are simply more functional than the
old ones.

If you wish to know the full details of how these disk images are
created, the best documentation so far is [the source code for the
`mkos8` script][mkos8] and the [documentation for `class simh`][cs].

The remainder of this section describes some aspects of these disk
images which are not clear from the descriptions of the `--*-os8-*`
configuration options above.

[aut]: https://tangentsoft.com/pidp8i/doc/trunk/AUTHORS.md


### Baseline

The baseline for the bootable OS/8 disk images comes from a set of
DECtapes distributed by Digital Equipment Corporation which are now
included with the PiDP-8/I software; see the [`media/os8/*.tu56`
files][os8mf]. From these files and your configuration options, the
`mkos8` script creates the baseline `os8v3d-bin.rk05` disk image.

The default build creates a complete OS/8 system including `BUILD`
support, FORTRAN IV, MACREL v2, and more.


### Subtractions

It turns out that it's pretty easy to run out of directory space on an
OS/8 RK05 disk due to a limitation in the number of files on an OS/8
filesystem.  For this reason, the archive of device drivers and TD8E
system are left off the system packs.  They can be found in [OS/8
Binary Distribution DECtape #2][bdt2].

If you do fancy work with `BUILD`, you may need to attach that DECtape
image and copy files in from it.


### Default Additions

The OS/8 RK05 disk image build process normally installs many software
and data file sets to the disk image.  See the [option descriptions
above](#disable-os8): the "disable" option set effectively lists those
packages that `mkos8` installs by default, and the following set of
["enable" options](#enable-os8) lists those left out by default.


### Console Enhancements

The default build [enhances the console](/wiki?name=Console+TTY+Setup),
adding support for lower case terminals where:

1.  The SIMH PDP-8 simulator and a few select parts of OS/8 are adjusted
    to cope with lowercase input to [varying degrees](#lowercase).

2.  Rubout/backspace handling is set to assume a video terminal rather
    than a teletype by default.


### Patches

After the baseline disk image is created, `mkos8` makes a copy of it as
`os8v3d-patched.rk05` and applies a [carefully selected set of official
DEC patches][os8p] to it unless you give the `--disable-os8-patches`
configuration option.  The IF=0 and IF=7 boot options boot from the
patched disk unless you give that option.


[bdt2]:  https://tangentsoft.com/pidp8i/file/media/os8/al-4712c-ba-os8-v3d-2.1978.tu56
[cl]:    https://tangentsoft.com/pidp8i/doc/trunk/ChangeLog.md
[cs]:    https://tangentsoft.com/pidp8i/doc/trunk/doc/class-simh.md
[mkos8]: https://tangentsoft.com/pidp8i/doc/trunk/libexec/mkos8
[os8mf]: https://tangentsoft.com/pidp8i/file/media/os8
[tlrm]:  https://tangentsoft.com/pidp8i/doc/trunk/README.md


<a name="overwrite-setup"></a>
<a id="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
    holds the OS/8 image the simulator boots from by default. These media
    image files are considered "precious" because you may have modified
    the OS configuration or saved personal files to the disk the OS
    boots from, which in turn modifies this media image file out in the
    host operating environment.

    There is an important exception here: when upgrading from v20170404
    to v201712xx or newer, the old `os8.rk05` disk image will be left
    untouched per the above, but because `os8v3d-*.rk05` does not exist
    yet, those will be copied alongside `os8.rk05`. However, the next
    item still holds, so that the simulator will continue to use
    `os8.rk05` because it will be booted by the preexisting scripts.

2.  **The PDP-8 simulator configuration files**, installed as
    `$prefix/share/boot/*.script`, which may similarly have local
    changes, and thus be precious to you.

Sometimes this "protect the precious" behavior isn't what you want.
(Gollum!) One common reason this may be the case is that you've damaged
your local configuration and want to start over. Another common case is
that the newer software you're installing contains changes that you want
to reflect into your local configuration.

You have several options here:

1.  If you just want to reflect upstream PDP-8 simulator configuration
1.  If you just want to reflect the prior PDP-8 simulator configuration
    file changes into your local versions, you can hand-edit the
    installed simulator configuration scripts to match the changes in
    the newly-generated `boot/*.script` files under the build directory.

2.  If the upstream change is to the binary PDP-8 media image files and
    you're unwilling to overwrite them wholesale, you'll have to mount
    both versions of the media image files under the PDP-8 simulator and
    copy the changes over by hand.
2.  If the change is to the binary PDP-8 media image files — including
    the [generated OS/8 disk images](#os8di) — and you're unwilling to
    overwrite your existing ones wholesale, you'll have to mount both
    versions of the media image files under the PDP-8 simulator and copy
    the changes over by hand.

3.  If your previously installed binary OS media images — e.g. the OS/8
    RK05 disk image that the simulator boots from by default — are
    precious but the simulator configuration scripts aren't precious,
    you can just copy the generated `boot/*.script` files from the build
    directory into the installation directory, `$prefix/share/boot`.
    (See the `--prefix` option above for the meaning of `$prefix`.)
3.  If your previously installed binary OS media images — e.g. the
    [OS/8 RK05 disk image](#os8di) that the simulator boots from by
    default — are precious but the simulator configuration scripts
    aren't precious, you can just copy the generated `boot/*.script`
    files from the build directory into the installation directory,
    `$prefix/share/boot`.  (See the `--prefix` option above for the
    meaning of `$prefix`.)

4.  If neither your previously installed simulator configuration files
    nor the binary media images are precious, you can force the
    installation script to overwrite them both with a `sudo make
    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
<a id="testing"></a>
## Testing Your PiDP-8/I Hardware

You can test your PiDP-8/I LED and switch functions with these commands:

    $ sudo systemctl stop pidp8i
    $ pidp8i-test

You may have to log out and back in before the second command will work,
since the installation script modifies your normal user's `PATH` the
first time you install onto a given system.

It is important to stop the PiDP-8/I simulator before running the test
program, since both programs need exclusive access to the LEDs and
switches on the front panel.  After you are done testing, you can start
the PiDP-8/I simulator back up with:

    $ sudo systemctl start pidp8i

See [its documentation][test] for more details.


<a name="using"></a>
<a id="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
For the most part, this software distribution works like the [old stable
2015.12.15 distribution][osd]. Its [documentation][oprj] therefore
describes this software too, for the most part.

The largest user-visible difference between the two software
distributions is that all of the shell commands affecting the software
were renamed to include `pidp8i` in their name:

1.  To start the simulator:
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
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







-
+









-
+
-


-
+


-
+





    what else you can do with the simulator command language, or read
    the [SIMH Users' Guide][sdoc].

5.  To shut the simulator down from the Raspbian command line:

        $ sudo systemctl stop pidp8i

There are [other major differences][mdif] between the upstream
There are [other major differences][mdif] between the old stable
distribution and this one.  See that linked wiki article for details.


## License

Copyright © 2016-2017 by Warren Young. This document is licensed under
the terms of [the SIMH license][sl].


[prj]:  https://tangentsoft.com/pidp8i/
[cprj]: https://tangentsoft.com/pidp8i/
[upst]: http://obsolescence.wixsite.com/obsolescence/pidp-8
[sm1]:  http://obsolescence.wixsite.com/obsolescence/2016-pidp-8-building-instructions
[sm2]:  https://groups.google.com/d/msg/pidp-8/-leCRMKqI1Q/Dy5RiELIFAAJ
[usd]:  http://obsolescence.wixsite.com/obsolescence/pidp-8-details
[osd]:  http://obsolescence.wixsite.com/obsolescence/pidp-8-details
[dt2]:  https://github.com/VentureKing/Deeper-Thought-2
[sdoc]: https://tangentsoft.com/pidp8i/uv/doc/simh/main.pdf
[prj]:  http://obsolescence.wixsite.com/obsolescence/pidp-8
[oprj]: http://obsolescence.wixsite.com/obsolescence/pidp-8
[test]: https://tangentsoft.com/pidp8i/doc/trunk/doc/pidp8i-test.md
[thro]: https://tangentsoft.com/pidp8i/doc/trunk/README-throttle.md
[mdif]: https://tangentsoft.com/pidp8i/wiki?name=Major+Differences
[sl]:   https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md
[ils]:  https://tangentsoft.com/pidp8i/wiki?name=Incandescent+Lamp+Simulator
Deleted RELEASE-PROCESS.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












































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
# PiDP-8/I Software Release Process

This documents the process for producing release versions of the
software.


## Update ChangeLog.md

Trawl the Fossil timeline for user-visible changes since the last
release, and write them up in user-focused form into the `ChangeLog.md`
file.  If a regular user of the software cannot see a given change, it
shouldn't go in the `ChangeLog.md`; let it be documented via the
timeline only.


## Update the Release Branch

Run `make release` to check the `ChangeLog.md` file changes in, tagging
that checkin with a release version tag of the form vYYYYMMDD and merge
those changes into the `release` branch.

It runs entirely automatically unless an error occurs, in which case it
stops immediately, so check its output for errors before continuing.


## Update the Home Page Links

The zip and tarball links on the front page produce files named after
the date of the release. Those dates need to be updated immediately
after tagging the release, since they point at the "release" tag applied
by the previous step, so they begin shipping the new release immediately
after tagging it.


## Produce the Normal Binary OS Image

Start with the latest version of [Raspbian Lite][os] on a multi-core
Raspberry Pi.

1.  If the version of the base OS has changed since the last binary OS
    image was created, download the new one.

    While the OS is downloading, zero the SD card you're going to use
    for this, so the prior contents don't affect this process.
    
    Blast the base OS image onto the cleaned SD card.

2.  Boot it up on a multi-core Pi.

    Log in, then retreive and initialize BOSI:

        $ wget https://tangentsoft.com/bosi
        $ chmod +x bosi
        $ exec sudo ./bosi init

    The `exec` bit is required so that the `bosi` invocation is run as
    root without any processes running as `pi` in case the `init` step
    sees that user `pi` hasn't been changed to `pidp8i` here: the
    `usermod` command we give to make that change will refuse to do what
    we ask if there are any running processes owned by user `pi`.

    It will either reboot the system after completing its tasks
    successfully or exit early, giving the reason it failed.

3.  Clone the software repo and build the softare:

        $ ./bosi build

    On reboot, say `top -H` to make sure the software is running and
    that the CPU percentages are reasonable for the platform.

    You may also want to check that it is running properly with a
    `pidp8i` command.  Is the configuration line printed by the
    simulator correct?  Does OS/8 run?  Are there any complaints from
    SIMH, such as about insufficient CPU power?

4.  Do final inside-the-image steps:

        $ ./bosi prepare

5.  Move the SD card to a USB reader plugged into the Pi, boot the Pi
    from its prior SD card, and shrink the OS image:

        $ wget https://tangentsoft.com/bosi
        $ chmod +x bosi
        $ ./bosi shrink

6.  Move the USB reader to the Mac,¹ then say:

        $ bosi image [nls]

    For the ILS images, you can give "ils" as a parameter or leave it
    blank.

7.  The prior step rewrote the SD card with the image file it created.
    Boot it up and make sure it still works.  If you're happy with it,
    give this command *on the desktop PC*.

        $ bosi finish [nls]

    As above, the parameter can be "ils" or left off for the ILS images.

[os]: https://www.raspberrypi.org/downloads/raspbian/


## Produce the "No Lamp Simulator" Binary OS Image

Do the same series of steps above on a single-core Raspberry Pi, except
that you give "nls" parameters to the `image` and `finish` steps.


## Publicizing

While the NLS image uploads — the ILS image was already sent in step 7
in the first pass through the list above — compose the announcement
message, and modify the front page to point to the new images.  You
might also need to update the approximate image sizes reported on that
page.  Post the announcement message and new front page once that second
upload completes.


----------------------

### Footnotes

1.  The image production steps could just as well be done on a Linux box
    or on a Windows box via Cygwin or WSL, but the commands in that
    final stage change due to OS differences.  Since this document
    exists primarily for use by the one who uses it, there is little
    point in having alternatives for other desktop OSes above.  Should
    someone else take over maintainership, they can translate the above
    commands for their own desktop PC.


### License

Copyright © 2016-2017 by Warren Young. This document is licensed under
the terms of [the SIMH license][sl].

[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md
Deleted asm/asr33-rim-loader.pal.
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

















































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/ asr33-rim-loader.pal
/
/ Load paper tapes in RIM format from a Teletype Model 33 ASR's paper
/ tape reader.  By contrast with the loader in hs-rim-loader.pal, this
/ is the low-speed paper tape RIM loader.
/
/ This is the RIM loader printed on the front panel of the PDP-8/I.  The
/ RIM loader in hs-rim-loader.pal (which is translated to boot/1.script
/ at build time) is similar to this one, but it works on the DEC high-
/ speed paper tape reader peripheral, which is where the PiDP-8/I's
/ automatic paper tape mounting feature attaches any tape images it
/ finds via USB.
/
/ Therefore, you cannot use this RIM loader if you want to use the
/ PiDP-8/I's automatic media attachment mechanism.  It is included
/ here mainly for documentation at the moment, but also in case
/ someone works out a way to build the simulator so that it can
/ conditionally load tapes from an emulated ASR-33.
/
/ Raw disassembly done from the octal values by Bernhard Baehr's PDP-8/E
/ Simulator.  Comments and labels by Warren Young.  Original copyright
/ by Digital Equipment Corporation: this program appeared in DEC manuals
/ and was printed on the front panel of every PDP-8/I.
/
/ SIMH: echo Installing the RIM loader for the ASR 33 paper tape reader...
/ SIMH: set df disabled

	*7755
	HLT		/ nonstandard: auto-halt on SIMH startup

	/ normal RIM loader entry point, 7756
NEXT,	KCC		/ clear PTR flag
RBYTE1,	KSF		/ loop until PTR is ready
	JMP RBYTE1
	KRB		/ read first byte in
	CLL RTL		/ shift it left by 2, clearing link as well
	RTL		/ and 2 more again
	SPA		/ if top bit of AC is set...
	JMP RBYTE1	/ ...AC contains the addr's value
	RTL		/ ...else rotate it another 2 positions
RBYTE2,	KSF		/ wait for next character
	JMP RBYTE2
	KRS		/ read second byte in
	SNL		/ if link is set...
	DCA I BYTE	/ ...it's the value's address
GOTVAL,	DCA BYTE	/ ...else it's the value at that addr
	JMP NEXT	/ go round again, getting next value from PTR
BYTE,	0
$
Deleted asm/hs-rim-loader.pal.
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















































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/ hs-rim-loader.pal
/
/ Load paper tapes in RIM format from the DEC high-speed PTR.
/
/ This routine differs from that printed in DEC's manuals in that it
/ starts with a HLT instruction so that when you [re]start SIMH with
/ IF=1, running this program, the processor auto-halts, giving the user
/ the opportunity to attach a paper tape via DF or ATTACH, then start
/ the processor at 7756 as normal.
/
/ The RIM loader code printed on the front panel of the PDP-8/I differs
/ because it is for the the paper tape reader built into a Teletype
/ Model 33 ASR.  See asr33-rim-loader.pal for that other implementation,
/ including more information about it.
/
/ Raw disassembly done from the octal values by Bernhard Baehr's PDP-8/E
/ Simulator.  Comments and labels by Warren Young.  Original copyright
/ by Digital Equipment Corporation: this program appeared in many DEC
/ manuals printed throughout the PDP-8 era variants of which were made
/ (and thus documented) from 1965 to 1979.
/
/ SIMH: echo Installing the RIM loader for the DEC high-speed tape reader...
/ SIMH: set df disabled
/ SIMH: set cpu noidle

	*7755
	HLT		/ nonstandard: auto-halt on SIMH startup

	/ normal RIM loader entry point, 7756
	RFC		/ clear PTR flag
RBYTE1,	RSF		/ loop until PTR is ready
	JMP RBYTE1
	RCC		/ read first byte in
	CLL RTL		/ shift it left by 2, clearing link as well
	RTL		/ and 2 more again
	SPA		/ if top bit of AC is set...
	JMP GOTVAL	/ ...AC contains the addr's value
	RTL		/ ...else rotate it another 2 positions
RBYTE2,	RSF		/ wait for next character
	JMP RBYTE2
	RCC		/ read second byte in
	SNL		/ if link is set...
	DCA I BYTE	/ ...it's the value's address
GOTVAL,	DCA BYTE	/ ...else it's the value at that addr
	JMP RBYTE1	/ go round again, getting next value from PTR
BYTE,	0
$
Changes to auto.def.
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
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







+

+
+
+
+
+
+
+


+



-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











+
+
+
+
+
+
+









-
-






-
+

-
+





-
-
-
+
+
+

-
-
-
-
+
-
-
-
+
+
-
-
+
-
-
-
+
-
-
+
-
-
-
-
+
+
+
+
-

-
-
-
-
-
-
-
-
-
-
-







# authorization from those authors.
########################################################################

define defaultprefix /opt/pidp8i

use cc
use cc-lib
use cc-shared

# Canonicalize some paths which may be relative and generate others from them
set abspfx [file-normalize [get-define prefix]]
define ABSPREFIX $abspfx
define BOOTDIR  "$abspfx/share/boot"
define MEDIADIR "$abspfx/share/media"

# Define command line options
options {
    alt-serial-mod    => "use GPIO drive scheme suitable for James L-W's serial mod method"
    cc8-cross=1       => "do not build the cc8 cross-compiler on the host"
    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       => "add the game of CHESS to built OS/8 RK05 image"
    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-advent=1      => "leave Adventure off the built OS/8 RK05 image"
    os8-ba=1          => "leave *.BA BASIC games and demos off the built OS/8 RK05 image"
    os8-cc8=1         => "leave the native OS/8 CC8 compiler off the built OS/8 RK05 image"
    os8-crt=1         => "suppress CRT-style rubout processing in the built OS/8 RK05 image"
    os8-chess=1       => "leave the CHECKMO-II game of chess off the built OS/8 RK05 image"
    os8-dcp=1         => "leave the DCP disassembler off the 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-fortran-ii=1  => "leave FORTRAN II off the built OS/8 RK05 image"
    os8-fortran-iv=1  => "leave FORTRAN IV off the built OS/8 RK05 image"
    os8-init=1        => "do not show the OS/8 INIT message in the built OS/8 RK05 image"
    os8-k12=1         => "leave 12-bit Kermit off the built OS/8 RK05 image"
    os8-macrel=1      => "leave MACREL assembler off the built OS/8 RK05 image"
    os8-minimal       => "set all --disable-os8-* options, giving minimal OS/8 bin disk"
    os8-music         => "add *.MU files to the built OS/8 RK05 image"
    os8-patches=1     => "do not apply DEC patches to the built OS/8 RK05 bin image"
    os8-src=1         => "do not build os8v3d-src.rk05 from OS/8 source tapes"
    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"
}

# Handle meta-options first.
if {![opt-bool os8-focal]} {
    if {[opt-bool os8-focal69]} {
        dict set $::autosetup(optset) "os8-focal69" 0
    }
    if {[opt-bool os8-uwfocal]} {
        dict set $::autosetup(optset) "os8-uwfocal" 0
    }
}
set os8min [opt-bool os8-minimal]
set os8src [opt-bool os8-src]

# Translate the --*-os8-* options to mkos8 command line option format
# and build the Python option map it uses to interpret them.  This
# avoids the need to define the options by hand in both places.
set mkos8_opts ""
set os "# GENERATED BY auto.def; DO NOT MODIFY.\n\nopts = {\n"
foreach opt [lsort [dict keys $::autosetup(optdefault)]] {
    # Skip meta-options handled above and non --*-os8-* options.
    if {$opt in {os8-focal os8-minimal os8-src}} { continue }
    if {[string first "os8-" $opt] != 0} { continue }

    # Add next line to Python output
    set od [dict get $::autosetup(optdefault) $opt]
    set oh [dict get $::autosetup(opthelpline) $opt]
    set obn [string replace $opt 0 3]
    append os "\t\"" $obn "\": \[ $od, \"" $oh "\" \],\n"

    # Fix grammar for upcoming msg-result calls
    if {[string first "do not " $oh] == 0} {
        set oh [string replace $oh 0 6]
    } elseif {[string first "do " $oh] == 0} {
        set oh [string replace $oh 0 2]
    }

    # Choose option to pass to mkos8 corresponding to this configure
    # script option.  Also tell user what we're going to do; grammar
    # is inverted because the option help text says what happens when
    # you give the opposite of the option's default argument.
    set ov [opt-bool $opt]
    if {$ov == $od} {
        # Don't append a mkos8 option; the default is correct.
        msg-result "Will not $oh."
    } elseif {!$os8min && $ov} {
        append mkos8_opts " --enable-" $obn
    } else {
        append mkos8_opts " --disable-" $obn
    }
}
append os "}\n"
if {![file exists "lib/mkos8"]} { file mkdir "lib/mkos8" }
write-if-changed "lib/mkos8/opts.py" $os

# Handle the OS/8 options not passed to mkos8 generically by above.
if {$os8src} {
    msg-result "Building os8v3d-src.rk05 from OS/8 source tapes."
    define OS8_SRC_RK05 bin/os8v3d-src.rk05
} else {
    msg-result "Will not build os8v3d-src.rk05 from OS/8 source tapes."
    define OS8_SRC_RK05 {}
}
if {$os8min || ![opt-bool os8-patches]} {
    msg-result "Will not patch OS/8 on the RK05 bin disk."
    define OS8_BOOT_DISK "os8v3d-bin.rk05"
    define MKOS8_BIN_PATCHES {}
} else {
    msg-result "Will apply OS/8 V3D patches to the RK05 bin disk."
    define OS8_BOOT_DISK "os8v3d-patched.rk05"
    define MKOS8_BIN_PATCHES patch
}

# React to remaining chosen command line options
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 cc8-cross]} {
    define CC8_CROSS bin/cc8
} else {
    define CC8_CROSS {}
    msg-result "Will not build the cc8 cross-compiler."
}

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

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 mk_os8_rk05s_options " --without-lcmod"
    append mkos8_opts " --disable-lcmod"
} elseif {$lv == "upper"} {
    append mk_os8_rk05s_options " --without-lcmod"
    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 mk_os8_rk05s_options " --without-advent"
if {[opt-bool os8-cc8] && ![opt-bool os8-fortran-ii]} {
    msg-result "Must clear --disable-os8-fortran-ii when CC8 is enabled."
    regsub -- {--disable-os8-fortran-ii } $mkos8_opts {}
}
if {![opt-bool os8-ba]} {
    msg-result "Will not add BASIC games and demos to the OS/8 RK05 disk image."
    append mk_os8_rk05s_options " --without-ba"
}

if {![opt-bool os8-cc8]} {
    msg-result "Will not add CC8 to the OS/8 RK05 disk image."
    append mk_os8_rk05s_options " --without-cc8"
# We've purposely held off exporting the mkos8 option set until now
# because some of the configuration options can affect the option set.
}
if {![opt-bool os8-chess]} {
define MKOS8_OPTS $mkos8_opts
    msg-result "Will not add game of CHESS to the OS/8 RK05 disk image."
    append mk_os8_rk05s_options " --without-chess"
}

if {![opt-bool os8-crt]} {
    msg-result "Will not add BASIC games and demos to the OS/8 RK05 disk image."
# Force a rebuild of the OS/8 media if the option set changed.
    append mk_os8_rk05s_options " --without-crt"
}
if {![opt-bool os8-k12]} {
    msg-result "Will not add Kermit-12 to the OS/8 RK05 disk image."
if {![file exists "obj"]} { file mkdir "obj" }
write-if-changed "obj/mkos8.opts" $mkos8_opts {
    file delete -force bin/os8v3d-bin.rk05
    msg-result "mkos8 options changed; will rebuild OS/8 disk images."
    append mk_os8_rk05s_options " --without-k12"
}
if {[opt-bool os8-music]} {
    msg-result "Will add music files to the OS/8 RK05 disk image."
    append mk_os8_rk05s_options " --with-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 mk_os8_rk05s_options " --with-vtedit"
}
define MK_OS8_RK05_OPTS $mk_os8_rk05s_options

# 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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
299
300
301
302
303
304
305





306
307
308
309
310
311
312







-
-
-
-
-







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







-
+












+
+
+
+
+
-
+
+











+
+



+
+
+
+
+
+
+
+
+
+
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
















+



+
+
+
+

+
+



-

-

# mask a real problem that needs to be diagnosed.
set tool "tools/version"
set cmd "$srcdir/$tool"
set status [catch {exec $cmd} version]
if {$status != 0} {
    # The most likely cause for tools/version to fail is that the repo
    # file has become disassociated from the local checkout directory.
    set sql "select value from vvar where name=\"repository\""
    set sql ".timeout 5000 ; select value from vvar where name=\"repository\""
    set cmd "fossil sql --no-repository $srcdir/.fslckout '$sql'"
    set status [catch {exec /bin/sh -c $cmd} path]
    if {$status != 0} {
        user-error "Fossil doesn't seem to work here.  Is it installed?\nCMD: $cmd"
    } elseif {[file exists $path]} {
        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.
catch {exec hostname} host
set user $::env(USER)
define BUILDUSER "$user@$host"
define BUILDTS [clock format [clock seconds] -format "%Y.%m.%d at %T %Z"]
# We need Python and pexpect installed here for mkos8 to run.

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

# Generate autodependency Makefile rule sets.
#
# It is important to list "src" last, because GNU make takes the first
# one that matches, and the wildcards in the generated rules for "src"
# match all "src/*" subdirs.
set status [catch {
    exec $srcdir/tools/mkadrules $srcdir src/cc8/cross src/d8tape src/palbart src/PDP8 src
} result]
if {$status == 0} {
    msg-result $result
} else {
    user-error "Failed to generate autodependency rules: $result!"
}

# 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 boot/run.script.in
make-template etc/pidp8i-init.in
make-template etc/sudoers.in
make-template examples/Makefile.in
make-template lib/pidp8i/__init__.py.in
make-template lib/pidp8i/dirs.py.in
make-template lib/pidp8i/ips.py.in
make-template media/os8/init.tx.in
make-template src/Makefile.in
make-template src/cc8/Makefile.in
make-template src/cc8/os8/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/mkos8.in
make-template tools/simh-update.in
exec chmod +x "$builddir/tools/mkos8"
exec chmod +x "$builddir/tools/simh-update"
Changes to autosetup/autosetup.
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
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







-
+

+
+

+

















-

+
+
+
+
+
+
+
+
+
+
+
+















-
-
+
+

















-
+


-
+








-
+

-
+




-
+

-
+



-
+

-
+






-
+

-
+








	set autosetup(argv) $argv
	set autosetup(cmdline) {}
	# options is a list of known options
	set autosetup(options) {}
	# optset is a dictionary of option values set by the user based on getopt
	set autosetup(optset) {}
	# optdefault is a dictionary of default values for options
	# optdefault is a dictionary of default values
	set autosetup(optdefault) {}
	# options-defaults is a dictionary of overrides for default values for options
	set autosetup(options-defaults) {}
	set autosetup(optionhelp) {}
	set autosetup(opthelpline) {}
	set autosetup(showhelp) 0

	# Parse options
	use getopt

	# At the is point we don't know what is a valid option
	# We simply parse anything that looks like an option
	set autosetup(getopt) [getopt argv]

	#"=Core Options:"
	options-add {
		help:=local  => "display help and options. Optionally specify a module name, such as --help=system"
		licence license => "display the autosetup license"
		version      => "display the version of autosetup"
		ref:=text manual:=text
		reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'"
		debug        => "display debugging output as autosetup runs"
		sysinstall:path  => "install standalone autosetup to the given directory (e.g.: /usr/local)"
		install:=.   => "install autosetup to the current or given directory"
	}
	if {$autosetup(installed)} {
		# hidden options so we can produce a nice error
		options-add {
			sysinstall:path
		}
	} else {
		options-add {
			sysinstall:path  => "install standalone autosetup to the given directory (e.g.: /usr/local)"
		}
	}
	options-add {
		force init:=help   => "create initial auto.def, etc.  Use --init=help for known types"
		# Undocumented options
		option-checking=1
		nopager
		quiet
		timing
		conf:
	}

	if {[opt-bool version]} {
		puts $autosetup(version)
		exit 0
	}

	# autosetup --conf=alternate-auto.def
	if {[opt-val conf] ne ""} {
		set autosetup(autodef) [lindex [opt-val conf] end]
	if {[opt-str conf o]} {
		set autosetup(autodef) $o
	}

	# Debugging output (set this early)
	incr autosetup(debug) [opt-bool debug]
	incr autosetup(force) [opt-bool force]
	incr autosetup(msg-quiet) [opt-bool quiet]
	incr autosetup(msg-timing) [opt-bool timing]

	# If the local module exists, source it now to allow for
	# project-local customisations
	if {[file exists $autosetup(libdir)/local.tcl]} {
		use local
	}

	# Now any auto-load modules
	autosetup_load_auto_modules

	if {[opt-val help] ne ""} {
	if {[opt-str help o]} {
		incr autosetup(showhelp)
		use help
		autosetup_help [lindex [opt-val help] end]
		autosetup_help $o
	}

	if {[opt-bool licence license]} {
		use help
		autosetup_show_license
		exit 0
	}

	if {[opt-val {manual ref reference}] ne ""} {
	if {[opt-str {manual ref reference} o]} {
		use help
		autosetup_reference [lindex [opt-val {manual ref reference}] end]
		autosetup_reference $o
	}

	# Allow combining --install and --init
	set earlyexit 0
	if {[opt-val install] ne ""} {
	if {[opt-str install o]} {
		use install
		autosetup_install [lindex [opt-val install] end]
		autosetup_install $o
		incr earlyexit
	}

	if {[opt-val init] ne ""} {
	if {[opt-str init o]} {
		use init
		autosetup_init [lindex [opt-val init] end]
		autosetup_init $o
		incr earlyexit
	}

	if {$earlyexit} {
		exit 0
	}
	if {[opt-val sysinstall] ne ""} {
	if {[opt-str sysinstall o]} {
		use install
		autosetup_install [lindex [opt-val sysinstall] end] 1
		autosetup_install $o 1
		exit 0
	}

	if {![file exists $autosetup(autodef)]} {
		# Check for invalid option first
		options {}
		user-error "No auto.def found in \"$autosetup(srcdir)\" (use [file tail $::autosetup(exe)] --init to create one)"
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
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







-

-
-

+
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







}

# @opt-val optionlist ?default=""?
#
# Returns a list containing all the values given for the non-boolean options in '$optionlist'.
# There will be one entry in the list for each option given by the user, including if the
# same option was used multiple times.
# If only a single value is required, use something like:
#
## lindex [opt-val $names] end
#
# If no options were set, '$default' is returned (exactly, not as a list).
#
# Note: For most use cases, 'opt-str' should be preferred.
#
proc opt-val {names {default ""}} {
	option-check-names {*}$names

	foreach opt $names {
		if {[dict exists $::autosetup(optset) $opt]} {
			lappend result {*}[dict get $::autosetup(optset) $opt]
		}
	}
	if {[info exists result]} {
		return $result
	}
	return $default
}

# @opt-str optionlist varname ?default?
#
# Sets '$varname' in the callers scope to the value for one of the given options.
#
# For the list of options given in '$optionlist', if any value is set for any option,
# the option value is taken to be the *last* value of the last option (in the order given).
#
# If no option was given, and a default was specified with 'options-defaults',
# that value is used.
#
# If no 'options-defaults' value was given and '$default' was given, it is used.
#
# If none of the above provided a value, no value is set.
#
# The return value depends on whether '$default' was specified.
# If it was, the option value is returned.
# If it was not, 1 is returns if a value was set, or 0 if not.
#
# Typical usage is as follows:
#
## if {[opt-str {myopt altname} o]} {
##     do something with $o
## }
#
# Or:
## define myname [opt-str {myopt altname} o "/usr/local"]
#
proc opt-str {names varname args} {
	global autosetup

	option-check-names {*}$names
	upvar $varname value

	if {[llength $args]} {
		# A default was given, so always return the string value of the option
		set default [lindex $args 0]
		set retopt 1
	} else {
		# No default, so return 0 or 1 to indicate if a value was found
		set retopt 0
	}

	foreach opt $names {
		if {[dict exists $::autosetup(optset) $opt]} {
			set result [lindex [dict get $::autosetup(optset) $opt] end]
		}
	}

	if {![info exists result]} {
		# No user-specified value. Has options-defaults been set?
		foreach opt $names {
			if {[dict exists $::autosetup(options-defaults) $opt]} {
				set result [dict get $autosetup(options-defaults) $opt]
			}
		}
	}

	if {[info exists result]} {
		set value $result
		if {$retopt} {
			return $value
		}
		return 1
	}

	if {$retopt} {
		set value $default
		return $value
	}

	return 0
}

proc option-check-names {args} {
	foreach o $args {
		if {$o ni $::autosetup(options)} {
			autosetup-error "Request for undeclared option --$o"
		}
	}
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
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







+

















+
+
+
+
+
+











+
-
+




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-












-
+












+
+
+







+







		set opt [lindex $opts $i]
		if {[string match =* $opt]} {
			# This is a special heading
			lappend autosetup(optionhelp) $opt ""
			set header {}
			continue
		}
		unset -nocomplain defaultvalue equal value

		#puts "i=$i, opt=$opt"
		regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value
		if {$name in $autosetup(options)} {
			autosetup-error "Option $name already specified"
		}

		#puts "$opt => $name $colon $equal $value"

		# Find the corresponding value in the user options
		# and set the default if necessary
		if {[string match "-*" $opt]} {
			# This is a documentation-only option, like "-C <dir>"
			set opthelp $opt
		} elseif {$colon eq ""} {
			# Boolean option
			lappend autosetup(options) $name

			# Check for override
			if {[dict exists $autosetup(options-defaults) $name]} {
				# A default was specified with options-defaults, so use it
				set value [dict get $autosetup(options-defaults) $name]
			}

			if {$value eq "1"} {
				set opthelp "--disable-$name"
			} else {
				set opthelp "--$name"
			}

			# Set the default
			if {$value eq ""} {
				set value 0
			}
			set defaultvalue $value
			dict set autosetup(optdefault) $name $value
			dict set autosetup(optdefault) $name $defaultvalue

			if {[dict exists $autosetup(getopt) $name]} {
				# The option was specified by the user. Look at the last value.
				lassign [lindex [dict get $autosetup(getopt) $name] end] type setvalue
				if {$type eq "str"} {
					# Can we convert the value to a boolean?
					if {$setvalue in {1 enabled yes}} {
						set setvalue 1
					} elseif {$setvalue in {0 disabled no}} {
						set setvalue 0
					} else {
						user-error "Boolean option $name given as --$name=$setvalue"
					}
				}
				dict set autosetup(optset) $name $setvalue
				#puts "Found boolean option --$name=$setvalue"
			}
		} else {
			# String option.
			lappend autosetup(options) $name

			if {$colon eq ":"} {
				# Was ":name=default" given?
				# If so, set $value to the display name and $defaultvalue to the default
				# (This is the preferred way to set a default value for a string option)
				if {[regexp {^([^=]+)=(.*)$} $value -> value defaultvalue]} {
					dict set autosetup(optdefault) $name $defaultvalue
				}
			}

			# Maybe override the default value
			if {[dict exists $autosetup(options-defaults) $name]} {
				# A default was specified with options-defaults, so use it
				set defaultvalue [dict get $autosetup(options-defaults) $name]
				dict set autosetup(optdefault) $name $defaultvalue
			} elseif {![info exists defaultvalue]} {
				# For backward compatiblity, if ":name" was given, use name as both
				# the display text and the default value, but only if the user
				# specified the option without the value
				set defaultvalue $value
			}

			if {$equal eq "="} {
				# String option with optional value
				set opthelp "--$name?=$value?"
			} else {
				# String option with required value
				set opthelp "--$name=$value"
			}
			dict set autosetup(optdefault) $name $value

			# Get the values specified by the user
			if {[dict exists $autosetup(getopt) $name]} {
				set listvalue {}

				foreach pair [dict get $autosetup(getopt) $name] {
					lassign $pair type setvalue
					if {$type eq "bool" && $setvalue} {
						if {$equal ne "="} {
							user-error "Option --$name requires a value"
						}
						# If given as a boolean, use the default value
						set setvalue $value
						set setvalue $defaultvalue
					}
					lappend listvalue $setvalue
				}

				#puts "Found string option --$name=$listvalue"
				dict set autosetup(optset) $name $listvalue
			}
		}

		# Now create the help for this option if appropriate
		if {[lindex $opts $i+1] eq "=>"} {
			set desc [lindex $opts $i+2]
			if {[info exists defaultvalue]} {
				set desc [string map [list @default@ $defaultvalue] $desc]
			}
			#string match \n* $desc
			if {$header ne ""} {
				lappend autosetup(optionhelp) $header ""
				set header ""
			}
			# A multi-line description
			lappend autosetup(optionhelp) $opthelp $desc
			dict set autosetup(opthelpline) $name $desc
			incr i 2
		}
	}
}

# @module-options optionlist
#
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
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







-
+

















+
+
+







		}
	}
}

# @options optionspec
#
# Specifies configuration-time options which may be selected by the user
# and checked with 'opt-val' and 'opt-bool'. '$optionspec' contains a series
# and checked with 'opt-str' and 'opt-bool'. '$optionspec' contains a series
# of options specifications separated by newlines, as follows:
#
# A boolean option is of the form:
#
## name[=0|1]  => "Description of this boolean option"
#
# The default is 'name=0', meaning that the option is disabled by default.
# If 'name=1' is used to make the option enabled by default, the description should reflect
# that with text like "Disable support for ...".
#
# An argument option (one which takes a parameter) is of the form:
#
## name:[=]value  => "Description of this option"
#
# If the 'name:value' form is used, the value must be provided with the option (as '--name=myvalue').
# If the 'name:=value' form is used, the value is optional and the given value is used as the default
# if it is not provided.
#
# The description may contain '@default@', in which case it will be replaced with the default
# value for the option (taking into account defaults specified with 'options-defaults'.
#
# Undocumented options are also supported by omitting the '=> description'.
# These options are not displayed with '--help' and can be useful for internal options or as aliases.
#
# For example, '--disable-lfs' is an alias for '--disable=largefile':
#
## lfs=1 largefile=1 => "Disable large file support"
519
520
521
522
523
524
525











526
527
528
529
530
531
532
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







+
+
+
+
+
+
+
+
+
+
+







		foreach o [dict keys $::autosetup(getopt)] {
			if {$o ni $::autosetup(options)} {
				user-error "Unknown option --$o"
			}
		}
	}
}

# @options-defaults dictionary
#
# Specifies a dictionary of options and a new default value for each of those options.
# Use before any 'use' statements in 'auto.def' to change the defaults for
# subsequently included modules.
proc options-defaults {dict} {
	foreach {n v} $dict {
		dict set ::autosetup(options-defaults) $n $v
	}
}

proc config_guess {} {
	if {[file-isexec $::autosetup(dir)/autosetup-config.guess]} {
		if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.guess} alias]} {
			user-error $alias
		}
		return $alias
571
572
573
574
575
576
577
578

579

580






581
582
583
584
585
586
587
703
704
705
706
707
708
709

710
711
712

713
714
715
716
717
718
719
720
721
722
723
724
725







-
+

+
-
+
+
+
+
+
+







# If the variable is not defined or empty, it is set to '$value'.
# Otherwise the value is appended, separated by a space.
# Any extra values are similarly appended.
# If any value is already contained in the variable (as a substring) it is omitted.
#
proc define-append {name args} {
	if {[get-define $name ""] ne ""} {
		# Make a token attempt to avoid duplicates
		# Avoid duplicates
		foreach arg $args {
			set found 0
			if {[string first $arg $::define($name)] == -1} {
			foreach str [split $::define($name) " "] {
				if {$str eq $arg} {
					incr found
				}
			}
			if {!$found} {
				append ::define($name) " " $arg
			}
		}
	} else {
		set ::define($name) [join $args]
	}
	#dputs "$name += [join $args] => $::define($name)"
1550
1551
1552
1553
1554
1555
1556

1557
1558
1559
1560
1561









1562
1563
1564
1565
1566
1567
1568
1688
1689
1690
1691
1692
1693
1694
1695





1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711







+
-
-
-
-
-
+
+
+
+
+
+
+
+
+







# Module which can install autosetup

# autosetup(installed)=1 means that autosetup is not running from source
# autosetup(sysinstall)=1 means that autosetup is running from a sysinstall verion
# shared=1 means that we are trying to do a sysinstall. This is only possible from the development source.

proc autosetup_install {dir {shared 0}} {
	global autosetup
	if {$shared && $::autosetup(installed)} {
		user-error "Can only --sysinstall from development sources"
	}

	if {$::autosetup(sysinstall)} {
	if {$shared} {
		if {$autosetup(installed) || $autosetup(sysinstall)} {
			user-error "Can only --sysinstall from development sources"
		}
	} elseif {$autosetup(installed) && !$autosetup(sysinstall)} {
		user-error "Can't --install from project install"
	}

	if {$autosetup(sysinstall)} {
		# This is the sysinstall version, so install just uses references
		cd $dir

		puts "[autosetup_version] creating configure to use system-installed autosetup"
		autosetup_create_configure 1
		puts "Creating autosetup/README.autosetup"
		file mkdir autosetup
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
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







-
+











-
+
















-
-
+
+







-
+







		file mkdir $targetdir

		set f [open $target w]

		set publicmodules {}

		# First the main script, but only up until "CUT HERE"
		set in [open $::autosetup(dir)/autosetup]
		set in [open $autosetup(dir)/autosetup]
		while {[gets $in buf] >= 0} {
			if {$buf ne "##-- CUT HERE --##"} {
				puts $f $buf
				continue
			}

			# Insert the static modules here
			# i.e. those which don't contain @synopsis:
			# All modules are inserted if $shared is set
			puts $f "set autosetup(installed) 1"
			puts $f "set autosetup(sysinstall) $shared"
			foreach file [lsort [glob $::autosetup(libdir)/*.{tcl,auto}]] {
			foreach file [lsort [glob $autosetup(libdir)/*.{tcl,auto}]] {
				set modname [file tail $file]
				set ext [file ext $modname]
				set buf [readfile $file]
				if {!$shared} {
					if {$ext eq ".auto" || [string match "*\n# @synopsis:*" $buf]} {
						lappend publicmodules $file
						continue
					}
				}
				dputs "install: importing lib/[file tail $file]"
				puts $f "# ----- @module $modname -----"
				puts $f "\nset modsource($modname) \{"
				puts $f $buf
				puts $f "\}\n"
			}
			if {$shared} {
				foreach {srcname destname} [list $::autosetup(libdir)/README.autosetup-lib README.autosetup \
						$::autosetup(srcdir)/LICENSE LICENSE] {
				foreach {srcname destname} [list $autosetup(libdir)/README.autosetup-lib README.autosetup \
						$autosetup(srcdir)/LICENSE LICENSE] {
					dputs "install: importing $srcname as $destname"
					puts $f "\nset modsource($destname) \\\n[list [readfile $srcname]\n]\n"
				}
			}
		}
		close $in
		close $f
		exec chmod 755 $target
		catch {exec chmod 755 $target}

		set installfiles {autosetup-config.guess autosetup-config.sub autosetup-test-tclsh}
		set removefiles {}

		if {!$shared} {
			autosetup_install_readme $targetdir/README.autosetup 0

1653
1654
1655
1656
1657
1658
1659
1660

1661
1662
1663
1664
1665
1666
1667
1668
1796
1797
1798
1799
1800
1801
1802

1803

1804
1805
1806
1807
1808
1809
1810







-
+
-







		foreach fileinfo $installfiles {
			if {[llength $fileinfo] == 2} {
				lassign $fileinfo source dest
			} else {
				lassign $fileinfo source
				set dest $source
			}
			autosetup_install_file $::autosetup(dir)/$source $targetdir/$dest
			autosetup_install_file $autosetup(dir)/$source $targetdir/$dest
			exec chmod 755 $targetdir/$dest
		}

		# Remove obsolete files
		foreach file $removefiles {
			if {[file exists $targetdir/$file]} {
				file delete $targetdir/$file
			}
1724
1725
1726
1727
1728
1729
1730




1731
1732
1733
1734
1735
1736
1737
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883







+
+
+
+








proc autosetup_install_file {source target} {
	dputs "install: $source => $target"
	if {![file exists $source]} {
		error "Missing installation file '$source'"
	}
	writefile $target [readfile $source]\n
	# If possible, copy the file mode
	file stat $source stat
	set mode [format %o [expr {$stat(mode) & 0x1ff}]]
	catch {exec chmod $mode $target}
}

proc autosetup_install_readme {target sysinstall} {
	set readme "README.autosetup created by [autosetup_version]\n\n"
	if {$sysinstall} {
		append readme \
{This is the autosetup directory for a system install of autosetup.
Changes to autosetup/cc.tcl.
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
26
27
28
29
30
31
32





33
34
35
36
37
38
39







-
-
-
-
-







## CC_FOR_BUILD
## LD

use system

module-options {}

# Note that the return code is not meaningful
proc cc-check-something {name code} {
	uplevel 1 $code
}

# Checks for the existence of the given function by linking
#
proc cctest_function {function} {
	cctest -link 1 -declare "extern void $function\(void);" -code "$function\();"
}

# Checks for the existence of the given type by compiling
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160







-
+







	cc-check-some-feature $args {
		cctest_type $each
	}
}

# @cc-check-defines define ...
#
# Checks that the given preprocessor symbol is defined.
# Checks that the given preprocessor symbols are defined.
proc cc-check-defines {args} {
	cc-check-some-feature $args {
		cctest_define $each
	}
}

# @cc-check-decls name ...
238
239
240
241
242
243
244

245

246
247
248
249
250
251
252
253
254
233
234
235
236
237
238
239
240

241


242
243
244
245
246
247
248







+
-
+
-
-







						incr found
						break
					}
				}
			}
		}
	}
	define-feature $function $found
	if {$found} {
	if {!$found} {
		define [feature-define-name $function]
	} else {
		msg-result "no"
	}
	return $found
}

# @cc-check-tools tool ...
#
Changes to autosetup/pkg-config.tcl.
45
46
47
48
49
50
51
52
53


54
55
56
57
58
59
60
45
46
47
48
49
50
51


52
53
54
55
56
57
58
59
60







-
-
+
+







		}
	} else {
		msg-result $version
		define PKG_CONFIG_VERSION $version

		set found 1

		if {[opt-val sysroot] ne ""} {
			define SYSROOT [file-normalize [lindex [opt-val sysroot] end]]
		if {[opt-str sysroot o]} {
			define SYSROOT [file-normalize $o]
			msg-result "Using specified sysroot [get-define SYSROOT]"
		} elseif {[get-define build] ne [get-define host]} {
			if {[catch {exec-with-stderr [get-define CC] -print-sysroot} result errinfo] == 0} {
				# Use the compiler sysroot, if there is one
				define SYSROOT $result
				msg-result "Found compiler sysroot $result"
			} else {
Changes to autosetup/system.tcl.
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
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







+
+
-
+
+







-
+
















+







## infodir
## mandir
## includedir
#
# If '--prefix' is not supplied, it defaults to '/usr/local' unless 'defaultprefix' is defined *before*
# including the 'system' module.

if {[is-defined defaultprefix]} {
	user-notice "Note: defaultprefix is deprecated. Use options-defaults to set default options"
set defaultprefix [get-define defaultprefix /usr/local]
	options-defaults [list prefix [get-define defaultprefix]]
}

module-options [subst -noc -nob {
	host:host-alias =>		{a complete or partial cpu-vendor-opsys for the system where
							the application will run (defaults to the same value as --build)}
	build:build-alias =>	{a complete or partial cpu-vendor-opsys for the system
							where the application will be built (defaults to the
							result of running config.guess)}
	prefix:dir =>			{the target directory for the build (defaults to '$defaultprefix')}
	prefix:dir=/usr/local => {the target directory for the build (default: '@default@')}

	# These (hidden) options are supported for autoconf/automake compatibility
	exec-prefix:
	bindir:
	sbindir:
	includedir:
	mandir:
	infodir:
	libexecdir:
	datadir:
	libdir:
	sysconfdir:
	sharedstatedir:
	localstatedir:
	maintainer-mode=0
	dependency-tracking=0
	silent-rules=0
}]

# @check-feature name { script }
#
# defines feature '$name' to the return value of '$script',
# which should be 1 if found or 0 if not found.
#
126
127
128
129
130
131
132
133


134
135
136
137
138
139
140
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145







-
+
+







		writefile $file $buf\n
		uplevel 1 $script
	}
}

# @make-template template ?outfile?
#
# Reads the input file '<srcdir>/$template' and writes the output file '$outfile'.
# Reads the input file '<srcdir>/$template' and writes the output file '$outfile'
# (unless unchanged).
# If '$outfile' is blank/omitted, '$template' should end with '.in' which
# is removed to create the output file name.
#
# Each pattern of the form '@define@' is replaced with the corresponding
# "define", if it exists, or left unchanged if not.
# 
# The special value '@srcdir@' is substituted with the relative
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
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







-
+
-
-
+
+



-
+







-
+







					set cond [expr {!$cond}]
				}
			}
			continue
		}
		lappend result $line
	}
	writefile $out [string map $mapping [join $result \n]]\n
	write-if-changed $out [string map $mapping [join $result \n]] {

	msg-result "Created [relative-path $out] from [relative-path $template]"
		msg-result "Created [relative-path $out] from [relative-path $template]"
	}
}

# build/host tuples and cross-compilation prefix
set build [lindex [opt-val build] end]
opt-str build build ""
define build_alias $build
if {$build eq ""} {
	define build [config_guess]
} else {
	define build [config_sub $build]
}

set host [lindex [opt-val host] end]
opt-str host host ""
define host_alias $host
if {$host eq ""} {
	define host [get-define build]
	set cross ""
} else {
	define host [config_sub $host]
	set cross $host-
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
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







-
+











-
-
+






-
+



-

-




-
+

+
+
+
+
-
+
+
+
+
+

+
+
+
+
+







		user-error "Invalid canonical $type: $v"
	}
	define ${type}_cpu $cpu
	define ${type}_vendor $vendor
	define ${type}_os $os
}

set prefix [lindex [opt-val prefix $defaultprefix] end]
opt-str prefix prefix /usr/local

# These are for compatibility with autoconf
define target [get-define host]
define prefix $prefix
define builddir $autosetup(builddir)
define srcdir $autosetup(srcdir)
define top_srcdir $autosetup(srcdir)
define abs_top_srcdir [file-normalize $autosetup(srcdir)]
define abs_top_builddir [file-normalize $autosetup(builddir)]

# autoconf supports all of these
set exec_prefix [lindex [opt-val exec-prefix $prefix] end]
define exec_prefix $exec_prefix
define exec_prefix [opt-str exec-prefix exec_prefix $prefix]
foreach {name defpath} {
	bindir /bin
	sbindir /sbin
	libexecdir /libexec
	libdir /lib
} {
	define $name [lindex [opt-val $name $exec_prefix$defpath] end]
	define $name [opt-str $name o $exec_prefix$defpath]
}
foreach {name defpath} {
	datadir /share
	sysconfdir /etc
	sharedstatedir /com
	localstatedir /var
	infodir /share/info
	mandir /share/man
	includedir /include
} {
	define $name [lindex [opt-val $name $prefix$defpath] end]
	define $name [opt-str $name o $prefix$defpath]
}
if {$prefix ne {/usr}} {
	opt-str sysconfdir sysconfdir $prefix/etc
} else {
	opt-str sysconfdir sysconfdir /etc

}
define sysconfdir $sysconfdir

define localstatedir [opt-str localstatedir o /var]

define SHELL [get-env SHELL [find-an-executable sh bash ksh]]

# These could be used to generate Makefiles following some automake conventions
define AM_SILENT_RULES [opt-bool silent-rules]
define AM_MAINTAINER_MODE [opt-bool maintainer-mode]
define AM_DEPENDENCY_TRACKING [opt-bool dependency-tracking]

# Windows vs. non-Windows
switch -glob -- [get-define host] {
	*-*-ming* - *-*-cygwin - *-*-msys {
		define-feature windows
		define EXEEXT .exe
	}
Changes to autosetup/tmake.auto.
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
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







-
+




+
+






-
-
-
-
-
-
-
-
-
+

-
-
-
-
-
-
-
-
+


+
+
+
+
-
-
+
+
-
-






}

cc-check-tools ar ranlib

set objdir [get-env BUILDDIR objdir]

make-config-header $objdir/include/autoconf.h
make-tmake-settings $objdir/settings.conf {[A-Z]*}
make-tmake-settings $objdir/settings.conf {[A-Z]*} *dir lib_*
}

	autosetup_check_create project.spec \
{# Initial project.spec created by 'autosetup --init=tmake'

tmake-require-version 0.7.3

# vim:set syntax=tcl:
define? DESTDIR _install

# XXX If configure creates additional/different files than include/autoconf.h
#     that should be reflected here

# We use [set AUTOREMAKE] here to avoid rebuilding settings.conf
# if the AUTOREMAKE command changes
Depends {settings.conf include/autoconf.h} auto.def -msg {note Configuring...} -do {
	run [set AUTOREMAKE] >$build/config.out
} -onerror {puts [readfile $build/config.out]} -fatal
Clean config.out
DistClean --source config.log
DistClean settings.conf include/autoconf.h
Autosetup include/autoconf.h

# If not configured, configure with default options
# Note that it is expected that configure will normally be run
# separately. This is just a convenience for a host build
define? AUTOREMAKE configure TOPBUILDDIR=$TOPBUILDDIR --conf=auto.def

Load settings.conf

# e.g. for up autoconf.h
# e.g. for autoconf.h
IncludePaths include

ifconfig !CONFIGURED {
	# Not configured, so don't process subdirs
	AutoSubDirs off
	# And don't process this file any further
ifconfig CONFIGURED

	ifconfig false
}
# Hmmm, but should we turn off AutoSubDirs?
#AutoSubDirs off
}

	if {![file exists build.spec]} {
		puts "Note: I don't see build.spec. Try running: tmake --genie"
	}
}
Added bin/.agignore.


1
2
+
+
*.pt
*.rk05
Added bin/.ignore.

1
+
.agignore
Added bin/cc8-to-os8.




































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/env perl
########################################################################
# cc8-to-os8 - Filter C code meant for the CC8 cross-compiler so that it
#   will work with the OS/8 version of CC8.
#
#   This is not meant to fix up 100% of the differences in the C
#   compilers, just the ones which prevent src/cc8/examples/*.c
#   from building under OS/8 CC8 as-is.
#
#   You can run it like a filter: 
#
#       $ cc8-to-os8 < foo.c | txt2ptp > foo.pt
#
#   Or you can just give it one or more file name(s), with output going
#   to stdout, which gives this way to hack around the lack of #include:
#
#       $ cc8-to-os8 myheader.h foo.c > OS8FOO.C
#
# Copyright © 2017 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.
########################################################################

use strict;
use warnings;

my $nblines = 0;        # number of non-blank lines written out

while (<>) {
    if (m{^[a-zA-Z_][a-zA-Z0-9_]*\s*\(}) {
        # Looks like a function definition without a type; assume int.
        print "int $_";
        ++$nblines;
    }
    elsif (!m{^\#include } and length and $nblines) {
        # Pass it through unchanged.
        print;
    }
    else {
        # Unlike the CC8 cross-compiler, the OS/8 version of CC8 doesn't
        # process #includes, or in fact any C preprocessor directives,
        # not even #asm.  Strip just the #includes in the hope that it's
        # init.h or libc.h, the contents of which are hard-coded into
        # the OS/8 CC8 compiler.
    }
}
Added bin/teco-pi-demo.



















































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/python
# -*- coding: utf-8 -*-
########################################################################
# teco-pi-demo - Starts the simulator with the OS/8, sends one of the
#   famous TECO "calculate pi" program to it, and starts it running at
#   a very slow rate of speed to act as a blinkenlights demo.
#
# 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.
########################################################################

# 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')
sys.path.insert (0, os.getcwd () + '/lib')

# Other core modules we need
from datetime import datetime
import time

# Our local modules
from pidp8i import *
from simh   import *


#### main ##############################################################

def main ():
  # Check for command line flags
  benchmark = len (sys.argv) > 1 and sys.argv[1] == '-b'

  # Create the SIMH child instance and tell it where to send log output
  try:
    s = simh (dirs.build)
  except (RuntimeError) as e:
    print "Could not start simulator: " + e.message + '!'
    exit (1)
  s.set_logfile (os.fdopen (sys.stdout.fileno (), 'w', 0))

  # Find and boot the built OS/8 bin disk
  rk = os.path.join (dirs.os8mo, 'os8v3d-bin.rk05')
  if not os.path.isfile (rk):
    print "Could not find " + rk + "; OS/8 media not yet built?"
    exit (1)
  print "Booting " + rk + "..."
  s.send_cmd ("att rk0 " + rk)
  s.send_cmd ("boot rk0")

  # Start TECO8 in the simulator under OS/8
  s.os8_send_cmd ('\\.', "R TECO")

  # The macro comes from http://www.iwriteiam.nl/HaPi_TECO_macro.html
  #
  # The 248 preceding "UN" in the first line of the macro is the number
  # of digits of pi to calculate.  That value was reached by experiment
  # as the largest value that runs without crashing TECO with a
  #
  #    ?MEM  STORAGE CAPACITY EXCEEDED
  #
  # error.  You can see that by increasing the value below, commenting
  # out the throttle setting below, and running the demo.  On a Pi 3, it
  # should take a bit over an hour to complete, if it doesn't error out.
  #
  # With the simulator throttled, generating 248 digits takes 17 years!
  #
  # That is based on generating 1 digit every ~16 seconds on a Pi 3 when
  # running unthrottled, roughly 8 MIPS.  When throttled to 59 IPS — or
  # 17ms per instruction, as below — you multiply the seconds by the
  # factor 8 MIPS / 59 IPS = ~136000, giving about 2.2 million seconds
  # per digit.  Multiplying that by 248 gives ~17 years.
  macro = [
    'GZ0J\UNQN"E 248UN \' BUH BUV HK',
    'QN< J BUQ QN*10/3UI',
    'QI< \+2*10+(QQ*QI)UA B L K QI*2-1UJ QA/QJUQ',
    'QA-(QQ*QJ)-2\ 10@I// -1%I >',
    'QQ/10UT QH+QT+48UW QW-58"E 48UW %V \' QV"N QV^T \' QWUV QQ-(QT*10)UH >',
    'QV^T @^A/',
    '/HKEX',
  ]

  # First and last lines are handled specially, so slice them off.
  first = macro.pop (0)
  last  = macro.pop ()

  # Send the first line of the macro; implicitly awaits 1st TECO prompt
  s.os8_send_cmd ('\*', first)

  # Blindly send core lines of the macro; TECO gives no prompts for 'em.
  for line in macro: 
    s.os8_send_line (line)

  # Send last line of macro sans CR, followed by two Esc characters to
  # start it running.
  s.os8_send_str (last)     # not os8_send_line!
  s.os8_send_ctrl ('[')
  s.os8_send_ctrl ('[')

  if benchmark:
    # Run demo long enough to get a good sense of the simulator's
    # execution rate while unthrottled on this host hardware.  If
    # you don't run it long enough, the IPS value is untrustworthy.
    try:
      s.spin (10)
    except pexpect.TIMEOUT:
      # Find out how many IPS was executing
      s.os8_send_ctrl ('e')
      s.send_cmd ('show clocks')
      line = s.read_tail ('Execution Rate:')
      curr_ips = int (line.strip().replace(',', '').split(' ')[0])
      pf = open ('lib/pidp8i/ips.py', 'a')
      pf.write ('current = ' + str (curr_ips) + '    # ' + \
            str (datetime.today ()) + '\n')
      pf.close ()
      s.send_cmd ('quit')
      pdp_ratio = float (curr_ips) / ips.pdp8i
      rpi_ratio = float (curr_ips) / ips.raspberry_pi_b_plus
      print "\nYour system is " + format (rpi_ratio, '.1f') + \
          " times faster than a Raspberry Pi Model B+"
      print "or " + format (pdp_ratio, '.1f') + \
          " times faster than a PDP-8/I.\n"
  else:
    # Normal mode.  Pop out to SIMH and throttle it down to a rate
    # suitable for a blinkenlights demo.  1/17 means SIMH runs one
    # instruction then waits for 17ms, yielding ~59 IPS.
    time.sleep (0.02)   # FIXME: simulator chokes on 'cont' without this
    s.os8_send_ctrl ('e')
    s.send_cmd ('set throttle 1/17')

    # You can't hit Ctrl-E while running this script in the foreground
    # since pexpect takes over stdio.  Therefore, if you want to be able
    # to send commands to the simulator while the demo is running,
    # uncomment the line below, which will let you send commands to the
    # simulator via telnet.  From another terminal or SSH session:
    #
    #     $ telnet localhost 3141
    #
    # or from a remote machine:
    #
    #     $ telnet 192.168.1.2 3141
    #
    # It's disabled by default because SIMH can't be made to listen only
    # on localhost, so doing this may be a security risk.  SIMH disables
    # obviously-unsafe commands like ! on the remote console, but it is
    # possible some mischief may be possible via this path anyway.  It
    # could be used to exfiltrate a sensitive file via ATTACH, for one
    # thing.  For another, it's a potential DoS vector.
    #s.send_cmd ('set remote telnet=3141')

    # Let it run.  Never exits.
    s.send_cmd ('cont')
    s.spin ()


if __name__ == "__main__":
    main()
Added bin/txt2os8.






















































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/python
# -*- coding: utf-8 -*-
########################################################################
# Create a tu56 or rk05 image and fill it with ASCII files, i.e.  source code.
#
# It is intended to be be called manually when we have a POSIX
# directory full of ASCII files we want to bulk-copy into SIMH.
#
# The argument is taken both as the name of the image to create
# and the list of files to copy in.
#
# For now, it takes all input and produces all output in the
# current working directory.
#
# IMPORTANT:  Currently all input files are mindlessly passed through
# txt2ptp which transforms POSIX ASCII files to OS/8 ASCII files.
# It WILL mutilate non-ASCII files.
#
# This program is based on cc8-tu56-update.
#
# Copyright © 2017 by Warren Young and Bill Cattey
#
# 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
import argparse

sys.path.insert (0, os.path.dirname (__file__) + '/../lib')
sys.path.insert (0, os.getcwd () + '/lib')

# Our local modules
from pidp8i import *
from simh   import *

# Other global Python modules
import glob
import subprocess


#### GLOBALS AND CONSTANTS #############################################

progmsg = True


#### main ##############################################################

def main ():
  global progmsg

  # Set up the arg parser and use it to parse the command line.
  parser = argparse.ArgumentParser()
  parser.add_argument("name",
                      help="Create an OS/8 image from a list of ASCII files.")
  group = parser.add_mutually_exclusive_group(required=True)
  group.add_argument("--tu56", "-t", action="store_true")
  group.add_argument("--rk05a", "-ra", action="store_true")
  group.add_argument("--rk05b", "-rb", action="store_true")

  args = parser.parse_args()
  
  print "Filename: " + args.name
  if args.tu56:
    sdev = "dt0"
    os8dev = "DTA0:"
    imagename = args.name + ".tu56"
    stat_str = "DECtape"

  if args.rk05a:
    sdev = "rk1"
    os8dev = "RKA1:"
    imagename = args.name + ".rk05"
    stat_str = "partition A of"
    
  if args.rk05b:
    sdev = "rk1"
    os8dev = "RKB1:"
    imagename = args.name + ".rk05"
    stat_str = "partition B of"
  
  listname = args.name + ".list"

  # Create the SIMH child instance and tell it where to send log output
  try:
    s = simh (dirs.build, True)
  except (RuntimeError) as e:
    print "Could not start simulator: " + e.message + '!'
    exit (1)
  s.set_logfile (os.fdopen (sys.stdout.fileno (), 'w', 0))

  # Attach a clean version of the image to the simulator
  if os.path.exists (imagename):
    print "Overwriting old " + stat_str + " image " + imagename

  s.send_cmd ("att " + sdev + " " + imagename)

  # Find and boot the bootable OS/8 disk.  Use the "patched" version
  # because that is what "make run" uses; we use that command to
  # inspect this script's work.
  rk = os.path.join (dirs.os8mo, 'os8v3d-patched.rk05')
  if not os.path.isfile (rk):
    print "Could not find " + rk + "; OS/8 media not yet built?"
    exit (1)
  print "Booting " + rk + "..."
  s.send_cmd ("att rk0 " + rk)
  s.send_cmd ("boot rk0")

  s.os8_send_cmd ('\\.', "ZERO " + os8dev)

  manifest = open (listname, "r")

  for line in manifest:
    src = line.strip()
    if src == "": continue
    if src[0] == '#': continue      # Allow commenting out files

    dest = src.upper ()
    s.os8_send_file (src, os8dev + dest)

  # Exit simulator nicely so that image detaches cleanly
  s.back_to_cmd ('\\.')
  s.send_cmd ("det " + sdev)
  s.send_cmd ('quit')


if __name__ == "__main__": main()
Changes to boot/0.script.in.
37
38
39
40
41
42
43
44

45
37
38
39
40
41
42
43

44
45







-
+

@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
att rk0 @MEDIADIR@/os8/@OS8_BOOT_DISK@
boot rk0
Added boot/ratio.script.























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
; A stripped down version of 5.script with the throttle value set to a
; ratio between two values.  Used primarily to test our handling of this
; throttle type.  For example, we need to disable ILS at simulator
; start/continue time when this throttle type is used because it
; throws our timing logic way off.

set throttle 30/1
set df disabled
set cpu noidle
echo Running reeeealy slow version of the AC/MQ blinker...
dep 00000 2020
dep 00001 5000
dep 00002 7200
dep 00003 1021
dep 00004 7421
dep 00005 1021
dep 00006 7040
dep 00007 2021
dep 00010 7000
dep 00011 5000
dep 00020 0000
dep 00021 0000
go  00000
Added boot/run.script.in.



















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
; Same as 0.script, only with RK0 attached to the *.rk05 file in the
; build directory, not the one in the installation directory.  Used
; by "make run" so you don't have to "make mediainstall" first.
;
reset
echo Loading OS/8 from the RK05 cartridge disk in the build directory...
set cpu 32k
set cpu noidle
set df disabled
@SET_THROTTLE@

@if SIMH_PASS_LOWERCASE
set tti 7b
@else
set tti ksr
@endif

att rk0 bin/@OS8_BOOT_DISK@
boot rk0
Added doc/RELEASE-PROCESS.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# PiDP-8/I Software Release Process

This documents the process for producing release versions of the
software.


## *Can* You Release Yet?

Before release, you must:

*   Fix all Immediate, High, and Medium priority [Bugs](/bugs)
*   Implement all Immediate and High priority [Features](/features)

Or reclassify them, of course.

Most of the bug levels simply aid scheduling: Immediate priority bugs
should be fixed before High, etc. Low priority bugs are "someone should
fix this someday" type of problems, so they can move from release to
release.

The Features levels may be read as:

*   **Immediate**: ASAP, or sooner. :)
*   **High**: Features for this release.
*   **Medium**: Features we'll look at lifting individually to High for
    the next release.
*   **Low**: "Wouldn't it be nice if..."


## Update SIMH

If `tools/simh-update` hasn't been run recently, you might want to do
that and re-test before publishing a new version.


## Publish OS/8 RK05s

Re-configure the software with default settings, remove `bin/*.rk05`,
rebuild, and run `tools/publish-os8` to send the "final" OS/8 disk
images for this version of the software up to tangentsoft.com as
unversioned assets.

Update the date stamp in the "OS/8 RK05 Media" section of the project
home page.


## Update ChangeLog.md

Trawl the Fossil timeline for user-visible changes since the last
release, and write them up in user-focused form into the `ChangeLog.md`
file.  If a regular user of the software cannot see a given change, it
shouldn't go in the `ChangeLog.md`; let it be documented via the
timeline only.


## Update the Release Branch

Run `make release` to check the `ChangeLog.md` file changes in, tagging
that checkin with a release version tag of the form vYYYYMMDD and merge
those changes into the `release` branch.

It runs entirely automatically unless an error occurs, in which case it
stops immediately, so check its output for errors before continuing.


## Update the Home Page Links

The zip and tarball links on the front page produce files named after
the date of the release. Those dates need to be updated immediately
after tagging the release, since they point at the "release" tag applied
by the previous step, so they begin shipping the new release immediately
after tagging it.


## Produce the Normal Binary OS Image

Start with the latest version of [Raspbian Lite][os] on a multi-core
Raspberry Pi.

1.  If the version of the base OS has changed since the last binary OS
    image was created, download the new one.

    While the OS is downloading, zero the SD card you're going to use
    for this, so the prior contents don't affect this process.
    
    Blast the base OS image onto the cleaned SD card.

2.  Boot it up on a multi-core Pi.

    Log in, then retreive and initialize BOSI:

        $ wget https://tangentsoft.com/bosi
        $ chmod +x bosi
        $ exec sudo ./bosi init

    The `exec` bit is required so that the `bosi` invocation is run as
    root without any processes running as `pi` in case the `init` step
    sees that user `pi` hasn't been changed to `pidp8i` here: the
    `usermod` command we give to make that change will refuse to do what
    we ask if there are any running processes owned by user `pi`.

    It will either reboot the system after completing its tasks
    successfully or exit early, giving the reason it failed.

3.  Clone the software repo and build the softare:

        $ ./bosi build

    On reboot, say `top -H` to make sure the software is running and
    that the CPU percentages are reasonable for the platform.

    You may also want to check that it is running properly with a
    `pidp8i` command.  Is the configuration line printed by the
    simulator correct?  Does OS/8 run?  Are there any complaints from
    SIMH, such as about insufficient CPU power?

4.  Do final inside-the-image steps:

        $ ./bosi prepare

5.  Move the SD card to a USB reader plugged into the Pi, boot the Pi
    from its prior SD card, and shrink the OS image:

        $ wget https://tangentsoft.com/bosi
        $ chmod +x bosi
        $ ./bosi shrink

6.  Move the USB reader to the Mac,¹ then say:

        $ bosi image [nls]

    For the ILS images, you can give "ils" as a parameter or leave it
    blank.

7.  The prior step rewrote the SD card with the image file it created.
    Boot it up and make sure it still works.  If you're happy with it,
    give this command *on the desktop PC*.

        $ bosi finish [nls]

    As above, the parameter can be "ils" or left off for the ILS images.

[os]: https://www.raspberrypi.org/downloads/raspbian/


## Produce the "No Lamp Simulator" Binary OS Image

Do the same series of steps above on a single-core Raspberry Pi, except
that you give "nls" parameters to the `image` and `finish` steps.


## Publicizing

While the NLS image uploads — the ILS image was already sent in step 7
in the first pass through the list above — compose the announcement
message, and modify the front page to point to the new images.  You
might also need to update the approximate image sizes reported on that
page.  Post the announcement message and new front page once that second
upload completes.


----------------------

### Footnotes

1.  The image production steps could just as well be done on a Linux box
    or on a Windows box via Cygwin or WSL, but the commands in that
    final stage change due to OS differences.  Since this document
    exists primarily for use by the one who uses it, there is little
    point in having alternatives for other desktop OSes above.  Should
    someone else take over maintainership, they can translate the above
    commands for their own desktop PC.


### License

Copyright © 2016-2017 by Warren Young. This document is licensed under
the terms of [the SIMH license][sl].

[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md
Added doc/class-simh.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# How to Control SIMH and OS/8 from Python

## Introduction

While we were building the `libexec/mkos8` tool, we built up a set of
functionality for driving SIMH and OS/8 running under SIMH from the
outside using [Python][py], a very powerful programming language well
suited to scripting tasks. It certainly beats writing PDP-8 code to
achieve the same ends!

When someone on the mailing list asked for a way to automatically drive
a demo script he'd found online, it was natural to generalize the core
functionality of `mkos8` as a reusable Python class, then write a script
to make use of it. I called resulting demo script `teco-pi-demo` and the
reusable bits `class simh`, which is now shared with `mkos8`.

This document describes how `teco-pi-demo` works, and through it, how
`class simh` works, with an eye toward teaching you how to reuse this
functionality for your own ends.

[py]: https://www.python.org/


## Invocation

Because we do not install these components in the system's Python
library path, you must modify that path to allow your script to find
these components. Simply copy this invocation block into the top of your
script:

    import os
    import sys
    sys.path.insert (0, os.path.dirname (__file__) + '/../lib')
    sys.path.insert (0, os.getcwd () + '/lib')

    from pidp8i import *
    from simh   import *

That adjusts the path, then imports all of the generic functionality
from the PiDP-8/I `lib` directory into the current namespace.

We do not pull the `mkos8` components into `teco-pi-demo` because they
are intended only to be used by `libexec/mkos8`. If you find something
in the `lib/mkos8` directory that you think is widely useful, make a
case for it on the mailing list, and we'll see about moving it to either
the `simh` or `pidp8i` namespace.

The `sys.path.insert` business assumes that your script is installed
into the PiDP-8/I's `bin` directory alongside `teco-pi-demo`. If you've
installed it somewhere else, you'll need to adjust these paths.


## Starting SIMH

The first thing we'lld do is start SIMH as a child process of our Python
script under control of an instance of `class simh`:

    s = simh (dirs.build)

We call that instance `s` for short, because we will be calling its
methods a lot in this script. 

We pass `dirs.build` to its constructor, which tells it how to find the
`pidp8i-sim` program, which is a version of the PDP-8 simulator from the
SIMH project, configured and modified for the needs of the PiDP-8/I
project. We call this the child program, which is what `class simh`
controls from the outside.

(Currently, we don't have a way to make it use other versions of the
SIMH `pdp8` simulator. Please send a patch if you do that.)

The next step is to tell the `s` object where to send its logging
output:

    s.set_logfile (os.fdopen (sys.stdout.fileno (), 'w', 0))

Contrast the corresponding line in `mkos8` which chooses whether to send
logging output to the console or to a log file:

    s.set_logfile (open (dirs.log + 'mkos8-' + first_act + '.log', 'w') \
        if progmsg else os.fdopen (sys.stdout.fileno (), 'w', 0))


## Finding and Booting the OS/8 Media

If your program will use our OS/8 boot disk, you can find it
programmatically by using the `dirs.os8mo` constant, which means "OS/8
media output directory", where "output" refers to the worldview of
`mkos8`.  Contrast `dirs.os8mi`, which points to the directory holding
the input media for `mkos8`.

This snippet shows how to use it:

    rk = os.path.join (dirs.os8mo, 'os8v3d-bin.rk05')
    if not os.path.isfile (rk):
        print "Could not find " + rk + "; OS/8 media not yet built?"
        exit (1)

Now we attach the RK05 disk image to the PiDP-8/I simulator found by the
`simh` object and boot from it:

    print "Booting " + rk + "..."
    s.send_cmd ("att rk0 " + rk)
    s.send_cmd ("boot rk0")

This shows one of the most-used methods in `class simh`, `send_cmd`,
which sends a line of text along with a carriage return to the spawned
child program, which again is `pidp8i-sim`.


## Driving SIMH and OS/8

After the simulator starts up, we want to wait for an OS/8 `.` prompt
and then send the first OS/8 command to start our demo. We use the
`simh.os8_send_cmd` method for that:

    s.os8_send_cmd ('\.', "R TECO")

This method differs from `send_cmd` in a couple of key ways.

First, it waits for a configurable prompt character — sent as the first
parameter — before sending the command.  This is critical when driving
OS/8 because OS/8 lacks a keyboard input buffer, so if you send text to
it too early, all or part of your input is likely to be lost, so your
command won't work.

Second, because OS/8 can only accept so many characters of input per
second, `os8_send_cmd` inserts a small delay between each input
character to prevent character losses.

(See the commentary for `simh._kbd_delay` if you want to know how that
delay value was calculated.)

The bulk of `teco-pi-demo` consists of more calls to `simh.os8_send_cmd`
and `simh.send_cmd`. Read the script if you want more examples.

Notice that we escape the OS/8 `.` command prompt in the first parameter
because `class simh` treats it as a [regular expression][re], and a `.`
in REs means "any character," which we definitely do *not* want to match
on!  If we did not escape this special RE character with a backslash,
`class simh` would send the command when the first character from the
running OS/8 instance came through, likely causing some or all of the
command to be lost by the time OS/8 is ready to accept input.

[re]: https://en.wikipedia.org/wiki/Regular_expression


## Escaping OS/8 to SIMH

Sometimes you need to escape from OS/8 back to SIMH with a
<kbd>Ctrl-E</kbd> keystroke so that you can send more SIMH commands
after OS/8 starts up. This accomplishes that:

    s.os8_send_ctrl ('e')

While out in the SIMH context, you *could* continue to call the
`simh.os8_*` methods, but since SIMH can accept input as fast as your
program can give it, it is best to use methods like `simh.send_cmd`
which don't insert artificial delays.  For many programs, this
difference won't matter, but it results in a major speed improvement in
a program like `mkos8` which sends many SIMH and OS/8 commands
back-to-back!


## Getting Back to OS/8 from SIMH

There are several ways to get back to the simulated OS/8 environment
from SIMH context, each with different tradeoffs.


### Rebooting

You saw the first one above: send a `boot rk0` command to SIMH. This
restarts OS/8 entirely. This is good if you need a clean environment.
If you need to save state between one run of OS/8 and the next, save it
to the RK05 disk pack or other SIMH media, then re-load it when OS/8
reboots.


### Continuing

The way `teco-pi-demo` does it is to send a `cont` command to SIMH.

The problem with this method is that it sometimes hangs the simulator.
The solution is to insert a small delay *before* escaping to the SIMH
context. I'm not sure why this is sometimes necessary. My best guess is
required to give OS/8 time to settle into an interruptible state before
escaping to SIMH, so that on "continue," we re-enter OS/8 in a sane
state.

You can usually avoid the need for that delay by waiting for an OS/8
command prompt before escaping to SIMH, since that is a reliable
indicator that OS/8 is in such an interruptible state.

You don't see these anomalies when using OS/8 interactively because
humans aren't fast enough to type commands at OS/8 fast enough to cause
the problem.  That is doubtless why there this bug still exists in OS/8
in 2017.


### Re-Entering

If your use of OS/8 is such that all required state is saved to disk
before re-entering OS/8, you can call the `simh.os8_restart` method to
avoid the need for a delay *or* a reboot.  It re-calls OS/8's entry
point from SIMH context, which we've found through much testing is
entirely reliable, as compared to sending a SIMH `cont` command without
having delayed before escaping to SIMH context.

`mkos8` uses this option extensively.


## Sending Escape Characters

Several OS/8 programs expect an <kbd>Escape</kbd> (a.k.a. `ALTMODE`)
keystroke to do things. Examples are `TECO` and `FRTS`. There isn't a
specific method to do this because we can do that in terms of one we've
just described:

    s.os8_send_ctrl ('[')

Yes, <kbd>Escape</kbd> is <kbd>Ctrl-\[</kbd>. Now you can be the life of
the party with that bit of trivia up your sleeve. Or maybe you go to
better parties than I do.


## But There's More!

The above introduced you to most of the functionality of `class simh`
used by `teco-pi-demo`, but there's more to the class than that,
primarily because the `mkos8` script's needs are broader.  Rather than
just recapitulate the class documentation here, please read through [the
class's source code][ssc], paying particular attention to the method
comments. It's a pretty simple class, making it a quick read.

Another useful module is [`pidp8i.dirs`][dsc] which contains paths to
many directories in the PiDP-8/I system, which you can reuse to avoid
having to hard-code their locations.  This not only makes your script
independent of the installation location, which is configurable at build
time via `./configure --prefix=/some/path`, but also allows it to run
correctly from the PiDP-8/I software's build directory, which has a
somewhat different directory structure from the installation tree.

[ssc]: https://tangentsoft.com/pidp8i/file/lib/simh.py
[dsc]: https://tangentsoft.com/pidp8i/file/lib/pidp8i/dirs.py


## <a id="license" name="credits"></a>Credits and License

Written by and copyright © 2017 by Warren Young. Licensed under the
terms of [the SIMH license][sl].

[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md
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/graphics/back.jpg.

cannot compute difference between binary files

Added doc/graphics/front.jpg.

cannot compute difference between binary files

Added doc/graphics/power-switch.png.

cannot compute difference between binary files

Added doc/graphics/schofield-brtval.R.






























































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# schofield-brtval.R: Generate SVG graph of the brightness curves
#    produced by Ian Schofield's ILS patch.
#
# The schofield-brtval.svg file checked into Fossil is modified from the
# version output by this program:
#
# 1. The colors are modified to match the scheme on tangentsoft.com/pidp8i
#
# 2. The data line thickness was increased
#
# 3. The data lines were smoothed by Inkscape's "simplify" function
#
# Copyright © 2017 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.
#

min = 0
max = 32
a = min
b = max

rising  = c(min);
falling = c(max);

for (i in 1:400) {
  a = a + (max - a) * 0.01
  b = b + (min - b) * 0.01
  
  rising[i]  = a
  falling[i] = b
  
  if (a > 31 || b < 1) break
}

data = data.frame(Rising = rising, Falling = falling)
dts = ts(data)
svg("schofield-brtval.svg", width=8, height=6)
plot.ts(dts, plot.type='single', ylab='Brightness',
        yaxp=c(min, max, 8))
dev.off()
Added doc/graphics/schofield-brtval.svg.


























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="576pt"
   height="396.73468pt"
   viewBox="0 0 576 396.73468"
   version="1.1"
   id="svg4276"
   inkscape:version="0.91 r13725"
   sodipodi:docname="schofield-brtval.svg">
  <title
     id="title4481">Schofield ILS Brightness Curves</title>
  <metadata
     id="metadata267">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title>Schofield ILS Brightness Curves</dc:title>
        <cc:license
           rdf:resource="https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md" />
        <dc:date>2017-02-02</dc:date>
        <dc:creator>
          <cc:Agent>
            <dc:title>Warren Young</dc:title>
          </cc:Agent>
        </dc:creator>
        <dc:language>English</dc:language>
        <dc:description>Curves showing the brightness levels of an LED driven from 0 to full brightness and from full brightness back to 0 under Ian Schofield's indandescent lamp simulator for the PiDP-8/I.</dc:description>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <sodipodi:namedview
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1"
     objecttolerance="10"
     gridtolerance="10"
     guidetolerance="10"
     inkscape:pageopacity="0"
     inkscape:pageshadow="2"
     inkscape:window-width="2560"
     inkscape:window-height="1391"
     id="namedview4548"
     showgrid="false"
     inkscape:zoom="1.6333333"
     inkscape:cx="360"
     inkscape:cy="253.95917"
     inkscape:window-x="0"
     inkscape:window-y="1"
     inkscape:window-maximized="1"
     inkscape:current-layer="svg4276"
     fit-margin-top="0"
     fit-margin-left="0"
     fit-margin-right="0"
     fit-margin-bottom="0" />
  <defs
     id="defs4278">
    <g
       id="g4280">
      <symbol
         overflow="visible"
         id="glyph0-0"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0.390625,0 0,-8.609375 6.828125,0 0,8.609375 z m 5.75,-1.078125 0,-6.453125 -4.671875,0 0,6.453125 z"
           id="path4283"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-1"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 7.171875,-8.609375 0,1.03125 -2.890625,0 0,7.578125 -1.1875,0 0,-7.578125 -2.90625,0 0,-1.03125 z"
           id="path4286"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-2"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0.78125,-6.25 1.0625,0 0,6.25 -1.0625,0 z m 0,-2.359375 1.0625,0 0,1.203125 -1.0625,0 z"
           id="path4289"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-3"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0.78125,-6.28125 1.03125,0 0,0.890625 C 2.0625,-5.691406 2.289062,-5.914062 2.5,-6.0625 c 0.34375,-0.238281 0.738281,-0.359375 1.1875,-0.359375 0.5,0 0.898438,0.125 1.203125,0.375 0.164063,0.136719 0.320313,0.34375 0.46875,0.625 0.226563,-0.34375 0.5,-0.59375 0.8125,-0.75 0.320313,-0.164063 0.679687,-0.25 1.078125,-0.25 0.84375,0 1.414062,0.308594 1.71875,0.921875 0.164062,0.324219 0.25,0.765625 0.25,1.328125 L 9.21875,0 8.125,0 l 0,-4.359375 C 8.125,-4.773438 8.019531,-5.0625 7.8125,-5.21875 7.601562,-5.375 7.347656,-5.453125 7.046875,-5.453125 c -0.417969,0 -0.777344,0.140625 -1.078125,0.421875 -0.292969,0.273438 -0.4375,0.730469 -0.4375,1.375 l 0,3.65625 -1.078125,0 0,-4.09375 c 0,-0.425781 -0.054687,-0.738281 -0.15625,-0.9375 C 4.140625,-5.320312 3.84375,-5.46875 3.40625,-5.46875 3.007812,-5.46875 2.644531,-5.3125 2.3125,-5 1.988281,-4.695312 1.828125,-4.140625 1.828125,-3.328125 L 1.828125,0 0.78125,0 Z"
           id="path4292"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-4"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 3.390625,-6.421875 c 0.445313,0 0.878906,0.105469 1.296875,0.3125 0.414062,0.210937 0.734375,0.480469 0.953125,0.8125 0.207031,0.324219 0.347656,0.695313 0.421875,1.109375 0.0625,0.292969 0.09375,0.757812 0.09375,1.390625 l -4.609375,0 C 1.566406,-2.160156 1.71875,-1.648438 2,-1.265625 2.28125,-0.878906 2.71875,-0.6875 3.3125,-0.6875 c 0.550781,0 0.988281,-0.179688 1.3125,-0.546875 C 4.8125,-1.441406 4.945312,-1.6875 5.03125,-1.96875 l 1.03125,0 C 6.039062,-1.738281 5.953125,-1.484375 5.796875,-1.203125 5.640625,-0.921875 5.46875,-0.6875 5.28125,-0.5 4.957031,-0.1875 4.554688,0.0195312 4.078125,0.125 3.828125,0.1875 3.539062,0.21875 3.21875,0.21875 2.4375,0.21875 1.773438,-0.0625 1.234375,-0.625 c -0.542969,-0.570312 -0.8125,-1.367188 -0.8125,-2.390625 0,-1.007813 0.269531,-1.828125 0.8125,-2.453125 0.550781,-0.632812 1.269531,-0.953125 2.15625,-0.953125 z M 5.0625,-3.640625 C 5.019531,-4.097656 4.921875,-4.460938 4.765625,-4.734375 4.484375,-5.242188 4.003906,-5.5 3.328125,-5.5 2.835938,-5.5 2.425781,-5.320312 2.09375,-4.96875 1.769531,-4.625 1.597656,-4.179688 1.578125,-3.640625 Z m -1.78125,-2.78125 z"
           id="path4295"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-5"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 3.25,-8.390625 c 1.082031,0 1.867188,0.449219 2.359375,1.34375 0.375,0.6875 0.5625,1.636719 0.5625,2.84375 C 6.171875,-3.066406 6,-2.125 5.65625,-1.375 5.164062,-0.300781 4.359375,0.234375 3.234375,0.234375 c -1,0 -1.75,-0.4375 -2.25,-1.3125 C 0.578125,-1.816406 0.375,-2.800781 0.375,-4.03125 c 0,-0.945312 0.125,-1.765625 0.375,-2.453125 0.457031,-1.269531 1.289062,-1.90625 2.5,-1.90625 z m -0.015625,7.65625 c 0.550781,0 0.988281,-0.238281 1.3125,-0.71875 0.320313,-0.488281 0.484375,-1.394531 0.484375,-2.71875 0,-0.945313 -0.121094,-1.726563 -0.359375,-2.34375 C 4.441406,-7.128906 3.988281,-7.4375 3.3125,-7.4375 c -0.625,0 -1.085938,0.292969 -1.375,0.875 -0.28125,0.585938 -0.421875,1.445312 -0.421875,2.578125 0,0.855469 0.09375,1.542969 0.28125,2.0625 0.28125,0.792969 0.757813,1.1875 1.4375,1.1875 z"
           id="path4298"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-6"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 1.484375,-2.140625 c 0.070313,0.605469 0.351563,1.023437 0.84375,1.25 0.25,0.117187 0.535156,0.171875 0.859375,0.171875 0.625,0 1.085938,-0.195312 1.390625,-0.59375 C 4.878906,-1.707031 5.03125,-2.148438 5.03125,-2.640625 5.03125,-3.222656 4.847656,-3.675781 4.484375,-4 4.128906,-4.320312 3.703125,-4.484375 3.203125,-4.484375 c -0.367187,0 -0.679687,0.074219 -0.9375,0.21875 C 2.003906,-4.128906 1.785156,-3.9375 1.609375,-3.6875 L 0.6875,-3.734375 1.328125,-8.25 l 4.359375,0 0,1.015625 -3.5625,0 -0.359375,2.328125 c 0.195313,-0.144531 0.382813,-0.253906 0.5625,-0.328125 0.3125,-0.125 0.671875,-0.1875 1.078125,-0.1875 0.769531,0 1.421875,0.25 1.953125,0.75 0.539063,0.492187 0.8125,1.117187 0.8125,1.875 0,0.792969 -0.25,1.496094 -0.75,2.109375 -0.492187,0.6054688 -1.273437,0.90625 -2.34375,0.90625 -0.679687,0 -1.28125,-0.1953125 -1.8125,-0.578125 -0.523437,-0.382813 -0.8125,-0.976563 -0.875,-1.78125 z"
           id="path4301"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-7"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 1.15625,-5.9375 0,-0.8125 c 0.757812,-0.070312 1.285156,-0.195312 1.578125,-0.375 0.300781,-0.175781 0.53125,-0.585938 0.6875,-1.234375 l 0.828125,0 L 4.25,0 3.125,0 l 0,-5.9375 z"
           id="path4304"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-8"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="M 0.375,0 C 0.414062,-0.71875 0.566406,-1.34375 0.828125,-1.875 1.085938,-2.414062 1.59375,-2.90625 2.34375,-3.34375 L 3.46875,-4 c 0.5,-0.289062 0.851562,-0.539062 1.0625,-0.75 0.320312,-0.320312 0.484375,-0.691406 0.484375,-1.109375 0,-0.488281 -0.152344,-0.875 -0.453125,-1.15625 -0.292969,-0.289063 -0.679688,-0.4375 -1.15625,-0.4375 -0.730469,0 -1.230469,0.273437 -1.5,0.8125 -0.15625,0.304687 -0.242188,0.710937 -0.25,1.21875 l -1.078125,0 c 0.007813,-0.726563 0.144531,-1.320313 0.40625,-1.78125 0.457031,-0.8125 1.265625,-1.21875 2.421875,-1.21875 0.957031,0 1.65625,0.261719 2.09375,0.78125 0.445312,0.523437 0.671875,1.101563 0.671875,1.734375 0,0.667969 -0.234375,1.242188 -0.703125,1.71875 C 5.195312,-3.90625 4.707031,-3.566406 4,-3.171875 l -0.8125,0.4375 C 2.8125,-2.523438 2.515625,-2.320312 2.296875,-2.125 1.898438,-1.789062 1.648438,-1.414062 1.546875,-1 l 4.59375,0 0,1 z"
           id="path4307"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-9"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 3.125,0.234375 c -1,0 -1.726562,-0.2695312 -2.171875,-0.8125 -0.449219,-0.550781 -0.671875,-1.21875 -0.671875,-2 l 1.109375,0 c 0.039063,0.542969 0.140625,0.9375 0.296875,1.1875 0.28125,0.4375 0.773438,0.65625 1.484375,0.65625 0.5625,0 1.007813,-0.144531 1.34375,-0.4375 0.332031,-0.300781 0.5,-0.6875 0.5,-1.15625 0,-0.570313 -0.179687,-0.972656 -0.53125,-1.203125 -0.355469,-0.238281 -0.84375,-0.359375 -1.46875,-0.359375 -0.074219,0 -0.148437,0.00781 -0.21875,0.015625 -0.074219,0 -0.148437,0 -0.21875,0 l 0,-0.9375 c 0.113281,0.023438 0.207031,0.03125 0.28125,0.03125 0.070313,0 0.148437,0 0.234375,0 0.394531,0 0.71875,-0.0625 0.96875,-0.1875 C 4.507812,-5.1875 4.734375,-5.578125 4.734375,-6.140625 4.734375,-6.554688 4.582031,-6.875 4.28125,-7.09375 3.988281,-7.320312 3.644531,-7.4375 3.25,-7.4375 c -0.699219,0 -1.183594,0.234375 -1.453125,0.703125 -0.148437,0.25 -0.230469,0.617187 -0.25,1.09375 l -1.046875,0 c 0,-0.625 0.125,-1.15625 0.375,-1.59375 0.425781,-0.78125 1.179688,-1.171875 2.265625,-1.171875 0.851563,0 1.515625,0.195312 1.984375,0.578125 0.46875,0.375 0.703125,0.929687 0.703125,1.65625 0,0.511719 -0.136719,0.929687 -0.40625,1.25 -0.179687,0.199219 -0.402344,0.355469 -0.671875,0.46875 0.4375,0.125 0.78125,0.359375 1.03125,0.703125 0.25,0.34375 0.375,0.765625 0.375,1.265625 0,0.804687 -0.265625,1.460937 -0.796875,1.96875 -0.523437,0.5 -1.265625,0.75 -2.234375,0.75 z"
           id="path4310"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-0"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0,-0.390625 -8.609375,0 0,-6.828125 8.609375,0 z m -1.078125,-5.75 -6.453125,0 0,4.671875 6.453125,0 z"
           id="path4313"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-1"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -4.96875,-4.15625 c 0,-0.488281 -0.066406,-0.867188 -0.203125,-1.140625 -0.21875,-0.425781 -0.601563,-0.640625 -1.15625,-0.640625 -0.5625,0 -0.941406,0.230469 -1.140625,0.6875 -0.113281,0.25 -0.171875,0.632812 -0.171875,1.140625 l 0,2.078125 2.671875,0 z M -1,-4.546875 c 0,-0.707031 -0.207031,-1.21875 -0.625,-1.53125 -0.257812,-0.1875 -0.570312,-0.28125 -0.9375,-0.28125 -0.625,0 -1.050781,0.28125 -1.28125,0.84375 -0.125,0.292969 -0.1875,0.683594 -0.1875,1.171875 l 0,2.3125 3.03125,0 z m -7.609375,3.65625 0,-3.6875 c 0,-1.007813 0.304687,-1.726563 0.90625,-2.15625 0.355469,-0.25 0.765625,-0.375 1.234375,-0.375 0.542969,0 0.984375,0.15625 1.328125,0.46875 0.1875,0.15625 0.355469,0.386719 0.5,0.6875 0.167969,-0.4375 0.359375,-0.765625 0.578125,-0.984375 0.375,-0.394531 0.890625,-0.59375 1.546875,-0.59375 0.554687,0 1.054687,0.179688 1.5,0.53125 C -0.335938,-6.476562 0,-5.65625 0,-4.53125 l 0,3.640625 z"
           id="path4316"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-2"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.28125,-0.796875 0,-1.015625 1.09375,0 c -0.21875,-0.070312 -0.476562,-0.269531 -0.78125,-0.59375 -0.300781,-0.320312 -0.453125,-0.691406 -0.453125,-1.109375 0,-0.019531 0.00781,-0.050781 0.015625,-0.09375 0,-0.050781 0.00781,-0.132813 0.015625,-0.25 l 1.109375,0 c -0.00781,0.0625 -0.015625,0.121094 -0.015625,0.171875 0,0.054688 0,0.109375 0,0.171875 0,0.53125 0.171875,0.945313 0.515625,1.234375 0.335938,0.28125 0.726562,0.421875 1.171875,0.421875 l 3.609375,0 0,1.0625 z"
           id="path4319"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-3"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.25,-0.78125 0,-1.0625 6.25,0 0,1.0625 z m -2.359375,0 0,-1.0625 1.203125,0 0,1.0625 z"
           id="path4322"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-4"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.390625,-2.984375 c 0,-0.5 0.121094,-0.929687 0.359375,-1.296875 0.148438,-0.195312 0.351562,-0.398438 0.609375,-0.609375 l -0.796875,0 0,-0.96875 5.703125,0 c 0.800781,0 1.429687,0.117187 1.890625,0.34375 0.851562,0.4375 1.28125,1.265625 1.28125,2.484375 0,0.679688 -0.152344,1.246094 -0.453125,1.703125 -0.304687,0.460937 -0.777344,0.71875 -1.421875,0.78125 l 0,-1.078125 c 0.28125,-0.050781 0.5,-0.148438 0.65625,-0.296875 0.226562,-0.238281 0.34375,-0.613281 0.34375,-1.125 0,-0.8125 -0.289062,-1.34375 -0.859375,-1.59375 C 0.585938,-4.785156 -0.0078125,-4.851562 -0.875,-4.84375 c 0.324219,0.210938 0.5625,0.464844 0.71875,0.765625 0.15625,0.292969 0.234375,0.683594 0.234375,1.171875 0,0.679688 -0.238281,1.273438 -0.71875,1.78125 -0.488281,0.511719 -1.289063,0.765625 -2.40625,0.765625 -1.050781,0 -1.867187,-0.253906 -2.453125,-0.765625 -0.59375,-0.519531 -0.890625,-1.140625 -0.890625,-1.859375 z m 3.21875,-1.90625 c -0.769531,0 -1.34375,0.164063 -1.71875,0.484375 -0.375,0.324219 -0.5625,0.730469 -0.5625,1.21875 0,0.75 0.351563,1.261719 1.046875,1.53125 0.367188,0.148438 0.851562,0.21875 1.453125,0.21875 0.710937,0 1.25,-0.140625 1.625,-0.421875 0.367187,-0.289063 0.546875,-0.679687 0.546875,-1.171875 0,-0.757812 -0.34375,-1.289062 -1.03125,-1.59375 -0.382812,-0.175781 -0.835938,-0.265625 -1.359375,-0.265625 z m -3.25,1.78125 z"
           id="path4325"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-5"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -8.640625,-0.78125 0,-1.046875 3.21875,0 C -5.742188,-2.078125 -5.96875,-2.300781 -6.09375,-2.5 -6.3125,-2.84375 -6.421875,-3.269531 -6.421875,-3.78125 c 0,-0.90625 0.320313,-1.519531 0.953125,-1.84375 0.34375,-0.175781 0.824219,-0.265625 1.4375,-0.265625 l 4.03125,0 0,1.078125 -3.953125,0 c -0.457031,0 -0.796875,0.0625 -1.015625,0.1875 -0.34375,0.1875 -0.515625,0.546875 -0.515625,1.078125 0,0.4375 0.152344,0.835937 0.453125,1.1875 0.304688,0.355469 0.871094,0.53125 1.703125,0.53125 l 3.328125,0 0,1.046875 z"
           id="path4328"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-6"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -8.03125,-0.984375 0,-1.0625 1.75,0 0,-1 0.859375,0 0,1 4.109375,0 c 0.21875,0 0.367188,-0.078125 0.4375,-0.234375 0.042969,-0.070312 0.0625,-0.207031 0.0625,-0.40625 0,-0.050781 0,-0.101562 0,-0.15625 -0.007812,-0.0625 -0.015625,-0.128906 -0.015625,-0.203125 l 0.828125,0 c 0.03125,0.117187 0.0507812,0.242187 0.0625,0.375 0.0195312,0.125 0.03125,0.265625 0.03125,0.421875 0,0.492188 -0.125,0.824219 -0.375,1 -0.25,0.179688 -0.578125,0.265625 -0.984375,0.265625 l -4.15625,0 0,0.84375 -0.859375,0 0,-0.84375 z"
           id="path4331"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-7"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.28125,-0.78125 0,-1 0.890625,0 c -0.363281,-0.289062 -0.625,-0.601562 -0.78125,-0.9375 -0.164063,-0.332031 -0.25,-0.703125 -0.25,-1.109375 0,-0.882813 0.3125,-1.484375 0.9375,-1.796875 0.34375,-0.175781 0.828125,-0.265625 1.453125,-0.265625 l 4.03125,0 0,1.078125 -3.953125,0 c -0.382813,0 -0.691406,0.058594 -0.921875,0.171875 -0.394531,0.1875 -0.59375,0.527344 -0.59375,1.015625 0,0.25 0.027344,0.453125 0.078125,0.609375 0.085937,0.292969 0.257813,0.546875 0.515625,0.765625 0.210938,0.179688 0.421875,0.292969 0.640625,0.34375 0.21875,0.054688 0.539063,0.078125 0.953125,0.078125 l 3.28125,0 0,1.046875 z M -6.421875,-3.25 Z"
           id="path4334"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-8"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.421875,-3.390625 c 0,-0.445313 0.105469,-0.878906 0.3125,-1.296875 0.210937,-0.414062 0.480469,-0.734375 0.8125,-0.953125 0.324219,-0.207031 0.695313,-0.347656 1.109375,-0.421875 0.292969,-0.0625 0.757812,-0.09375 1.390625,-0.09375 l 0,4.609375 C -2.160156,-1.566406 -1.648438,-1.71875 -1.265625,-2 -0.878906,-2.28125 -0.6875,-2.71875 -0.6875,-3.3125 c 0,-0.550781 -0.179688,-0.988281 -0.546875,-1.3125 C -1.441406,-4.8125 -1.6875,-4.945312 -1.96875,-5.03125 l 0,-1.03125 c 0.230469,0.023438 0.484375,0.109375 0.765625,0.265625 0.28125,0.15625 0.515625,0.328125 0.703125,0.515625 0.3125,0.324219 0.5195312,0.726562 0.625,1.203125 0.0625,0.25 0.09375,0.539063 0.09375,0.859375 0,0.78125 -0.28125,1.445312 -0.84375,1.984375 -0.570312,0.542969 -1.367188,0.8125 -2.390625,0.8125 -1.007813,0 -1.828125,-0.269531 -2.453125,-0.8125 -0.632812,-0.550781 -0.953125,-1.269531 -0.953125,-2.15625 z m 2.78125,-1.671875 c -0.457031,0.042969 -0.820313,0.140625 -1.09375,0.296875 -0.507813,0.28125 -0.765625,0.761719 -0.765625,1.4375 0,0.492187 0.179688,0.902344 0.53125,1.234375 0.34375,0.324219 0.789062,0.496094 1.328125,0.515625 z m -2.78125,1.78125 z"
           id="path4337"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-9"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -1.96875,-1.40625 c 0.355469,-0.03125 0.625,-0.117188 0.8125,-0.265625 0.335938,-0.257813 0.5,-0.71875 0.5,-1.375 0,-0.394531 -0.082031,-0.738281 -0.25,-1.03125 -0.164062,-0.300781 -0.425781,-0.453125 -0.78125,-0.453125 -0.269531,0 -0.476562,0.121094 -0.625,0.359375 -0.082031,0.15625 -0.179688,0.460937 -0.296875,0.90625 L -2.8125,-2.421875 C -2.945312,-1.890625 -3.097656,-1.5 -3.265625,-1.25 c -0.28125,0.460938 -0.675781,0.6875 -1.1875,0.6875 -0.59375,0 -1.070313,-0.210938 -1.4375,-0.640625 C -6.253906,-1.628906 -6.4375,-2.207031 -6.4375,-2.9375 c 0,-0.9375 0.277344,-1.613281 0.828125,-2.03125 0.355469,-0.269531 0.734375,-0.398438 1.140625,-0.390625 l 0,1 c -0.238281,0.023437 -0.457031,0.105469 -0.65625,0.25 -0.269531,0.242187 -0.40625,0.664063 -0.40625,1.265625 0,0.398438 0.078125,0.699219 0.234375,0.90625 0.148437,0.199219 0.34375,0.296875 0.59375,0.296875 0.273437,0 0.492187,-0.132813 0.65625,-0.40625 0.09375,-0.15625 0.179687,-0.382813 0.25,-0.6875 l 0.171875,-0.6875 c 0.1875,-0.757813 0.367188,-1.269531 0.53125,-1.53125 0.273438,-0.40625 0.699219,-0.609375 1.28125,-0.609375 0.554688,0 1.03125,0.214844 1.4375,0.640625 0.40625,0.417969 0.609375,1.058594 0.609375,1.921875 0,0.9375 -0.2109375,1.605469 -0.625,2 -0.425781,0.386719 -0.953125,0.589844 -1.578125,0.609375 z m -4.453125,-1.546875 z"
           id="path4340"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-10"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -8.390625,-3.25 c 0,-1.082031 0.449219,-1.867188 1.34375,-2.359375 0.6875,-0.375 1.636719,-0.5625 2.84375,-0.5625 1.136719,0 2.078125,0.171875 2.828125,0.515625 1.074219,0.492188 1.609375,1.296875 1.609375,2.421875 0,1 -0.4375,1.75 -1.3125,2.25 -0.738281,0.40625 -1.722656,0.609375 -2.953125,0.609375 -0.945312,0 -1.765625,-0.125 -2.453125,-0.375 -1.269531,-0.457031 -1.90625,-1.289062 -1.90625,-2.5 z m 7.65625,0.015625 c 0,-0.550781 -0.238281,-0.988281 -0.71875,-1.3125 -0.488281,-0.320313 -1.394531,-0.484375 -2.71875,-0.484375 -0.945313,0 -1.726563,0.121094 -2.34375,0.359375 -0.613281,0.230469 -0.921875,0.683594 -0.921875,1.359375 0,0.625 0.292969,1.085938 0.875,1.375 0.585938,0.28125 1.445312,0.421875 2.578125,0.421875 0.855469,0 1.542969,-0.09375 2.0625,-0.28125 0.792969,-0.28125 1.1875,-0.757813 1.1875,-1.4375 z"
           id="path4343"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-11"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -2.96875,-3.96875 -3.8125,0 3.8125,2.6875 z M 0,-3.984375 l -2.046875,0 0,3.671875 -1.03125,0 -5.34375,-3.84375 0,-0.890625 5.453125,0 0,-1.234375 0.921875,0 0,1.234375 2.046875,0 z"
           id="path4346"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-12"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -4.875,-3.265625 c 0,-0.46875 -0.128906,-0.832031 -0.390625,-1.09375 C -5.523438,-4.617188 -5.832031,-4.75 -6.1875,-4.75 c -0.3125,0 -0.597656,0.125 -0.859375,0.375 -0.269531,0.25 -0.40625,0.632812 -0.40625,1.140625 0,0.511719 0.136719,0.882813 0.40625,1.109375 0.261719,0.230469 0.5625,0.34375 0.90625,0.34375 0.398437,0 0.710937,-0.144531 0.9375,-0.4375 0.21875,-0.300781 0.328125,-0.648438 0.328125,-1.046875 z m 4.15625,-0.0625 c 0,-0.488281 -0.128906,-0.894531 -0.390625,-1.21875 -0.269531,-0.320313 -0.664063,-0.484375 -1.1875,-0.484375 -0.539063,0 -0.953125,0.167969 -1.234375,0.5 -0.28125,0.335938 -0.421875,0.761719 -0.421875,1.28125 0,0.5 0.148437,0.914062 0.4375,1.234375 0.28125,0.3125 0.679687,0.46875 1.1875,0.46875 0.4375,0 0.820313,-0.144531 1.140625,-0.4375 0.3125,-0.289063 0.46875,-0.738281 0.46875,-1.34375 z m -3.75,1.5 c -0.125,0.292969 -0.269531,0.523437 -0.4375,0.6875 -0.3125,0.304687 -0.71875,0.453125 -1.21875,0.453125 -0.625,0 -1.160156,-0.222656 -1.609375,-0.671875 -0.457031,-0.457031 -0.6875,-1.097656 -0.6875,-1.921875 0,-0.8125 0.214844,-1.441406 0.640625,-1.890625 0.429688,-0.457031 0.921875,-0.6875 1.484375,-0.6875 0.523437,0 0.949219,0.132813 1.28125,0.390625 0.179687,0.148438 0.355469,0.375 0.53125,0.6875 0.15625,-0.34375 0.339844,-0.613281 0.546875,-0.8125 0.398438,-0.375 0.90625,-0.5625 1.53125,-0.5625 0.742188,0 1.367188,0.25 1.875,0.75 0.5117188,0.492188 0.765625,1.1875 0.765625,2.09375 0,0.824219 -0.21875,1.515625 -0.65625,2.078125 -0.445313,0.5625 -1.09375,0.84375 -1.9375,0.84375 -0.488281,0 -0.914063,-0.117187 -1.28125,-0.359375 -0.363281,-0.238281 -0.640625,-0.597656 -0.828125,-1.078125 z"
           id="path4349"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-13"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -5.9375,-1.15625 -0.8125,0 c -0.070312,-0.757812 -0.195312,-1.285156 -0.375,-1.578125 -0.175781,-0.300781 -0.585938,-0.53125 -1.234375,-0.6875 l 0,-0.828125 8.359375,0 0,1.125 -5.9375,0 z"
           id="path4352"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-14"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="M 0,-0.375 C -0.71875,-0.414062 -1.34375,-0.566406 -1.875,-0.828125 -2.414062,-1.085938 -2.90625,-1.59375 -3.34375,-2.34375 L -4,-3.46875 c -0.289062,-0.5 -0.539062,-0.851562 -0.75,-1.0625 -0.320312,-0.320312 -0.691406,-0.484375 -1.109375,-0.484375 -0.488281,0 -0.875,0.152344 -1.15625,0.453125 -0.289063,0.292969 -0.4375,0.679688 -0.4375,1.15625 0,0.730469 0.273437,1.230469 0.8125,1.5 0.304687,0.15625 0.710937,0.242188 1.21875,0.25 l 0,1.078125 c -0.726563,-0.007813 -1.320313,-0.144531 -1.78125,-0.40625 -0.8125,-0.457031 -1.21875,-1.265625 -1.21875,-2.421875 0,-0.957031 0.261719,-1.65625 0.78125,-2.09375 0.523437,-0.445312 1.101563,-0.671875 1.734375,-0.671875 0.667969,0 1.242188,0.234375 1.71875,0.703125 0.28125,0.273438 0.621094,0.761719 1.015625,1.46875 l 0.4375,0.8125 c 0.210937,0.375 0.414063,0.671875 0.609375,0.890625 0.335938,0.398437 0.710938,0.648437 1.125,0.75 l 0,-4.59375 1,0 z"
           id="path4355"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-15"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -8.421875,-3.515625 c 0,-0.9375 0.246094,-1.585937 0.734375,-1.953125 0.480469,-0.375 0.980469,-0.5625 1.5,-0.5625 l 0,1.046875 c -0.332031,0.0625 -0.59375,0.164063 -0.78125,0.296875 C -7.320312,-4.425781 -7.5,-4.039062 -7.5,-3.53125 c 0,0.59375 0.277344,1.070312 0.828125,1.421875 0.542969,0.34375 1.320313,0.53125 2.328125,0.5625 -0.351562,-0.238281 -0.617188,-0.539063 -0.796875,-0.90625 -0.15625,-0.332031 -0.234375,-0.707031 -0.234375,-1.125 0,-0.707031 0.226562,-1.320313 0.671875,-1.84375 0.449219,-0.519531 1.121094,-0.78125 2.015625,-0.78125 0.761719,0 1.4375,0.25 2.03125,0.75 0.5859375,0.492187 0.875,1.195313 0.875,2.109375 0,0.792969 -0.296875,1.476562 -0.890625,2.046875 -0.601563,0.5625 -1.609375,0.84375 -3.015625,0.84375 -1.039062,0 -1.925781,-0.125 -2.65625,-0.375 -1.382812,-0.488281 -2.078125,-1.382813 -2.078125,-2.6875 z M -0.71875,-3.4375 c 0,-0.550781 -0.1875,-0.960938 -0.5625,-1.234375 -0.375,-0.28125 -0.816406,-0.421875 -1.328125,-0.421875 -0.425781,0 -0.832031,0.125 -1.21875,0.375 -0.382813,0.242188 -0.578125,0.6875 -0.578125,1.34375 0,0.449219 0.152344,0.84375 0.453125,1.1875 0.292969,0.34375 0.742187,0.515625 1.34375,0.515625 0.53125,0 0.980469,-0.15625 1.34375,-0.46875 0.367187,-0.3125 0.546875,-0.742187 0.546875,-1.296875 z"
           id="path4358"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-16"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0.234375,-3.125 c 0,1 -0.2695312,1.726562 -0.8125,2.171875 -0.550781,0.449219 -1.21875,0.671875 -2,0.671875 l 0,-1.109375 c 0.542969,-0.039063 0.9375,-0.140625 1.1875,-0.296875 0.4375,-0.28125 0.65625,-0.773438 0.65625,-1.484375 0,-0.5625 -0.144531,-1.007813 -0.4375,-1.34375 -0.300781,-0.332031 -0.6875,-0.5 -1.15625,-0.5 -0.570313,0 -0.972656,0.179687 -1.203125,0.53125 -0.238281,0.355469 -0.359375,0.84375 -0.359375,1.46875 0,0.074219 0.00781,0.148437 0.015625,0.21875 0,0.074219 0,0.148437 0,0.21875 l -0.9375,0 c 0.023438,-0.113281 0.03125,-0.207031 0.03125,-0.28125 0,-0.070313 0,-0.148437 0,-0.234375 0,-0.394531 -0.0625,-0.71875 -0.1875,-0.96875 -0.21875,-0.445312 -0.609375,-0.671875 -1.171875,-0.671875 -0.414063,0 -0.734375,0.152344 -0.953125,0.453125 -0.226562,0.292969 -0.34375,0.636719 -0.34375,1.03125 0,0.699219 0.234375,1.183594 0.703125,1.453125 0.25,0.148437 0.617187,0.230469 1.09375,0.25 l 0,1.046875 c -0.625,0 -1.15625,-0.125 -1.59375,-0.375 -0.78125,-0.425781 -1.171875,-1.179688 -1.171875,-2.265625 0,-0.851563 0.195312,-1.515625 0.578125,-1.984375 0.375,-0.46875 0.929687,-0.703125 1.65625,-0.703125 0.511719,0 0.929687,0.136719 1.25,0.40625 0.199219,0.179687 0.355469,0.402344 0.46875,0.671875 0.125,-0.4375 0.359375,-0.78125 0.703125,-1.03125 0.34375,-0.25 0.765625,-0.375 1.265625,-0.375 0.804687,0 1.460937,0.265625 1.96875,0.796875 0.5,0.523437 0.75,1.265625 0.75,2.234375 z"
           id="path4361"
           inkscape:connector-curvature="0" />
      </symbol>
    </g>
  </defs>
  <rect
     id="rect4364"
     style="fill:#fff3df;fill-opacity:1;stroke:none"
     height="396.73468"
     width="576"
     y="0"
     x="0" />
  <path
     style="fill:none;stroke:#45873b;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
     d="M 88.266406,310.63218 C 122.02676,237.79712 168.07359,167.7088 234.99687,121.36656 298.00502,77.074005 374.52101,57.963738 449.84844,47.956407 c 29.57913,-3.956704 59.3163,-6.634684 89.08593,-8.656254"
     id="path4366"
     inkscape:connector-curvature="0" />
  <path
     style="fill:none;stroke:#95473f;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
     d="M 88.266406,33.300157 C 122.02506,106.13557 168.07177,176.22644 234.99687,222.56578 c 63.74354,44.80848 141.26877,63.83721 217.47266,73.75781 28.71422,3.77216 57.57485,6.34594 86.46484,8.31216"
     id="path4368"
     inkscape:connector-curvature="0" />
  <g
     id="g4370"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-41.632655)">
    <use
       height="100%"
       width="100%"
       id="use4372"
       y="413.28125"
       x="289.06641"
       xlink:href="#glyph0-1"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4374"
       y="413.28125"
       x="296.39648"
       xlink:href="#glyph0-2"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4376"
       y="413.28125"
       x="299.0625"
       xlink:href="#glyph0-3"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4378"
       y="413.28125"
       x="309.05859"
       xlink:href="#glyph0-4"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4380"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4382"
       y="237.14844"
       x="12.960938"
       xlink:href="#glyph1-1"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4384"
       y="229.14453"
       x="12.960938"
       xlink:href="#glyph1-2"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4386"
       y="225.14844"
       x="12.960938"
       xlink:href="#glyph1-3"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4388"
       y="222.48242"
       x="12.960938"
       xlink:href="#glyph1-4"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4390"
       y="215.80859"
       x="12.960938"
       xlink:href="#glyph1-5"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4392"
       y="209.13477"
       x="12.960938"
       xlink:href="#glyph1-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4394"
       y="205.80078"
       x="12.960938"
       xlink:href="#glyph1-7"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4396"
       y="199.12695"
       x="12.960938"
       xlink:href="#glyph1-8"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4398"
       y="192.45312"
       x="12.960938"
       xlink:href="#glyph1-9"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4400"
       y="186.45312"
       x="12.960938"
       xlink:href="#glyph1-9"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <path
     inkscape:connector-curvature="0"
     id="path4402"
     d="m 86.957812,321.72593 458.527348,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4404"
     d="m 86.957812,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4406"
     d="m 152.46172,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4408"
     d="m 217.96562,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4410"
     d="m 283.46953,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4412"
     d="m 348.97344,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4414"
     d="m 414.47734,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4416"
     d="m 479.98125,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4418"
     d="m 545.48516,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <g
     id="g4420"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4422"
       y="384.48047"
       x="72.421875"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4424"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4426"
       y="384.48047"
       x="134.58984"
       xlink:href="#glyph0-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4428"
       y="384.48047"
       x="141.26367"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4430"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4432"
       y="384.48047"
       x="196.75391"
       xlink:href="#glyph0-7"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4434"
       y="384.48047"
       x="203.42773"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4436"
       y="384.48047"
       x="210.10156"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4438"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4440"
       y="384.48047"
       x="262.25781"
       xlink:href="#glyph0-7"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4442"
       y="384.48047"
       x="268.93164"
       xlink:href="#glyph0-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4444"
       y="384.48047"
       x="275.60547"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4446"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4448"
       y="384.48047"
       x="327.76172"
       xlink:href="#glyph0-8"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4450"
       y="384.48047"
       x="334.43555"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4452"
       y="384.48047"
       x="341.10938"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4454"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4456"
       y="384.48047"
       x="393.26562"
       xlink:href="#glyph0-8"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4458"
       y="384.48047"
       x="399.93945"
       xlink:href="#glyph0-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4460"
       y="384.48047"
       x="406.61328"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4462"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4464"
       y="384.48047"
       x="458.76953"
       xlink:href="#glyph0-9"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4466"
       y="384.48047"
       x="465.44336"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4468"
       y="384.48047"
       x="472.11719"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4470"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4472"
       y="384.48047"
       x="524.27344"
       xlink:href="#glyph0-9"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4474"
       y="384.48047"
       x="530.94727"
       xlink:href="#glyph0-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4476"
       y="384.48047"
       x="537.62109"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <path
     inkscape:connector-curvature="0"
     id="path4478"
     d="m 70.239062,313.46421 0,-282.992177"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4480"
     d="m 70.239062,313.46421 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4482"
     d="m 70.239062,278.08921 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4484"
     d="m 70.239062,242.71421 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4486"
     d="m 70.239062,207.34312 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4488"
     d="m 70.239062,171.96812 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4490"
     d="m 70.239062,136.59312 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4492"
     d="m 70.239062,101.21812 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4494"
     d="m 70.239062,65.843125 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4496"
     d="m 70.239062,30.472033 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <g
     id="g4498"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4500"
       y="353.63281"
       x="41.761719"
       xlink:href="#glyph1-10"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4502"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4504"
       y="318.25781"
       x="41.761719"
       xlink:href="#glyph1-11"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4506"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4508"
       y="282.88281"
       x="41.761719"
       xlink:href="#glyph1-12"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4510"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4512"
       y="250.84766"
       x="41.761719"
       xlink:href="#glyph1-13"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4514"
       y="244.17383"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4516"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4518"
       y="215.47266"
       x="41.761719"
       xlink:href="#glyph1-13"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4520"
       y="208.79883"
       x="41.761719"
       xlink:href="#glyph1-15"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4522"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4524"
       y="180.09766"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4526"
       y="173.42383"
       x="41.761719"
       xlink:href="#glyph1-10"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4528"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4530"
       y="144.72266"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4532"
       y="138.04883"
       x="41.761719"
       xlink:href="#glyph1-11"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4534"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4536"
       y="109.34766"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4538"
       y="102.67383"
       x="41.761719"
       xlink:href="#glyph1-12"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4540"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4542"
       y="73.976562"
       x="41.761719"
       xlink:href="#glyph1-16"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4544"
       y="67.302734"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <path
     inkscape:connector-curvature="0"
     id="path4546"
     d="m 70.239062,321.72593 486.722658,0 0,-299.519523 -486.722658,0 0,299.519523"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
</svg>
Added doc/graphics/serial-db9.jpg.

cannot compute difference between binary files

Added doc/graphics/serial-kk.jpg.

cannot compute difference between binary files

Added doc/graphics/system.jpg.

cannot compute difference between binary files

Added doc/os8-fortran-iv.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# OS/8 FORTRAN IV

OS/8 `FORTRAN IV` is extensively documented:

* Chapter 8 of the [1974 OS/8 Handbook][os8h1974] (large file!).
* Section 2 of the [OS/8 Language Reference Manual AA-H609A-TA][os8lang].
* The [OS/8 Fortran IV Software Support Manual DEC-S8-LFSSA-A-D][os8f4suppt]

This file serves more as a quick guide to what has been gathered
to this repository as far as distribution DECtapes, and patches
are concerned.

The [1979 OS/8 Software Components Catalog][os8cat] provides an
inventory of and part numbers for the OS/8 V3D binary and source
distribution DECtapes. Images of these DECtapes are all found in
media/os8/subsys as follows:

| DEC Part    | Filename                        | Description       |   
| ----------- | ------------------------------- | ----------------- |
| AL-4549D-BA | al-4549d-ba-fr4-v3d-1.1978.tu56 | Binary DECtape #1 |
| AL-5596D-BA | al-5596d-ba-fr4-v3d-2.1978.tu56 | Binary DECtape #2 |
| AL-4545D-SA | al-4545d-sa-fr4-v3d-1.tu56      | Source DECtape #1 |
| AL-4546D-SA | al-4546d-sa-fr4-v3d-2.tu56      | Source DECtape #2 |
| AL-4547D-SA | al-4547d-sa-fr4-v3d-3.tu56      | Source DECtape #3 |

When with-os8-fortran-iv is enabled (which is the default), the
contents of Binary DECtape #1 are installed on the OS/8 RK05 images
built by `mkos8`.

Binary DECtape #2 contains the `.RA` sources for the components
of the FORTRAN IV library.  Those files are already assembled
and archived in `FORLIB.RL` and installed from Binary DECtape #1.

The three source DECtapes contain the buildable source code for the
rest of OS/8 FORTRAN IV system.  Build instructions are found in
Appendix B of the _OS/8 Fortran IV Software Support Manual_.

If with-os8-patches is configured, the `os8v3d-patched.rk05` image will contain the following patches:

* `F4 21.1.2 M` Fix for the `EQUIVALENCE` statement that brings
  `F4.SV` and `PASS3.SV` up to version 4B.
* `F4 51.3.1 M` Enable recognition of `"` as an incorrect character in
  a subroutine call argument that brings `F4.SV` up to version 4C.
* `F4 51.3.2 M` Enable recognition of syntax errors in type
  declarations.
* `FORLIB 51.10.1 M` Updates `FORLIB.RL` to contain an corrected
  `DLOG` function that will correctly handle numbers smaller than
  `1.1-018`.

There is one more patch available, `FRTS 51.3.3 O`, an optional patch
that enables the FORTRAN IV runtime system to accommodate 2 page
system handlers in addition to the TD8E handler.  This patch has not
yet been verified.

FORTRAN IV on the system packs should work.

The sources are available in the tree in the files named above.

Enjoy!

[os8h1974]: http://bitsavers.trailing-edge.com/pdf/dec/pdp8/os8/OS8_Handbook_Apr1974.pdf
[os8lang]: http://bitsavers.trailing-edge.com/pdf/dec/pdp8/os8/AA-H609A-TA_OS8_Language_Reference_Manual_Mar79.pdf
[os8f4suppt]: http://bitsavers.trailing-edge.com/pdf/dec/pdp8/os8/DEC-S8-LFSSA-A-D_F4swSupp.pdf
[os8cat]: https://ia601002.us.archive.org/8/items/bitsavers_decpdp8sofoftwareComponentsCatalogJul79_7798622/AV-0872E-TA_PDP-8_Software_Components_Catalog_Jul79.pdf

### <a id="license"></a>License

Copyright © 2017 by Bill Cattey. Licensed under the terms of
[the SIMH license][sl].

[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md
Added doc/os8-macrel.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# OS/8 `MACREL`

`MACREL`, the MAcro RELocating assembler, was a late development. It
was an attempt to replace `PAL8` with a Macro assembler capable of
producing relocatable modules.  When `MACREL` first came on the scene,
several companies decided to port their next major upgrade to `MACREL`
from `PAL8`.  `MACREL` was so buggy that everybody basically had to
revert to `PAL8` and back-port all the new code originally intended
for the new major upgrade.  This situation befell ETOS Version 5.

We have a binary distribution DECtape image of `MACREL` version 1, DEC
part number `AL-5642A-BA`. Unfortunately the version numbers of the
patches did not match what was shown in the binaries.

With the `MACREL` V1 patches, I wanted to do more research before
recommending application of the patches. In the course of that
research, I discovered that all the archived manuals to be found online
were for `MACREL` v2.

See: [Willem van der Mark's PDP-8 Manuals archive][vandermarkman] for:

* [OS/8 MACREL/LINK -- Software Support -- Version 2C -- September 1980 AA-J7073-TA][maclinkss]
* [OS/8 MACREL/LINK -- User Manual -- Version 2D -- January 1979 AA-5664B-TA][maclinkuser]

Or see the [PDP8 doc tree on ftp.dbit com][dbitdocs] for:

* [maclkssm.doc -- OS/8 MACREL/LINK V2 Software Support Manual][dbitmacssm]
* [maclnkum.doc -- OS/8 MACREL/LINK V2 User's Manual.][dbitmacuser]
* [macrelrn.doc -- OS/8 MACREL/LINK V2 Release Notes][dbitmacrel]

Version 2 was the clearly better baseline.  I didn't hold out much
hope to find binary and source distributions of `MACREL` v2.  (DEC
Part numbers `AL-5642B-BA` for the binary DECtape and the 4 source
DECtapes, `AL-5643B-SA`, `AL-5644B-SA`, `AL-H602B-SA`, and
`AL-H602B-SA`.)

Very recently we found a complete set of `MACREL` version 2 binaries
as part of a buildable RTS-8 Archive at [ibiblio.org ... pdp-8/rts8/v3/release][rts8rel]

We found a source distribution of `MACREL` v2 in Dave Gesswein's
[misc_floppy][dgfloppy] archive.  Part one is flagged as having
errors, but another obscure site had a mis-labeled archive of this
same stuff so we may be ok.

The `MACREL` v2 source would not build under `MACREL` v1, but now we
have `MACREL` v2 and initial tests look promising.

Baseline `MACREL` v2 will be integrated into the system packs.
Because we didn't have `MACREL` v2, no work was done to create patch
files, or to validate them.  With both source and binary for `MACREL`
v2 now in hand, this work can proceed.  The plan is to fetch the
patches, validate them, and install all mandatory patches that can be
verified. 

The current integration of `MACREL` v2 includes a hand-applied patch
to `FUTIL`.  We want the latest version of `FUTIL` because it contains
new code handles extended memory and certain `MACREL` data
structures. However version 8A of `FUTIL` shipped on the `MACREL` v2
tape had a bug that causes it to *hang* when run under `BATCH`.

Patch `35.13.1M` fixes this problem and upgrades `FUTIL` to version
8B.  This patch was applied by hand tested, and grouped with what we
integrate onto the system packs when we add `MACREL`.

To reduce uncertainty around the operation of `OVRDRV.MA`, Source
patch `41.5.1M` has been applied by hand to `OVRDRV.MA`.

See also: [our documentaiton on the OS/8 Patching][os8patches]

[vandermarkman]:   http://vandermark.ch/pdp8/index.php?n=PDP8.Manuals
[maclinkss]: 	   http://vandermark.ch/pdp8/uploads/PDP8/PDP8.Manuals/AA-J073A-TA.txt
[maclinkuser]:	   http://vandermark.ch/pdp8/uploads/PDP8/PDP8.Manuals/AA-5664B-TA.txt
[dbitdocs]:	   ftp://ftp.dbit.com/pub/pdp8/doc/
[dbitmacssm]:	   ftp://ftp.dbit.com/pub/pdp8/doc/maclkssm.doc
[dbitmacuser]:	   ftp://ftp.dbit.com/pub/pdp8/doc/maclnkum.doc
[dbitmacrel]:	   ftp://ftp.dbit.com/pub/pdp8/doc/macrelrn.doc
[rts8rel]:	   http://www.ibiblio.org/pub/academic/computer-science/history/pdp-8/rts8/v3/release 
[dgfloppy]:	   http://www.pdp8online.com/images/images/misc_floppy.shtml
[os8patches]:	   https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-patching.md

### <a id="license"></a>License

Copyright © 2017 by Bill Cattey. Licensed under the terms of
[the SIMH license][sl].

[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md
Added doc/os8-patching.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# OS/8 System Patches

Between major updates to distribution media, DEC would send out
important information and patches to customers through its publication
_PDP-8 Digital Software News_ (_DSN_ for short).

Many issues of _DSN_ can be found on bitsavers.org under
[pdf/dec/pdp8/softwarenews][dsn].

To help customers keep track of which patches to apply, _DSN_ added a
Cumulative Index.

Using the _PDP-8 DIGITAL Software News Cumulative Index_ found in the
latest available issue of _DSN_, [October/November 1980][dsn8010], I
created a spreadsheet of all patches relevant to the OS/8 V3D packs
under construction.  That spreadsheet enabled me to go to the
particular issues containing the patches, and keep track of what
action I took with them.

I reviewed all the patches and came up with a list of the mandatory
patches.  Using OCR'd text from each relevant _DSN_ issue, I created a
file per patch, which I then compared to the scanned PDF and corrected
the OCR errors.

Then I enhanced our `mkos8` script to apply the patches in an
automated way.  Most of the patches were for programs available
in source form, so I built the programs from source, and then bench
checked the patch against the source.  In a few cases the code was too
obscure, and I marked the patch as "plausable" rather than "verified"
in my spreadsheet.

The file [patch_list.txt][pl] lists all of the patch files in
`media/os8/patches`.  Comments in that file begin with `#` and are
used to disable patches we have rejected for one reason or another.
Each rejected patch also has a comment that explains why that
particular patch was rejected from the default set.  Typical reasons
are:

*   The patch requires hardware our simulator doesn't have.
*   The patch conflicts with another patch we deem more important.
*   The patch changes some behavior, and we prefer that the unpatched
    behavior be the default.

You may want to examine this file to see if there are any decisions you
would reverse.  After modifying that file, say "`make`" to rebuild the
OS/8 binary RK05 disk image file with your choice of patches.

You can disable all of these OS/8 patches by giving the
`--disable-os8-patches` option to the `configure` script.

[dsn]:     http://bitsavers.org/pdf/dec/pdp8/softwarenews/
[dsn8010]: http://bitsavers.org/pdf/dec/pdp8/softwarenews/198010_PDP8swNews_AA-K629A-BA.pdf
[pl]:      https://tangentsoft.com/pidp8i/doc/trunk/media/os8/patches/patch_list.txt


## Review of Recommendations

`BRTS 31.11.2O` is an optional patch which disables 8th bit parity. It
is recommended because sometimes we may want to allow output that
does not force the 8th bit.

`BRTS 31.11.3O` is an optional patch that enables 132 column
output. It is recommended because it is expected that wide column
output is desirable.

`TECO 31.20.1O` is an optional patch that permanently forces no case
flagging.  It is not recommended because we want to allow the option
of case flagging.

`TECO 31.20.2O` is an optional patch that turns off verbose
errors. It was for slow terminals and experienced users who didn't
want to wait to see the long error messages they already knew.  It is
not recommended because we expect a majority of users to be on high
speed terminals needing the verbose errors.

`TECO 31.20.3O` turns off a warning that you are using the `YANK`
command to completely overwrite a buffer full of text.  Issuing the
command a second time succeeds.  It was again to avoid experienced
users.  It is not recommended because we expect fewer advanced users
who would be annoyed by the protection.

`TECO 31.20.4O` implements rubout support specifically and uniquely
for the `VT05` terminal in a way that breaks it for all other video
terminals.  It is not recommended because there are VERY few `VT05`
deployments that would use it.

It is at this point that I began to notice that in later years, patches
became less carefully produced, and more prone to errors. Some are not
correctable, even today.

`BASIC.UF-31.5.1M` shows:

    4044/4514 4556

changing location `4044` from `4514` to `4556`.  Such a change would be
consistent with the stated purpose of the patch, to correct references
to page zero literals that moved with the `V3D` version of `BRTS`.
The source around location '4044' looks like this:

    04043  4775          JMS I   (BUFCDF /SET UP USER BUF
    04044  1273          TAD     NSAM
    04045  7041          CIA
    04046  3276          DCA     NCTR    /-#OF BOARDS TO CLAR

In my judgment the `TAD NSAM` to get the subscript into the `AC`
should be retained, and the `4556` call to `UNSFIX` to truncate the
value of the Floating Point Accumulator should NOT be inserted.  I
modified the patch to leave out that change.  It remains to be seen if
calls to User Functions in OS/8 `BASIC` will ever be run to test this
code.  Here at least is an analysis to later explorers.

`EDIT 21.17.4 M` is supposedly a mandatory patch.  It fixes a problem
with line printer output through a specific parall interface card.
Unfortunately, the patch overwrites mandatory patch in 21.17.2 and is
NOT recommended.

`ABSLDR 21.29.1 M` is supposedly a mandatory patch that enables
`ABSLDR` to work with `SAVE` image files.  Normally `ABSLDR` only
loads `BIN` format files. The patch sequence number, `21.29`
identifies the patch as being for the OS/8 V3D version of `ABSLDR`.
But the patch changes locations that are not used by `ABSLDR.SV`.
Furthermore, the patch says it upgrades `ABSLDR` from version 6B to
version 6C.

Version 6 of `ABSLDR` was part of the OS/8 V3D Device Extensions kit.
See [our documention on the OS/8 V3D Device Extensions][os8ext].
Verification of this now seems within reach, with the expectation that
it is mis-labeled, and is properly applied to the version with the
Extensions kit. Until it is verified, applying this patch is *not*
recommended.

`PAL8-21.22.4M` is broken and doubly mis-labeled. Mis-label #1: It is
an optional, not mandatory patch. Mis-label #2: It is for product
sequence `35.14`, the `V13` codeline of `PAL-8` that, like `ABSLDR
V6`, is in the Device Extensions kit.  The breakage: Source listing
quits working.  *Do not apply this patch!*

Patch `FRTS-51.3.3-O.patch8` is to enable 2-page system drivers like
`RL01`.  Except that the `RL01` driver is only available in the
Extensions kit.  The patch overwrites existing code that makes `FRTS`
able to function with the `TD8E` 2-page system handler.  I've read the
code but don't fully understand it. Perhaps it generalizes the `TD8E`
support.  But if you happen to be using this setup under `TD8E` and
`FRTS` doesn't work, then back out this patch.


## Patch Application Order

The `patch` routine in `mkos8` applies the patches in the order they
appear in `patch_list.txt`.  That list is currently in alphabetical
order.  However, there may in future emerge patches that impose an
order.

For example, if the `ABSLDR` patch actually did work, it needs the
`FUTIL 31.21.2 M` in order to patch into the `ABSLDR` overlay bits.

I was skeptical of `FUTIL 31.21.2M` because, when I
load `ABSLDR.SV` into core with GET, the contents of memory showed by
`ODT` are *DIFFERENT* from those shown by `FUTIL`. With deeper
understanding of the OS/8 Device Extensions kit, I see that the patch
was incorporated into the version 8 `FUTIL` source, and also that
`ODT` is expected to be updated in version 3S of the Keyboard Monitor.

## Then There's `MACREL`

I've gone into detail on the explorations and understandings with
regard to `MACREL` in a [sister document to this one][macreldoc].

Originally I reviewed the patches for `MACREL` v1, because that's all
we had.  But the version numbers of the patches did not match the
version numbers of the executables.  A little diversion into the guess
work surrounding patch verification:

Version number mismatches sometimes do occur with patches. For
example, `TECO 31.20.11 M` says that it upgrades `TECO` to version
`5.06`, but got the bits wrong.  Instead of changing contents from
`0771` to `0772`, it looked to change from `0772` to `0773`.  `772`
octal is `506` decimal, and the `TECO` version number is represented
with a 12 bit number.  It's called "5.06" but it's represented as
`0772` octal, or `506` decimal.

With that TECO patch, I simply changed the version amendment line in
that `TECO` patch, because the rest was correct.  Whoever published
the patch got the version number wrong, and nobody complained.

With no `MACREL` v1 source verification was not really possible, so
applying those patches was postponed.  But then we found both binary
and source of `MACREL` v2!

In the interests of shipping out system packs in finite time, we will
integrate `MACREL` v2 into the system packs, and verify/apply `MACREL`
v2 patches as follow-on work.

After further testing of 'MACREL' I have concluded that integrating
the source-level patch `41.5.1M` will reduce uncertainty.  So I have
hand-integrated that patch into the `MACREL` tu56 image as well.

[macreldoc]:https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-macrel.md

## `FUTIL`:

I was dubious of some of the `FUTIL` patches, but with finding source
to version 8A, I gained confidence in the version 7 patches, and
understood how seriously important the first patch was to version 8.

The `MACREL` v2 tape shipped with version 8A of `FUTIL`. That was
necessary because V2 of `MACREL` supported the latest memory expansion,
and so the OS/8 Core Control Block needed to change.

Unfortunately, the `FUTIL.SV` distributed as version 8A had the wrong
starting address and Job Status Word settings. It *hangs* when run
under `BATCH`.  Our automated pack builder and patcher `mkos8` run
`FUTIL` under `BATCH`.

The `MACREL` v2 DECtape image we use with `mkos8` contains a
hand-applied patch `35.13.1M` that fixes this problem.

Currently if you opt in to having `MACREL` on the system packs, you
get `FUTIL` version 8B. If not, you get `FUTIL` version 7 and `mkos8`
applies the relevant patches. If `FUTIL` version 8 is installed, the
automated patch applier recognizes the version 7 patches don't fit and
fails to install them.

## One-off Patches

Most of the patches are parsed and applied in an automated manner
by mkos8.  However some are one-offs.

See the `FUTIL` section above with regards to patch `35.13.1M`.

`FORLIB 51.10.1 M` is a one line source change to `DLOG.RA`. The patch
file provides that line. It also provides instructions on how to use
`RALF` to assemble the source and on how to to use `LIBRA` to replace
the old version of `DLOG` with the new one in `FORLIB.RL`.  I followed
the instructions to hand-tool a patched `FORLIB.RL` which I then put
in the `local.tu56` DECtape image along with the other local hacks.
The `patch` routine `mkos8` has in-line code to replace `FORLIB.RL` on
`SYS:` if installation of FORTRAN IV is enabled.


## Unfinished Business

There remain the following patches that are still under development,
because they are not simple binary overlays on executables that could
be applied with simple scripts driving `ODT` or `FUTIL`.  Instead they
are either batch scripts, or are applied to source code that is
rebuilt either with an assembler or high level language compiler.

`LQP 21.49.1 M` patches a device driver `.BN` file, then using `BUILD`
to insert it into the system.  At the present time the OS/8 V3D packs
we build do not use the `LPQ` driver.  (We ran out of device ID space
and so we don't have anywhere to put an active `LPQ` driver.)


## The Tracking Spreadsheet

Below is the latest snapshot of the tracking spreadsheet.

Status column key:

| **A** | Patch Applies Successfully                                   |
| **V** | Patch Source Verified                                        |
| **K** | Patch Source Probably OK. Weaker confidence than "Verified". |
| **P** | Patch Source Plausible. Weaker confidence than "OK".         |
| **N** | Not recommended                                              |
| **O** | OCR Cleaned up. No other verification or application done.   |
| **D** | Does not apply.                                              |
| **B** | Bad patch. DO NOT APPLY.                                     |


### OS/8 V3D Patches

| Component | Issue | Sequence | Mon/Yr | Notes | Status |
| ------ | ------ | ------ | ------ | ------ | ------ |
|  `HANDLER` | `CTRL/Z` and `NULL` | `01 O *` | Oct-77 | Optional. Not going to apply. |  |
|  `CREF` | Bug with `FIXTAB` | `21.15.1M` | Apr/May-78 | `CREF-21.15.1-v2B.patch8` Corrects bad patch | AV |
|   | Input and output file specifications | `21.15.2M` | Feb/Mar-80 | `CREF-21.15.2-v2C.patch8` | AK |
|  `EDIT` | `EDIT` Problem with no `FORMFEED` at end of the input file | `21.17.1M` | Mar-78 | `EDIT-21.17.1M-v12B.patch8` | AV |
|   | `EDIT` `Q` command after `L` command | `21.17.2M` | Jun/Jul-79 | `EDIT-21.17.2M-v12C.patch8` | AV |
|   | `EDIT` `Q` command patch | `21.17.3M` | Jun/Jul-79 | `EDIT-21.17.3M-v12D.patch8` | AV |
|   | `EDIT.SV` `V` option will not work with `LPT DKC8-AA` | `21.17.4M` | Feb/Mar-80 | `EDIT-21.17.4M-v12C.patch8` Overwrites patch `21.12.2M` | AVB |
|  `FOTP` | Incorrect directory validation | `21.19.1M` | Jun/Jul-79 | `FOTP-21.19.1M-v9B.patch8` (Corrected from Aug/Sep 1978, Detailed in Apr/May 79) | AV |
|  `MCPIP` | `DATE-78` Patch for `MCPIP` | `21.21.1M` | Mar-78 | `MCPIP-21.21.1M-v6B.patch8` | AV |
|  `PAL8` | Incorrect core size routine | `21.22.1M` | Aug/Sep-78 | `PAL8-21.22.1M-v10B.patch8` | AV |
|   | Erroneous `LINK` generation noted on `PAGE` directive | `21.22.2M` | Aug/Sep-78 | `PAL8-21.22.2M-v10C.patch8` | AV |
|   | `EXPUNGE` patch to `PAL8` | `21.22.3M` | Feb/Mar-80 | `PAL8-21.22.3M-v10D.patch8` | AK |
|   | `TAB`s are translated incorrectly | `21.22.4M` | Oct/Nov-80 | `PAL8-21.22.4M` (Supercedes June/July 1980 (which had wrong contents of memory.)) Bad! Wrong version of `PAL8`! Breaks list output. | AB |
|  `PIP` | `PIP` `/Y` option does not work properly when transferring a system | `21.23-1M` | Aug/Sep-78 | `PIP-21.23.1M-V12B.patch8` | AK |
|  `PIP10` | `DATE-78` Patch to `PIP 10` | `21.24.1M` | Jun/Jul-79 | `PIP10-21.24.1M-V3B.patch8` (Corrected from Dec 78/Jan 79) | AV |
|  `SET` | Using `SET` with two-page system handlers | `21.26.1M` | Apr/May-78 | `SET-21.26.1M-v1C.patch8` | AV |
|   | `SCOPE` `RUBOUT`s fail in `SET` | `21.26.2M` | Apr/May-78 | `SET-21.26.2M-v1D.patch8` | AV |
|   | Parsing of `=` in `TTY WIDTH` option | `21.26.3M` | Aug/Sep-78 | `SET-21.26.3M-v1E.patch8` | AV |
|  `LPQ` | `LDP01` Handler fails to recognize `TAB`s | `21.49.1M` | Dec/Jan-80 | `LQP-21.49.1M-vB.patch8` (supercedes Mar 1978) | O |
|  `TM8E` | Write protect patch to `TM8E.PA` | `21.61.1H` | Feb/Mar-80 | New `TM8E` Source.  Too hard to correct. |  |


### OS/8 Extension Kit V3D Patches

| Component | Issue | Sequence | Mon/Yr | Notes | Status |
| ------ | ------ | ------ | ------ | ------ | ------ |
|  `SABR` | Line buffer problem in `SABR` | `21.91.1M` | Oct/Nov-79 | `SABR-21.91.1M-v18B.patch8` | AV |
|  `BASIC.UF` | `BASIC.UF` Incompatible from OS/8 V3C | `31.5.1M` | Aug/Sep-78 | `BASIC.UF-31.5.1M-V5B.patch8` Source also in _DSN_. | AV |
|  `BLOAD` | `BLOAD` Will not build `CCB` properly | `31.10.1M` | Feb/Mar-80 | `BLOAD-31.10.1M-v5B.patch8` | AV |
|  `BRTS` | `IOTABLE` Overflow | `31.11.1M` | Mar-78 | `BRTS-31.11.1-M-v5b.patch8` | AV |
|   | `BASIC` `PNT` Function | `31.11.2O` | Jun/Jul-78 | `BRTS-31.11.2-O.patch8` (superceds/corrects Mar 1978) | AV |
|   | Line size on output of `BASIC` | `31.11.3O` | Jun/Jul-78 | `BRTS-31.11.3-O.patch8` | AV |
|   | Change line printer width | `31.11.4F` | Oct/Nov-79 | Optional change of width to 132 columns |  |
|   | Patch to `BRTS` for addressing `LAB 8/E` functions | `31.11.5M` | Oct/Nov-79 | `BRTS-31.11.5-x.patch8` (`BASIC.UF` patch is a prerequisite.) | AV |
|  `TECO` | Changing the default `EU` value for no `case` flagging | `31.20.1O` | Mar-78 | `TECO-31.20.01O.patch8` | AVN |
|   | Changing the default `EH` value for one line error printouts | `31.20.2O` | Mar-78 | `TECO-31.20.02O.patch8` | AVN |
|   | Removing `YANK` protection | `31.20.3O` | Mar-78 | `TECO-31.20.03O.patch8` | AVN |
|   | `SCOPE` Support for `VT05` users | `31.20.4O` | Mar-78 | `TECO-31.20.04O.patch8` | AP N |
|   | Problem with `AY` command | `31.20.5M` | Mar-78 | `TECO-31.20.05M-v5A.patch8` | AV |
|   | Conditionals inside iterations | `31.20.6M` | Mar-78 | `TECO-31.20.06M-v5B.patch8` | AV |
|   | Echoing of warning bells | `31.20.7M` | Mar-78 | `TECO-31.20.07M-v5B.patch8` | AV |
|   | `CTRL/U` Sometimes fails after `*` | `31.20.8M` | Apr/May-78 | `TECO-31.20.08M-v5.04.patch8` | AK |
|   | Multiplying by `0` in `TECO` | `31.20.10M` | Apr/May-78 | `TECO-31.20.10M-v5.05.patch8` | AV |
|   | `Q` registers don't work in 8K | `31.20.11M` | Apr/May-78 | `TECO-31.20.11M-v5.06.patch8` | AV |
|   | Can't skip over `W` | `31.20.12M` | Apr/May-78 | `TECO-31.20.12M-v5.07.patch8` | AV |
|   | Unspecified iterations after inserts | `31.20.13M` | Oct/Nov-78 | `TECO-31.20.13M-v5.08.patch8` (Corrected from Jun/Jul 78) | AV |
|   | New features in `TECO V5` | `31.20.14` N | Aug/Sep-78 | Documentation Only |  |
|  `FUTIL` | `FUTIL` Patch | `31.21.1M` | Apr/May-78 | `FUTIL-31.21.1M-v7B.patch8` | AV |
|   | Fix `SHOW CCB` and mapping of `CD` modules | `31.21.2M` | Oct/Nov-78 | `FUTIL-31.21.2M-v7D.patch8` (Corrected from Aug/Sep 78) | AV |
|   | Optional: change `XS` format from `excess-240` to `excess-237`. Useful for viewing `COS` data files. | `31.21.3O` | Aug/Sep-78 | `FUTIL-31.21.3O.patch8` | AVN |
|   | `FUTIL` Patch to `MACREL`/`LINK` overlays | `31.21.4 N` | Jun/Jul-79 | Documentation Only |  |
|  `MSBAT` | `DIM` Statement not working in `MSBAT` | `31.22.1M` | Dec 78/Jan-79 | `MSBAT-31.22.1M-v3B.patch8` | AV |
|  `BATCH` | `MANUAL INTERVENTION REQUIRED` Erroneously | `31.23.1M` | Aug/Sep-78 | `BATCH-31.23.1M-v7B.patch8` | AV |


### OS/8 FORTRAN IV V3D Patches

| Component | Issue | Sequence | Mon/Yr | Notes | Status |
| ------ | ------ | ------ | ------ | ------ | ------ |
|  `F4` | `EQUIVALENCE` Statement | `02M` / `21.1.2M` | Dec/Jan-80 | `F4-21.1.2M-v4B.patch8` (Revised, Oct 77: `F4` and `PASS3` not `FRTS` patched.) | AP |
|   | `FORTRAN` Compiler fails to recognize `"` as an error | `51.3.1M` | Jun/Jul-78 | `F4-51.3.1M-v4C.patch8` (Corrects March 1978) | AP |
|   | `FORTRAN` Compiler not recognizing syntax error | `51.3.2M` | Jun/Jul-78 | `F4-51.3.2M-v4x.patch8` | AP |
|   | `FORTRAN` runtime system 2-page handler | `51.3.3O` | Oct/Nov-78 | `FRTS-51.3.3-O.patch8` Needed for RL02. (Corrected from Aug/Sep 78) | A |
|   | Restriction with subscripted variables | `51.3.4R` | Aug/Sep-80 | Documentation: `FIV` `FORTRAN IV` will not allow subscripting to be used on both sides of an arithmetic expression. |  |
|  `FORLIB` | `FORTRAN IV` `DLOG` Patch | `51.10.1M` | Feb/Mar-80 | `FORLIB-51.10.1M.patch8` (apply to `DLOG.RA`) | AV |


### OS/8 MACREL/LINKER V1A Patches

These patches are listed for completeness. The version numbers don't
match.  We lack source so we cannot verify them. we've moved on to
`MACREL` v2 as canon.  

| Component | Issue | Sequence | Mon/Yr | Notes | Status |
| ------ | ------ | ------ | ------ | ------ | ------ |
|  `LINK` | Patch `V1D` to `LINK` | `40.2.1M` | Apr/May-78 | `LINK-40.2.1M-v1D.patch8` | O |
|   | Patch `VIE` to `LINK` | `40.2.2M` | Apr/May-78 | `LINK-40.2.2M-v1E.patch8` | O |
|   | `LINK` Corrections | `40.2.3M` | Apr/May-78 | `LINK-40.2.3M-v1F.patch8` | O |
|  `MACREL` | Patch `V1D` to `MACREL` | `40.5.1M` | Apr/May-78 | `MACREL-40.5.1M-v1D.patch8` | OD |
|   | Patch `V1E` to `MACREL` | `40.5.2M` | Apr/May-78 | `MACREL-40.5.2M-v1E.patch8` | OD |
|  `OVRDRV` | Patch `V1B` to `OVRDRV.MA` | `40.6.1M` | Apr/May-78 | `OVRDRV-40.6.1M-v1B-8srccom` | O |


### OS/8 V3D Device Extensions December 1978 Patches

**WARNING**: Do not use this kit without first consulting _DSN_  Apr/May 1979.
See also: [Our OS/8 Device Extensions documentation][os8ext]

| Component | Issue | Sequence | Mon/Yr | Notes | Status |
| ------ | ------ | ------ | ------ | ------ | ------ |
|  `FRTS` | `FRTS` Patch | `35.1.3M` | Apr/May-79 |  |  |
|  `MONITOR` | `MONITOR` `V3S` Patch | `35.2.1M` | Apr/May-79 |  |  |
|  `FUTIL` | `FUTIL` hangs under `BATCH` | `35.13.1M` CRITICAL! | Apr/May-79 |  | AV |
|  `PAL8` | `EXPUNGE` Patch to `PAL8` | `35.14.1M` | Feb/Mar-80 | `PAL8-35.14.1M-v13B.patch8` | AN |
|  `ABSLDR` | Loader problem with `SAVE` image files | `21.29.1M` | Oct/Nov-80 | `ABSLDR-21.29.1M-v6C.patch8` (Supercedes June/July 1980) Bad: v6B was with OS/8 Device Extensions. | OB |
|  `ABSLDR` | `ABSLDR` Patch | `35.18.1M` | Apr/May-79 |  |  |
|  `BLOAD` | `BLOAD` Will not build `CCB` properly | `35.51.1M` | Feb/Mar-80 | `BLOAD-35.51.1M-v5C.patch8` | ON |


### OS/8 MACREL/LINKER V2A Patches

These patches have not been turned into files.  Armed with newly
discovered sources verification is possible.  Work on these will begin
soon.

| Component | Issue | Sequence | Mon/Yr | Notes | Status |
| ------ | ------ | ------ | ------ | ------ | ------ |
|  User's |`EXPUNGE` Documentation error | `41.1.1N` | Jun/Jul-79 |  |  |
|  Guide  | `MACREL` Version numbers: `MACREL` is `V2C` not `V2D`; `LINK` is `V2A` not `V2B`. | `41.1.2N` | Jun/Jul-79 |  |  |
|   | Macro restriction in `MACREL` | `41.1.3N` | Aug/Sep-79 |  |  |
|   | Error in `.MCALL` macro example | `41.1.4N` | Feb/Mar-80 |  |  |
|  `KREF` | Correct printing of numeric local symbols | `41.3.1M` | Apr/May-80 |  |  |
|  `MACREL` | `EXPUNGE` Patch to `MACREL` | `41.4.1F` | Jun/Jul-79 |  |  |
|   | Inconsistencies in `MACREL` error reporting | `41.4.2N` | Aug/Sep-79 |  |  |
|   | Forward reference patch to `MACREL` | `41.4.3M` | Aug/Sep-79 |  |  |
|   | Correct macro substring problem | `41.4.4M` | Apr/May-80 |  |  |
|   | Correct printing of numeric local symbols | `41.4.5M` | Apr/May-80 |  |  |
|  `OVRDRV` | Correct `CDF` problem | `41.5.1M` | Dec/Jan-80 | Source change applied by hand. | AV |
|  `FUTIL` | `FUTIL` hangs under `BATCH` | `35.13.1M` | Apr/May-79 | Critical to proper operation of our automated builder. Applied by hand to the `MACREL` v2 integration.  | AV |



### <a id="license"></a>License

Copyright © 2017 by Bill Cattey. Licensed under the terms of
[the SIMH license][sl].

[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md
[os8ext]: https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-v3d-device-extenaions.md

Added doc/os8-v3d-device-extensions.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# OS/8 V3D Device Extensions

The _OS/8 V3D Device Extensions_ kit (product code `QF026`) was released
in December 1978. It was created to support the newest PDP-8 hardware:

*   The `KT8A` Memory Management option which enables addressing by a
    factor of four beyond the previous maximum of 32K to a whopping
    128K of memory. The highest memory field for a PDP-8a goes up from
    7 to 37 (octal).

*   The `RL01` disk supporting 4 times the previous usual disk capacity,
    now up to nearly 5 Meg.

*   The `RX02` double-density floppy disks.

* Device drivers `VXSY` and `VXNS` enables use of `KT8A` extended
    memory as a file oriented system or non-system device.

This distribution contains software updates:

*   A version of `BUILD`, the system builder that could run under
    `BATCH`.  The previous version would just hang.

*   An update to the OS/8 system including `Keybord Monitor` version
    3S, and a version of `ODT` that works with memory fields greater
    than 7.

*   `ABSLDR` version 6A supports loading into memory fields greater
    than 7.

*   `PAL8` version 13A allows code to specify memory fields greater
    than 7.

*   `CCL` version 7A updates the 'MEMORY' command to recognize up
    to 128K words of memory.

*   `PIP` version 14A knows the sizes of the new devices, and has
    updated how it copies in the monitor system head.

*   `RESORC` version 5A includes new devices.

*   `BOOT` version 5A boots new devices.

*   `RXCOPY` version 4B formats and copies single and double density
    floppies.

*   `FUTIL` version 8A recognizes new core control block format that
    can represent extended above field 7.

The _OS/8 V3D Device Extensions User's Guide_ can be found
in [Willem van der Mark's PDP-8 doc archive][vdmdoc], under
[OS/8 - Device Extensions - User's Guide - December 1978 AA-D319A-TA.pdf][vdmextensions].
or on the [ftp.dbit.com pdp8 doc archive][dbitdoc] at [devextug.doc --
OS/8 Device Extensions User's Guide][dbitug]

The release notes can be found on ftp.dbit.com at [devextrn.doc	OS/8
Device Extensions Release Notes ][dbitrn].

Details on how the `KT8A` Memory Extension hardware works, physically
and programatically, can also be found at Willem van der Mark's site:
[vandermark.ch ... Emulator.128KMemory/EK-KT08A-UG_jul78-ocr.pdf][kt8adoc].

When reference is made to `PAL8` version 13, that version originally came
from this kit.

The distribution DECtape for this kit, part number `AL-H525A-BA` has
not yet been found.  The PDP-8 Software Components Catalog July 1979
gives no part number for a Source DECtape distribution of this kit.
There is an RK05 source distribution, part number
`AN-H529A-SA`. However, plausable source and binary have recently been
found!

The binaries were on someone's local hard disk and not published to
the net anywhere I could find.  Sadly those binaries did not include
the DECtape's system area, and so the updated version of the OS/8
Keyboard Monitor, Command Decoder and `ODT` seemed lost until a tape
could be found. It appears that the original source of these .en files
is Johnny Billingquist's site, [ftp.update.uu.se ... dectape1][uuseext1].

Then, however, a self-extracting archive called, `os8v3f.exe` was
found on a mirror site of ibiblio.org, [rtk.mirrors.pdp-11.ru
... fromhichols][rtknicnols]. Mainline ibiblio.org didn't have it, perhaps it was
purged because of its `.exe` extension.  The archive is also available
directly from [ftp.update.uu.se ... fromnichols][uusenichols], but at slow speed.

When that archive was extracted, the manifest of source files
corresponds exactly to the manifest of binaries in the Extensions Kit
file archive are present.  This looks quite promising for a future
project to upgrade to OS/8 V3D with the Device Extensions software,
and to create system packs useful even on PDP-8a hardware with 128K
words of memory!

After comparing sources found for OS/78, and OS/278, as well as Willem
van der Mark's locally modified sources labeled OS/8 version 4, I have
moderate confidence that these sources will enable validation and
integration of most, if not all the OS/8 V3D Device Extensions
functionality.

[rtknichols]: http://rtk.mirrors.pdp-11.ru/ftp.update.uu.se/pdp8/pdp-8/fromnichols/
[uuseext1]: ftp://ftp.update.uu.se/pub/pdp8/pdp-8/os8/os8.v3d/binaries/devext/dectapes/dectape1/
[uusenichols]: ftp://ftp.update.uu.se/pub/pdp8/pdp-8/fromnichols/

## FUTIL

This validation has been done with regards to `FUTIL`.

The `MACREL` v2 tape shipped with version 8A of `FUTIL`. That was
necessary because V2 of `MACREL` supported the latest memory
expansion, and so the OS/8 Core Control Block format needed to change.

`FUTIL` version 8A integrated patches for `FUTIL` version 7 into the
source.  Finding those patches in the version 8A source strongly
increased my confidence in those patches.

Unfortunately the `FUTIL.SV` verson 8A executable was saved
incorrectly and then shipped. The Core Control Block setting and
starting address were mis-specified. So `FUTIL` version 8A *hangs*
when run under `BATCH`.

The [April-May 1979][dsn1979apr] issue of _PDP-8 Digital Software
News_ contained patch `35.13.1M` which fixed this problem and upgraded
`FUTIL` to version 8B.  I've confirmed both the problem and the fix.

Currently if you opt in to having `MACREL` on the system packs, you
get `MACREL` v2 and `FUTIL` version 8B. If leave `MACREL` out, you get
`FUTIL` version 7. The automated pack builder recognizes that the
version 7 patches won't apply to version 8, and fails to apply them.
The research I did on the OS/8 Device Extensions kit and on
`MACREL` increased my confidence about the `FUTIL` version 7 patches.

See also [our documentation on the `MACREL` integration][macreldoc]
and [our documentation on applying OS/8 patches][patchdoc].

### <a id="license"></a>License

Copyright © 2017 by Bill Cattey. Licensed under the terms of
[the SIMH license][sl].

[vdmdoc]: http://vandermark.ch/pdp8/index.php?n=PDP8.Manuals
[vdmextensions]: http://vandermark.ch/pdp8/uploads/PDP8/PDP8.Manuals/AA-D319A-TA.pdf
[dbitdoc]: ftp://ftp.dbit.com/pub/pdp8/doc/
[dbitug]: ftp://ftp.dbit.com/pub/pdp8/doc/devextug.doc
[dbitrn]: ftp://ftp.dbit.com/pub/pdp8/doc/devextrn.doc
[kt8adoc]: http://www.vandermark.ch/pdp8/uploads/Emulator/Emulator.128KMemory/EK-KT08A-UG_jul78-ocr.pdf
[dsn1979apr]: http://bitsavers.org/pdf/dec/pdp8/softwarenews/198010_PDP8swNews_AA-K629A-BA.pdf
[macreldoc]:https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-macrel.md
[macreldoc]:https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-patching.md
[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md

Changes to doc/pidp8i-test.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

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











-
-
-
-
-
+
+


-
+






+
+
+
+
+



+
+
-
+
+
+
+
+
+
+
+
+







# PCB Test Program
# PiDP-8/I PCB Test Program


## Compiling and Installing

`pidp8i-test` is a simple program to test [Oscar Vermeulen's PiDP-8/I
Kit][project] during construction. It is built and installed alongside
the other software with the normal `make` process.


## Running It

If you are building the software on the Pi for the first time, log out
of your user account after installing it, then back in so that the
install script's changes to your user's `PATH` take effect.

Thereafter, simply give these commands:
If you're running one of the [binary OS images][devhome], simply give
these commands:

    $ sudo systemctl stop pidp8i
	$ pidp8i-test
    $ pidp8i-test

The first command ensures that the modified PDP-8 simulator is stopped
during the test, since only one program can be talking to the switch and
LED array at a given time. (This also applies to other programs like
[Deeper Thought 2][dt2].)

If you built the PiDP-8/I software from source atop some other Raspberry
Pi operating system installation, you will have to log out and back in
after installing the software so the installer's adjustments to your
`PATH` take effect. Then you can use the commands above.


## Test Procedure

The test normally proceeds automatically forward, but you can take
control of the test sequence with these keys:
You can at any time hit Ctrl-C to stop the test.

| Key                               | Effect
| --------------------------------- | ------
| <kbd>↑</kbd> or <kbd>→</kbd>      | Skip to next test
| <kbd>↓</kbd> or <kbd>←</kbd>      | Go back to previous test 
| <kbd>R</kbd>                      | Resume auto-advance behavior
| <kbd>X</kbd> or <kbd>Ctrl-C</kbd> | Exit program

Any of the arrow keypresses stop the auto-advancing behavior.

The test proceeds as follows:

*   All On test:

    It turns on all LEDs for 5 seconds.

90
91
92
93
94
95
96

97
98
99
102
103
104
105
106
107
108
109
110
111
112







+




## License

This document is licensed under the same terms as the associated
[`src/test.c` program][program].


[devhome]: https://tangentsoft.com/pidp8i/
[project]: http://obsolescence.wix.com/obsolescence#!pidp-8
[dt2]:     https://github.com/VentureKing/Deeper-Thought-2
[program]: https://tangentsoft.com/pidp8i/doc/trunk/src/test.c
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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# U/W FOCAL Manual Supplement for the PiDP-8/I

## Introduction

This document is a supplement to the [U/W FOCAL Manual][uwfm]. ("The
Manual") Although it is not a complete FOCAL tutorial — much less a
reference guide! — we suggest that you start learning about our
distribution of FOCAL by skimming through this document *first*, then
proceeding to [the Manual][uwfm], since this document will alert you to
the areas of the Manual that are simply incorrect for the PiDP-8/I
distribution of U/W FOCAL. Having gotten through the Manual, come back
here and re-read this supplement more carefully; you will get more out
of this supplement on that second pass with the context from the Manual.

Other helpful sources are the [U/W FOCAL reference cards][uwfr], the
[U/W FOCAL DECUS submission][uwfd], the [DECUS and OMSI manuals for PS/8
FOCAL, 1971][f71], and the [DEC FOCAL-8 Manual][df8]. To a first
approximation, those are ordered in decreasing degree of application to
our distribution of U/W FOCAL. The final document in that series is
still quite useful for understanding U/W FOCAL, however.

See [below](#rationale) for the reasons why we felt it was necessary to
write this document.


## <a id="starting" name="stopping"></a>Starting and Stopping U/W FOCAL

The section "Starting the Program" in [the 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>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!"!     ⇠ paste your program text here
    ^Z                              ⇠ hit Ctrl-Z to tell PIP it's reached end-of-file (EOF)
    *$                              ⇠ hit Escape to return to OS/8 from PIP ($ = Esc)
    .R UWF16K                       ⇠ run U/W FOCAL
    *O I HELLO                      ⇠ open text file for input; "types" pgm in for us
    _G                              ⇠ underscore = EOF seen, G starts program
    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 Manual][uwfm] talks about loading and saving programs, it is
in terms of the `PUNCH` command, which punches the current program out
on paper tape, because the Manual was written for 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. (We get the [`PLOT`
command](#miss-hw) instead.)

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][uwfd] and `CARD2.DA` in the [refcards][uwfr]
for more examples.


### <a id="ls-write"></a>The `WRITE` Command

U/W FOCAL's `LIBRARY` command 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 problems 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. If you give
these three commands separately, you end up with the `WRITE` command as
the first line in the output file and the `OUTPUT CLOSE` command as the
last; you must then either edit those commands out of the output file or
tolerate having FOCAL run those two commands again every time you load
the program from disk.

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-ptr"></a>The Paper Tape Reader

Above, I warned you off trying to save programs to simulated paper tape
via the `PUNCH` command, but what about *reading* programs back in? You
can do that, but it's trickier than you might guess.

First, if you've read [the Manual][uwfm], you may think to attach a
paper tape to SIMH then use U/W FOCAL's `OPEN READER` command, but as
with `PUNCH`, that command has been replaced in this version of U/W
FOCAL. With the removal of paper tape support in U/W FOCAL proper, they
felt free to reassign `O R` to `OPEN RESTART/RESUME`.

Thus, we again have to pop back out to OS/8 and use PIP to pull this
off.

First we must create that paper tape. If you place your FOCAL source
code in `examples/*.fc`, you can simply type `make` at the top level of
the PiDP-8/I source tree to have it translated to `bin/*-focal.pt` with
the same base name.

(This is done by Bill Cattey's `txt2ptp` program; there is also the
inverse filter, `ptp2txt`. We include the `-focal` tag to distinguish
these files from `*-pal.pt` files produced from `*.pal` source files by
a similar process.)

We'll work with the provided `examples/tratbl.fc` example program,
which got translated to `bin/tratbl-focal.pt` when the PiDP-8/I software
was built.

To attach that paper tape to SIMH's paper tape reader device, hit
<kbd>Ctrl-E</kbd> to get to the SIMH command prompt, then:

    sim> att ptr bin/tratbl-focal.pt
    sim> c

On re-entering the simulator with the `c` ("continue") command, we can
read that tape into OS/8:

    .R PIP
    *TRATBL.DA<PTR:                    ⇠ hit Esc *then* Enter
    .TYPE TRATBL.DA

If the final command doesn't show you the program text you expect,
something went wrong in the process above. There are several likely
possibilities:

1.  You hit <kbd>Enter</kbd> at the end of the PIP command, either
    instead of <kbd>Esc</kbd> or *before* hitting <kbd>Esc</kbd>.
    
    If you do it right, it should appear on screen as:

        *TRATBL.DA<PTR:$^

    with the `$^` appearing when you hit <kbd>Esc</kbd>, signifying that
    it has read the paper tape and hit the end. Hitting <kbd>Enter</kbd>
    should then drop you back to the OS/8 prompt, not leave you in
    `PIP`. If you get another `*` prompt from `PIP` on hitting
    <kbd>Enter</kbd>, you fat-fingered something. Try again.

2.  Every time you cause the PDP-8 to read the paper tape, you must
    re-attach it to SIMH to read it again.  Neither SIMH nor OS/8 warns
    you if you try to read from the paper tape reader with nothing
    attached; you just get no input.

    This mimics what happens with real paper tapes: once the reader has
    read the tape, it falls out of the machine and needs to be fed back
    in to be read again. The difference between the real paper tape
    reader and SIMH is that that repeated sequence is much more of a
    hassle than just sticking the tape back in the reader:

        .<Ctrl-E>
        sim> ATT PTR ...
        sim> C

    That `TTY:` based `PIP` method above will start to look awfully
    attractive after a while...

3.  You saved the FOCAL text out on the host side with Unix line
    endings, so on `TYPE`ing it at the OS/8 command prompt to check it,
    you got stair-stepped output. OS/8 expects CR+LF line endings,
    also called DOS line endings, though OS/8 long precedes MS-DOS,
    and the teletype based design of ASCII that made CR+LF a sensible
    way of ending lines in a text file long precedes *OS/8*. Fix the
    line endings, then say "make" to rebuild the `*-focal.pt` file,
    then reattach the tape, and try again.

Once you make it through that gauntlet, loading the ASCII program text
into U/W FOCAL is just as above: `O I TRATBL`.


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


### Improving the Output Format

The following changes to the examples as given in [the Manual][uwfm]
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 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="overloading"></a>Command Overloading

[The Manual][uwfm] tells you right up front that U/W FOCAL only pays
attention to the first letter of each command when trying to decide what
you're asking it to do, which is why we have strange commands like
`YNCREMENT`: `I` was already taken by `IF`.

What it *doesn't* make as clear is how the creators of U/W FOCAL began
adding a second layer of overloading to this scheme after they began
running out of letters in the English alphabet.

Early examples of FOCAL's command overloading scheme are the `ON`,
`OPEN` and `OUTPUT` commands. By looking at the first argument to the
command, FOCAL can tell which of the three you mean. If you give a valid
FOCAL expression in parentheses, it is clearly an `ON` command. If you
give anything beginning with a letter, FOCAL decides whether it's an
`OPEN` or `OUTPUT` command based on which letter that is; you will
notice that the first letter of no `OPEN` sub-command is the same as the
first letter of any `OUTPUT` sub-command.

In later versions of U/W FOCAL, they added a third level to some
commands. We have `OPEN RESTART READ` and `OPEN RESUME INPUT`, for
example. It may help to abbreviate the commands, as the first letter of
each word is all that really matter: `O R R` is clearly not the same as
`O R I`.

There are other examples of this, such as `LIBRARY` and `LIST`. It is
the first letter of the first argument to these commands that determines
what FOCAL does.

In at least one case, you can see this feature of FOCAL used to talk
about a single command under different names. Some FOCAL documents talk
about a `ZVR` command, meaning Zero VaRiable. It's just another way of
spelling the `ZERO` command: it does the same thing.

FOCAL *only* checks the first letter of the command and any necessary
disambiguating arguments. The following is therefore a perfectly legal
FOCAL program:

    01.10 SPEW I = 0
    01.20 YGGDRASIL I
    01.30 LOOGIE BRAIN 1.2 ; TARPIT I !

It does exactly the same thing as a program we will encounter
[shortly](#lbranch).


## <a id="library"></a>`LIBRARY`

The OS/8 version of U/W FOCAL includes a "library" feature modeled on a
similar feature in OMSI PS/8 FOCAL. These features collectively allow
the user access to FOCAL program files stored in the OS/8 file system
from within a U/W FOCAL program or as immediate commands to U/W FOCAL.

We showed how you can use some of these commands to save and load
programs to disk [above](#ls-library), but there's a lot more to it.
Coverage of this begins on page 34 of [the DECUS submission][uwfd].


## `LIST ALL`, `LIST ONLY`, `ONLY LIST`

These commands allow you to get OS/8 directory listings from within U/W
FOCAL:

*    **<code>LIST ALL</code>** — Same as `DIR` under OS/8. Accepts an
     optional OS/8 device name, e.g. `L A SYS:` to show the contents of the
     `SYS:` device.

*    **<code>LIST ONLY</code>** — Same as `DIR *.FC` under OS/8. You can
     also give a device name, a file name, or both to be more specific. For
     example, you could check for the existence of a `FOO.FC` file on
     the first half of the second RK05 device with `L O RKA1:FOO`.

*    **<code>ONLY LIST</code>** — Same as `LIST ONLY` except for FOCAL
     data files, `*.DA`. The [DECUS submission][uwfd] says it looks for
     `*.FD`, but that does not appear to be correct in my testing.


## <a id="lbranch"></a>`LOGICAL BRANCH`

Our distribution of U/W FOCAL includes the `LOGICAL BRANCH` feature,
which is not documented in [the Manual][uwfm], but it *is* documented on
page 37 of [the DECUS submission][uwfd]. Briefly, it acts like a `GOTO`
command where the jump is only made if keyboard input is detected.

The following program counts upward continuously until a key is pressed,
then it prints how many iterations it ran:

    01.10 SET I = 0
    01.20 YNCR I
    01.30 LOGICAL BRANCH 01.20 ; TYPE I !

The DECUS document suggests using this feature to turn the keyboard into
a giant "switch register," allowing the user to control the behavior of
a long-running program without stopping to wait for user input. Can you
say "video games"?


## <a id="lexit"></a>`LOGICAL EXIT`

Another addition to our version of U/W FOCAL as compared to the version
documented in [the Manual][uwfm] is `LOGICAL EXIT` which immediately
exits U/W FOCAL and returns you to OS/8, just as if you had hit
<kbd>Ctrl-C</kbd>.


## <a id="fsf"></a>FOCAL Statement Functions

This appears to be the same thing [the Manual][uwfm] calls Program
Defined Functions. Therefore, you may look on the material beginning on
page 63 of [the DECUS submission][uwfd] as just another take on the same
issue. Some of the examples are more enlightening than the one in the
manual. The first example in the DECUS submission, `F(EXP)`, is more
robustly coded than the same function in the Manual; comparing the two
is instructive.


## `FRA` Built-In Function

This function is not documented in [the Manual][uwfm], but it is
documented on page 60 of [the DECUS submission][uwfd]. It allows you to
set up random access to a file, storing numbers in various raw binary
formats directly to the file as if it were a large in-memory array.

If you've been trying to use the `FCOM` function but have run into the
limit on the size of a PDP-8 core memory field, switching to `FRA` is
the logical next step.


## <a id="front-panel"></a>Front Panel Differences

Whenever [the 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 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 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][uwfd]
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


## <a id="diffs"></a>Differences Between U/W FOCAL and Other FOCALs

The [DECUS submission for U/W FOCAL][uwfd] 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="rationale"></a>Why Did We Write This?

[The Manual][uwfm] is well written as far as it goes, but there are
gaps:

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. Those not familiar with the differences can therefore be
    confused by some of its directions.

1.  There are considerations in our simulated PiDP-8/I world that simply
    did not apply to those running U/W FOCAL on the real hardware.

1.  There are multiple versions of U/W FOCAL; the version covered by
    [the Manual][uwfm] isn't the one we actually ship.  Our two
    [other][uwfr] primary [sources][uwfd] also do not cover exactly the
    version of U/W FOCAL we ship.

1.  It inspires questions in the reader's mind without providing an
    answer.  Whether this was intentional — with the author intending
    that the user answer these questions on his own — or otherwise, some
    of these questions we felt needed answering here within the PiDP-8/I
    U/W FOCAL documentation.

This document is our attempt to fill these gaps and to supplement those
other documents.  [Extensions and corrections][hack] are welcome.


## <a id="references"></a>References

The primary sources for this supplement are:

*   [U/W FOCAL Manual][uwfm], October 1978, by Jim van Zee of the
    University of Washington.

*   [U/W FOCAL reference cards][uwfr] from the U/W FOCAL distribution,
    approximately contemporaneous with the Manual, but clearly for a
    different version of U/W FOCAL than is documented in the Manual.

*   [DECUS Submission for U/W FOCAL][uwfd], also by van Zee, from August
    1978.
    
    This document describes the OS/8 version of U/W FOCAL rather than
    the paper tape version described by [the Manual][uwfm] we use as our
    primary document here within the PiDP-8/I project. We chose to
    convert the Manual to Markdown rather than this DECUS submission
    because the DECUS document's scan is terrible, resulting in nearly
    worthless OCR output; we *really* did not want to retype the whole
    thing! Additionally, we think the Manual is a better tutorial than
    the DECUS submission, though the DECUS submission is perhaps a
    better reference text.

[df8]:  http://www.ibiblio.org/pub/academic/computer-science/history/pdp-8/FOCL69%20Files/DEC-08-AJAD-D.pdf
[f71]:  http://svn.so-much-stuff.com/svn/trunk/pdp8/src/decus/focal8-177/
[hack]: https://tangentsoft.com/pidp8i/doc/trunk/HACKERS.md#patches
[uwfd]: http://www.pdp8.net/pdp8cgi/query_docs/view.pl?id=191
[uwfm]: https://tangentsoft.com/pidp8i/doc/trunk/doc/uwfocal-manual.md
[uwfr]: https://tangentsoft.com/pidp8i/doc/trunk/doc/uwfocal-refcards.md


### <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 I=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 I=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 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
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
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







-
-
+
+

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+

-
+

-
+

-
-
+
+














-
-
+
+

-
-
-
-
-
+
+
+
+
+
+

-
-
+
+







-
+

+
+





-
+









## 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
    NEW OR OLD--NEW
    FILE NAME--PEP001.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
    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
    READY
    RUN

	PAL001  BA    4A    
    PEP001  BA    5A

	TOTAL:  xxxxxxx
    TOTAL:  xxxxxxx

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

If you get a 2-letter code from BASIC in response to your `RUN` command,
it means you have an error in the program. See the BASIC section of the
OS/8 Handbook for a decoding guide.


## How to Use the Assembly Language Examples

For each PAL8 assembly program in `examples/*.pal`, there are two
additional files:
For each PAL8 assembly program in `src/asm/*.pal` or `examples/*.pal`,
the build process produces several output files:

| Extension | Meaning
-----------------------------
| `*.pal`   | the PAL8 assembly source code for the program
| `*.lst`   | the human-readable assembler output
| `*.pt`    | the machine-readable assembler output (RIM format)
| Extension       | Meaning
| --------------- | ---------------
| `*.pal`         | the PAL8 assembly source code for the program; input to the process
| `obj/*.lst`     | the human-readable assembler output
| `bin/*-pal.pt`  | the machine-readable assembler output (RIM format)
| `boot/*.script` | a SIMH-readable version of the assembled code

There are three ways to run these on your PiDP-8/I, each starting with
one of the above three files:
Each of those files has a corresponding way of getting the example
running in the simulator:

1.  Transcribe the assembly program text to a file within a PDP-8
    operating system and assemble it inside the simulator.

2.  Toggle the program in from the front panel. I can recommend this
    method only for very short programs.

3.  Copy the `*.pt` file to a USB stick and use the PiDP-8/I's
3.  Copy the `*-pal.pt` file to a USB stick and use the PiDP-8/I's
    [automatic media mounting feature][howto]. This is the fastest method.

4.  Boot SIMH with the example in core, running the program immediately.

I cover each of these options below, in the same order as the list
above.


## Option 1: Transcribing the Assembly Code into an OS/8 Session
### Option 1: Transcribing the Assembly Code into an OS/8 Session

To transcribe [`examples/add.pal`][pal] into the OS/8 simulation on a
PiDP-8/I:

    .R EDIT
    *ADD.PA<

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







-
+



-
-
-
-
+
+
+
+
+


-
+







`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:
    *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.
`PIP` that the text input from the terminal (`TTY:`) is finished, then
Escape to return to OS/8 from PIP. 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.


## Option 2: Toggling a Programs in Via the Front Panel
### Option 2: Toggling Programs in Via the Front Panel

After building the PiDP-8/I software, each of the `examples/*.pal` files
is assembled by `palbart` which writes out a human-readable listing file
to `obj/*.lst`, named after the source file.

Take [`obj/add.lst`][lst] as an example, in which you will find three
columns of numbers on the code-bearing lines:
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
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







-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+




-
+







The first number refers to the corresponding line number in
[`add.pal`][pal], the second is a PDP-8 memory address, and the third is
the value stored at that address.

To toggle the `add` program in, press `Stop` to halt the processor, then
twiddle the switches like so:

| Set SR Switches To... | Then Toggle...
|------------------------------------------------
| 000 010 000 000       | `Load Add`
| 111 011 000 000       | `Dep`
| 001 010 000 101       | `Dep`
| 001 010 000 110       | `Dep`
| 011 010 000 111       | `Dep`
| 111 100 000 010       | `Dep`
| 000 000 000 010       | `Dep`
| 000 000 000 011       | `Dep`
| Set SR Switches To... | Then Toggle... | Because...
|--------------------------------------- | ----------
| 000 010 000 000       | `Load Add`     | 000010000000 is binary for octal 0200, the program's start address
| 111 011 000 000       | `Dep`          | the first instruction, 7300 octal, is 111011000000 in binary
| 001 010 000 101       | `Dep`          | 1205 octal in binary
| 001 010 000 110       | `Dep`          | etc, etc.
| 011 010 000 111       | `Dep`          |
| 111 100 000 010       | `Dep`          |
| 000 000 000 010       | `Dep`          |
| 000 000 000 011       | `Dep`          |

To run it, repeat the first step in the table above, loading the
program's starting address (0200) first into the switch register (SR)
and then into the PDP-8's program counter (PC) via `Load Add`. Then
toggle `Start` to run the program. If you then:
toggle `Start` to run the program.

If you then toggle 000 010 000 111 into SR, press `Load Add` followed by
`Exam`, you should see 000 000 000 101 in the third row of lights, the
bit pattern for five at memory location 0207, that being the correct
answer for "2 + 3" in the expected location, which is what we expected
`add.pal` to do. You could load that address back up again and `Dep` a
different value into that location, then start the program over again at
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
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







-
+


-
+














-
+


















+
+
+
+
+
+
+
+
+
+
+
+
+













Beware that this program does not contain an explicit value for memory
location 0207 at the start, but it does overwrite this location with the
answer, since location `C` is defined as having the address just after
the last value you entered via SR above, 0206. That is the source of the
"07" in the lower two digits of the fourth instruction, 3207.


## Option 3: Loading a Program from Paper Tape
### Option 3: Loading a Program from Paper Tape

The `palbart` assembly process described above also produces paper tape
output files in RIM format in `bin/*.pt`.
output files in RIM format in `bin/-pal*.pt`.

The simplest way to load these assembly examples into your PiDP-8/I is
to copy each such file to a USB stick, one file per stick.  Then, load
the paper tape into the simulated PDP-8/I's core memory.

The following is distilled from the [How to Use the PiDP-8/I][howto]
section of the PiDP-8/I documentation:

1.  Set the IF switches (first white group) to 001, and toggle `Sing
    Step` to reboot the simulator into the high-speed RIM loader. If the
    simulator wasn't already running, restarting the simulator with IF=1
    will achieve the same end as toggling `Sing Step` while it's
    running. Reset the IF switches to 0.

2.  Insert the USB stick containing the `*.pt` file you want to load
2.  Insert the USB stick containing the `*-pal.pt` file you want to load
    into the Pi.

3.  Set the DF switches (first brown group) to 001, then toggle `Sing
    Step` again. This attaches the tape to the high-speed paper tape
    reader peripheral within the PDP-8 simulator. Set DF back to 0.

4.  Set the switch register (SR) to 7756 (111 111 101 110) then press
    `Load Add`, then `Start`.

5.  Hit `Stop`, then reset SR to 0200 (000 010 000 000), that being the
    starting location of these example programs. Press `Load Add`, then
    `Start` to run the program.

There is an SVG template for USB stick labels in the distribution under
the [`labels/`][label] directory, for use if you find yourself creating
long-lived USB sticks. See [`labels/README.md`][lread] for more
information.


### Option 4: Booting SIMH with the Example in Core

As part of the PiDP-8/I software build process, a `boot/*.script` file
for each `examples/*.pal` file is created by translating `obj/*.lst`
into a series of "deposit" commands to SIMH. You can thus load and run
each example at the Linux command line with a command like this:

    $ bin/pidp8i-sim boot/add.script

That runs the `examples/add.pal` program's assembled binary code under
the simulator, just as if you'd loaded it there with option #3 above.


## License

Copyright © 2016-2017 by Warren Young. This document is licensed under
the terms of [the SIMH license][sl].


[lst]:   https://tangentsoft.com/pidp8i/doc/trunk/examples/add.lst
[pal]:   https://tangentsoft.com/pidp8i/doc/trunk/examples/add.pal
[label]: https://tangentsoft.com/pidp8i/dir?ci=trunk&name=labels
[lread]: https://tangentsoft.com/pidp8i/doc/trunk/labels/README.md
[howto]: http://obsolescence.wixsite.com/obsolescence/how-to-use-the-pidp-8
[sl]:    https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md
Added examples/pep001-f2.ft.












1
2
3
4
5
6
7
8
9
10
11
12
+
+
+
+
+
+
+
+
+
+
+
+
      DO 10 I = 3, 999
      IF (IREM(I / 3))  5, 15, 5
5     IF (IREM(I / 5)) 10, 15, 10
15    J = J + I
      IF (J - 1000) 10, 18, 18
18    WRITE (1,19) J
19    FORMAT (I4, ' + ')
      J = 0
10    CONTINUE
      WRITE (1,20) J
20    FORMAT (I4)
      END
Added examples/pep001-f4.ft.




1
2
3
4
+
+
+
+
      DO 10 I = 3, 999
10    IF (MOD(I, 3) .EQ. 0 .OR. MOD(I, 5) .EQ. 0) ITOTAL = ITOTAL + I
      WRITE (4,20) ITOTAL
20    FORMAT (' TOTAL: ', I6)
Added examples/pep001-ire0.c.

























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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#include <init.h>
#include <libc.h>

ire0 (n, d)
{
    while (n > 0) n = n - d;
    return n == 0;
}

main ()
{
    int i, st;
    st = 0;

    for (i = 3; i < 1000; i++) {
        if (ire0 (i, 3) | ire0 (i, 5)) st = st + i;

        if (st > 1000) {
            printf("%d + ", st);
            st = 0;
        }
    }

    printf("%d\n", st);
}
Added examples/pep001-mod.c.



















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#include <init.h>
#include <libc.h>

main ()
{
    int i, st;
    st = 0;

    for (i = 3; i < 1000; i++) {
        if ((i % 3 == 0) | (i % 5 == 0)) st = st + i;

        if (st > 1000) {
            printf("%d + ", st);
            st = 0;
        }
    }

    printf("%d\n", st);
}
Added examples/pep001.fc.







1
2
3
4
5
6
7
+
+
+
+
+
+
+
01.10 SET TOTAL = 0
01.20 FOR I = 3, 999 ; DO -.3
01.30 IF (FRAC(I / 3)) , 1.5
01.40 IF (FRAC(I / 5)) , 1.5, 1.6
01.50 SET TOTAL = TOTAL + I
01.60 NEXT
01.70 TYPE "TOTAL: ", %6, TOTAL, !
Added examples/tratbl.fc.






1
2
3
4
5
6
+
+
+
+
+
+
C Print transcendental function tables.
C Example from DEC FOCAL-8 Manual, July 1969, page 4-2.
01.05 T "     I       SINE     COSINE          LOG             E" !
01.10 FOR I=1,.00001,1.0001; DO 2.05
01.20 QUIT
02.05 T %7.06,I," ",FSIN(I)," ",FCOS<I>," ",%,FLOG[I]," ",FEXP(I),!!
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' ]
Added lib/mkos8/argparser.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/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
import opts

class ArgParser (argparse.ArgumentParser):
  def __init__ (self, allowed_acts):
    # Call parent class ctor to initialize the arg parser
    argparse.ArgumentParser.__init__ (self,
        description = 'Build OS/8 RK05 disk images')

    # Add general-purpose args
    self.add_bool ('-d', '--debug',
            help = 'add extra debugging output, normally suppressed')
    self.add_bool ('-v', '--verbose',
            help = 'verbose SIMH output instead of progress messages')

    # Add arguments corresponding to --*-os8-* configure script options
    for obn, vals in opts.opts.iteritems():
      od = 'dis' if vals[0] else 'en'
      self.add_bool ('--' + od + 'able-' + obn, help = vals[1])

    # Add options that do not exactly mirror configuration options
    self.add_bool ('--disable-lcmod',
            help = 'disable the OS/8 command upcasing patch; best set ' +
                   'when SIMH is set to tti ksr mode')

    # Add trailing "what do do" argument
    self.add_argument (
        'what',
        choices = allowed_acts,
        help    = 'select which RK05 media gets built; default is "all"',
        nargs   = argparse.REMAINDER)

    # Finish initializing
    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/pidp8i/__init__.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# -*- coding: utf-8 -*-
########################################################################
# __init__.py.in - pidp8i module initialization
#
# This file is a *.in file only because dirs.py.in is in this directory,
# and both *.in outputs need to land in the same directory for Python
# to find it.  That happens naturally when building in-tree, but we
# need this bit of *.in trickery when building out-of-tree.
#
# 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__ = [ 'dirs', 'ips' ]
Added lib/pidp8i/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
68
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# -*- 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   = os.path.join (build, "bin/")
log   = os.path.join (build, "obj/")
media = os.path.join (src, "media/")
os8mi = os.path.join (media, "os8/")    # mkos8 inputs
os8mo = bin                             # mkos8 outputs

# 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 lib/pidp8i/ips.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# -*- coding: utf-8 -*-
########################################################################
# ips.py.in - Defines the default instructions per second value.
#
# This file is a .in file purely to provide the default version of
# ips.py which can be appended to by running bin/teco-pi-demo -b on
# the target hardware to provide a better local IPS value.
#
# 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.
########################################################################

# Approximate IPS rate for a PDP-8/I based on one fact:
#
# 1. The core cycle time of a PDP-8/I is 1.5 µs.
#
# ...and one assumption:
#
# 2. PDP-8 systems execute instructions at varying rates from roughly 1
#    core cycle time up to many core cycle times.  Most of the core
#    PDP-8 instructions not involving I/O take 1-2 cycles, with I/O
#    taking longer, so we assume that the average for typical real
#    world PDP-8 code will average to 2 cycles per instruction.
#
# Thus, this value is the inverted cycle time divided by 2.
pdp8i = 333333

# A Raspberry Pi B+ running pidp8i-sim unthrottled executes PDP-8 code
# faster than a real PDP-8/I by the factor given, so that running the
# teco-pi-demo benchmkark (-b) it shows a factor of about 1.0.  This
# gives us the lowest possible IPS value short of throttling the sim,
# underclocking the Pi, or starving the simulator of CPU power.
raspberry_pi_b_plus = pdp8i * 5.6

# Initial value; teco-pi-demo -b appends overrides for this
current = raspberry_pi_b_plus

# Benchmark results:
Added lib/simh.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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/python
# -*- coding: utf-8 -*-
########################################################################
# simh/__init__.py - A wrapper class around pexpect for communicating
#   with an instance of the PiDP-8/I SIMH simulator running OS/8.
#
#   See ../doc/class-simh.md for a usage tutorial.
#
# 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.
########################################################################

import os.path
import pexpect
import pkg_resources
import subprocess
import tempfile
import time

import pidp8i

class simh: 
  # pexpect object instance, set by ctor
  _child = None

  # Constant used by os8_kbd_delay, assembled in stages:
  #
  # 1. PDP-8 RS-232 bits per character: 7-bit ASCII plus necessary
  #    start, stop, and parity bits.
  #
  # 2. The ratio of the instructions per second ratios of a PDP-8/I to
  #    that of the host hardware running the simulator.  The former is
  #    an approximate value; see lib/pidp8i/ips.py.in for the value and
  #    its defense.  The latter is either the IPS rate for:
  #
  #    a) a Raspberry Pi Model B+, that being the slowest host system
  #       we run this simulator on; or
  #
  #    b) the IPS rate of the actual host hardware if you have run the
  #       "bin/teco-pi-demo -b" benchmark on it.
  #
  # 2. The fact that real PDP-8s ran OS/8 reliably at 300 bps, and have
  #    been claimed to get flaky as early as 600 bps by some.  (Others
  #    claim to have run them up to 9,600 bps.)
  #
  # 3. The "safe BPS" value is the fastest bit per second speed actual
  #    PDP-8 hardware was known to run OS/8 terminal I/O at.  In this
  #    case, it is the high-speed tape reader.
  #
  #    TODO: We may be able to increase this.
  #
  #    We have one report that OS/8 was tested with terminals up to
  #    about ~600 bps before becoming unreliable.
  #
  #    We have another report that OS/8 could run under ETOS with
  #    9,600 bps terminals, but we don't know if that tells us anything
  #    about OS/8 running without the ETOS multitasking hardware.
  #
  # 4. Given above, calculate safe characters per second for host HW.
  #
  # 5. Invert to get seconds per character, that being the delay value.
  _bpc = 7 + 1 + 1 + 1                                       # [1]
  _ips_ratio = float (pidp8i.ips.current) / pidp8i.ips.pdp8i # [2]
  _pdp8i_safe_bps = 300                                      # [3]
  _host_safe_cps = _pdp8i_safe_bps * _ips_ratio / _bpc       # [4]
  _os8_kbd_delay = 1 / _host_safe_cps                        # [5]


  #### ctor ############################################################
  # The first parameter must be given as the parent of bin/pidp8i-sim.
  #
  # The second defaults to false, meaning that a failure to lock the
  # GPIO for the caller's exclusive use is a fatal error.  If you pass
  # True instead, we just complain if the GPIO is already locked and
  # move on.  This tolerant mode is appropriate for programs that need
  # the simulator alone, not actually the PiDP-8/I front panel display.
  
  def __init__ (self, basedir, ignore_gpio_lock = False):
    # Start the simulator instance
    self._child = pexpect.spawn(basedir + '/bin/pidp8i-sim')

    # Turn off pexpect's default inter-send() delay.  We add our own as
    # necessary.  The conditional tracks an API change between 3 and 4.
    pev4 = (pkg_resources.get_distribution("pexpect").parsed_version >
            pkg_resources.parse_version("4.0"))
    self._child.delaybeforesend = None if pev4 else 0

    # Wait for either an error or the simulator's configuration line.
    if not self.try_wait (\
        '^PiDP-8/I [a-z].*\[.*\]', \
        'Failed to lock /dev/gpiomem', timeout = 3):
      if ignore_gpio_lock:
        print "WARNING: Failed to lock GPIO for exclusive use.  Won't use front panel."
      else:
        raise RuntimeError ('GPIO locked')


  #### back_to_cmd ######################################################
  # Pause the simulation and return to the SIMH command prompt when the
  # simulated software emits the given prompt string.  Typically used to
  # wait for OS/8 to finish running a command so we can do something
  # down at the SIMH layer instead.

  def back_to_cmd (self, prompt):
    self._child.expect ("\n%s$" % prompt)
    self.os8_kbd_delay ()
    self._child.sendcontrol ('e')


  #### os8_get_file ####################################################
  # Rough inverse of os8_send_file.
  #
  # Both paths must be given and are used literally.  (Contrast our
  # inverse, where the destinatinon file name is produced from the
  # source if not given.)
  #
  # When this function is called to pull a file sent by our inverse, the
  # conversion should be lossless except for the transforms done by our
  # underlying utility tools, such as the LF -> CR+LF done by txt2ptp
  # but not undone by ptp2txt.
  #
  # Entry context should be inside OS/8.  Exit context is inside OS/8.

  def os8_get_file (self, intname, extname):
    # Attach a blank paper tape to the simulator.
    ptf = tempfile.NamedTemporaryFile (suffix = '.pt', delete = False)
    ptf.close ()
    ptn = ptf.name
    self.back_to_cmd ('\\.')
    self.send_cmd ('attach ptp ' + ptn)

    # Punch internal file to external paper tape image
    self.os8_restart ()
    self.os8_send_cmd ('\\.', 'PUNCH ' + intname);
    self.back_to_cmd ('\\.')        # wait for transfer to finish

    # Convert text file from SIMH paper tape format
    tool = os.path.join (pidp8i.dirs.build, 'bin', 'ptp2txt')
    self.send_cmd ('detach ptp')
    subprocess.call (tool + ' < ' +  ptn + ' > ' + extname, shell = True)

    # Return to OS/8, just because that's where we were on entry, so we
    # should not change that.
    self.os8_restart ()


  #### 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,
  # though we can scale it based on how much faster this host is than a
  # real PDP-8.  See the constants above for the calculation.
  def os8_kbd_delay (self):
    time.sleep (self._os8_kbd_delay)


  #### os8_send_cmd ####################################################
  # 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_send_cmd (self, prompt, line):
    self._child.expect ("\n%s$" % prompt)
    self.os8_send_line (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_send_cmd.

  def os8_send_ctrl (self, char):
    self.os8_kbd_delay ()
    self._child.sendcontrol (char[0].lower ())


  #### os8_send_file ###################################################
  # Send a copy of a local text file to OS/8.  The local path may
  # contain directory components, but the remote must not, of course.
  #
  # If the destination file name is not uppercase, it will be so forced.
  #
  # If the destination file name is not given, it is taken as the
  # basename of the source file name.
  #
  # The file is sent via the SIMH paper tape device through PIP in its
  # default ASCII mode, rather than character by character for two reasons:
  #
  # 1. It's faster.  It runs as fast as the simulator can process the
  #    I/O instructions, without any os8_kbd_delay() hooey.
  #
  # 2. It allows lowercase input regardless of the way the simulator is
  #    configured.  ASCII is ASCII.
  #
  # Entry context should be inside OS/8.  Exit context is inside OS/8.

  def os8_send_file (self, source, dest = None):
    # Create path and file names not given
    bns = os.path.basename (source)
    if dest == None: dest = bns
    dest = dest.upper ()

    # Convert text file to SIMH paper tape format
    bdir = pidp8i.dirs.build
    pt   = os.path.join (bdir, 'obj', bns + '.pt')
    tool = os.path.join (bdir, 'bin', 'txt2ptp')
    subprocess.call (tool + ' < ' + source + ' > ' + pt, shell = True)

    # Paper tape created, so attach it read-only and copy it in.  We're
    # relying on txt2ptp to insert the Ctrl-Z EOF marker at the end of
    # the file.
    self.back_to_cmd ('\\.')
    self.send_cmd ('attach -r ptr ' + pt)
    self.os8_restart ()
    self.os8_send_cmd ('\\.', 'R PIP')
    self.os8_send_cmd ('\*', dest + '<PTR:')
    self._child.expect ('\\^')
    self.os8_send_ctrl ('[')      # finish transfer
    self._child.expect ('\\*')
    self.os8_send_ctrl ('[')      # exit PIP


  #### os8_send_line ###################################################
  # Core of os8_send_cmd.  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 (self, line):
    self.os8_send_str (line)
    self._child.send ("\r")


  #### os8_send_str ########################################################
  # Core of os8_send_line.  Also used by code that needs to send text
  # "blind" to OS/8, without expecting a prompt and without a CR, as
  # when driving TECO.

  def os8_send_str (self, str):
    for i in xrange (0, len (str)):
      self._child.send (str[i])
      self.os8_kbd_delay ()


  #### 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 (self):
    self.send_cmd ("go 7600")


  #### os8_squish ########################################################
  # Wraps the OS/8 SQUISH command for a given device.

  def os8_squish (self, device):
    self.os8_send_cmd ("\.", "SQUISH " + device + ":")
    self.os8_kbd_delay ()
    self._child.send ("Y\r");


  #### quit ############################################################
  # Quits the simulator and waits for it to exit

  def quit (self):
    self.send_cmd ("q")
    self._child.expect (pexpect.EOF)


  #### read_tail #######################################################
  # Watch for a literal string, then get what follows on that line.

  def read_tail (self, head, timeout = -1):
    self._child.expect_exact ([head], timeout)
    return self._child.readline ()


  #### send_cmd ########################################################
  # Wait for a SIMH command prompt and then send the given command

  def send_cmd (self, cmd):
    self._child.expect ("sim> $")
    self._child.sendline (cmd)


  #### send_line #######################################################
  # Sends the given line "blind", without waiting for a prompt.

  def send_line (self, line):
    self._child.sendline (line)


  #### set_logfile #####################################################

  def set_logfile (self, lf):
    self._child.logfile = lf


  #### spin ############################################################
  # Let child run without asking anything more from it, with an optional
  # timeout value.  If no value is given, lets child run indefinitely.

  def spin (self, timeout = None):
    self._child.expect (pexpect.EOF, timeout = timeout)


  #### try_wait ########################################################
  # Wait for one of two strings to come back from SIMH, returning true
  # if we get the first, else false.

  def try_wait (self, success, failure, timeout = -1):
    return self._child.expect ([success, failure], timeout = timeout) == 0
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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/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')
sys.path.insert (0, os.getcwd () + '/lib')

# Remaining Python core modules
import re
import shutil
import subprocess

# Our local modules
from mkos8 import *
from pidp8i import *
from simh import *


#### globals and constants #############################################

# 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

# Name of the RK05 disk image files we create
_bin_rk05 = "os8v3d-bin.rk05"
_src_rk05 = "os8v3d-src.rk05"
_patched_rk05 = "os8v3d-patched.rk05"

# Parser regexps used in patcher
_com_os8_parse_str = "^\.([a-zA-Z]+)\s?(.*)$"
_com_os8_parse = re.compile(_com_os8_parse_str)
_com_split_str = "^([a-zA-Z]+)\s?(.*)$"
_com_split_parse = re.compile(_com_split_str)
_odt_parse_str = "^([0-7]+)\s?/\s?(\S+)\s+([0-7;]+)"
_odt_parse = re.compile(_odt_parse_str)

#### mkos8_abort  ######################################################
# Fatal error. Abort mkos8

def mkos8_abort (s):
  print "Fatal Error.  Cannot proceed."
  s.quit ()
  exit (-1)


#### check_exists ######################################################
# Check existence of all files needed

def check_exists (s, image_copyins):
  for copyin in image_copyins:
    image = copyin[1]
    image_path = dirs.os8mi + image
    if (not os.path.isfile(image_path)):
        print "Required file: " + image_path + " not found."
        mkos8_abort(s)
    # 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 (s, copyin0, copyin1, debug):
  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: s.send_cmd ("attach -r dt0 " + dirs.os8mi + copyin0[1])
  if copyin1: s.send_cmd ("attach -r dt1 " + dirs.os8mi + copyin1[1])

  s.os8_restart()

  if copyin0:
    if progmsg: print copyin0[2]
    if copyin0[3]:                    # We have specific files to do.
      for file_copyin in copyin0[3]:
        s.os8_send_cmd ("\\.", "COPY " + file_copyin[0] + "<DTA0:" + file_copyin[1])
    else:
      s.os8_send_cmd ("\\.", "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]:
        s.os8_send_cmd ("\\.", "COPY " + file_copyin[0] + "<DTA1:" + file_copyin[1])
    else:
      s.os8_send_cmd ("\\.", "COPY " + copyin1[0] + "<DTA1:*.*")

  s.back_to_cmd("\\.")

  if copyin0: s.send_cmd ("detach dt0")
  if copyin1: s.send_cmd ("detach dt1")


#### do_all_copyins ####################################################

def do_all_copyins (s, copyins, debug):
  pair_idx = 0
  pair_ct = int(len(copyins) / 2)
  while pair_idx < pair_ct:
    copyin_pair(s, copyins[pair_idx * 2], copyins[pair_idx * 2 + 1], debug)
    pair_idx += 1
  if pair_ct * 2 < len(copyins):
    copyin_pair(s, copyins[len(copyins) - 1], None, debug)

  
#### 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 (s, args):
  ro_boot_tape =       "al-4711c-ba-os8-v3d-1.1978.tu56"
  ro_boot_tape_path =  dirs.os8mi + ro_boot_tape
  driver_tape =        "al-4712c-ba-os8-v3d-2.1978.tu56"
  driver_tape_path =   dirs.os8mi + driver_tape
  local_tape =         "local.tu56"
  local_tape_path =    dirs.os8mi + local_tape
  
  boot_dt_path = dirs.os8mo + "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]

  fiv_copyins = [
    ['RKA0:', "subsys/al-4549d-ba-fr4-v3d-1.1978.tu56",
     "Installing FORTRAN IV ...", None],
  ]

  # Specify files explicitly. There's a README.TX we don't want copied.
  # We may decide to leave FUTIL.SV out.
  macrel_files = [
    ['SYS:', 'MACREL.SV'],
    ['SYS:', 'LINK.SV'],
    ['SYS:', 'KREF.SV'],
    ['SYS:', 'OVRDRV.MA'],
    ['SYS:', 'FUTIL.SV']
  ]

  macrel_copyin = ['RKA0:', "al-5642c-ba-macrel-v2-futil-v8b-by-hand.tu56",
                   "Installing MACREL V2 and new FUTIL V8B ...", macrel_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],
    ]

  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_advent:     bin_copyins.append(advent_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_fortran_iv: bin_copyins.extend(fiv_copyins)
  if not args.disable_macrel:     bin_copyins.append(macrel_copyin)

  if local_files != []:
    local_copyins = ['RKA0:', local_tape, local_stat_str, local_files]
    bin_copyins.append(local_copyins)

  if args.enable_focal69:
    bin_copyins.append(focal69_copyin)
  if not args.disable_uwfocal:
    bin_copyins.append(uwfocal_copyin)

  check_exists(s, special_bin_copyins)
  check_exists(s, bin_copyins)

  print "Generating " + _bin_rk05 + " from " + str(len(bin_copyins) + 2) + \
        " source tapes..."

  image_path = dirs.os8mo + _bin_rk05
  if os.path.isfile(image_path):
    save_path = dirs.bin + _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..."
  try:
    shutil.copyfile(ro_boot_tape_path, boot_dt_path)
  except shutil.Error as e:
    print "Bootable DECtape copy failed with error: " + e
    mkos8_abort (s)
  except IOError as e:
    print "Bootable DECtape copy failed with IOError: " + e
    mkos8_abort (s)

  s.send_cmd ("attach rk0 " + image_path)
  s.send_cmd ("attach dt0 " + boot_dt_path)
  s.send_cmd ("attach -r dt1 " + driver_tape_path)

  if progmsg: print "Performing config with BUILD..."
  s.send_cmd ("boot dt0")

  # By default, we're going to have an INIT.CM script, but we don't want
  # to set it up this early for two reasons.  First, we don't want the
  # "boot rk0" commands below to emit this message, since its text
  # varies depending on the host system, which would create unnecessary
  # differences in our output over time.  Second, OS/8 is alleged not to
  # always build properly if INIT is enabled in these early stages.
  s.os8_send_cmd ("\\.", "SET SYS NO INIT")

  s.os8_send_cmd ("\\.", "RUN SYS BUILD")
  s.os8_send_cmd ("\$", "LOAD DTA1:RK8ESY.BN")
  s.os8_send_cmd ("\$", "LOAD DTA1:PT8E.BN")
  s.os8_send_cmd ("\$", "DELETE SYS")
  s.os8_send_cmd ("\$", "SYSTEM RK8E")
  s.os8_send_cmd ("\$", "DELETE RXA1")
  s.os8_send_cmd ("\$", "INSERT PT8E,PTR")
  s.os8_send_cmd ("\$", "INSERT PT8E,PTP")
  s.os8_send_cmd ("\$", "DELETE RKA0")
  s.os8_send_cmd ("\$", "DELETE RKB0")
  s.os8_send_cmd ("\$", "INSERT RK8E,RKA0,RKB0")
  s.os8_send_cmd ("\$", "INSERT RK05,RKA2,RKB2")
  s.os8_send_cmd ("\$", "DELETE DTA0")
  s.os8_send_cmd ("\$", "INSERT TC,DTA0")
  s.os8_send_cmd ("\$", "DSK RK8E:RKB0")
  s.os8_send_cmd ("\$", "PRINT")
  s.os8_send_cmd ("\$", "BOOT")
  s.os8_send_cmd ("WRITE ZERO DIRECT\?", "Y")
  # SYS is now RK0; if we SAVE SYS we will overwrite with wrong BUILD.SV
  s.os8_send_cmd ("\\.", "SAVE DTA0 BUILD")
  s.back_to_cmd("\\.")
  
  if progmsg: print "Copying OS/8 system files from TU56 source to RK05 image..."
  if progmsg: print "Copying in system tape 1 of 2..."
  s.send_cmd ("boot dt0")
  s.os8_send_cmd ("\\.", "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..."
  # s.os8_send_cmd ("\\.", "COPY RKA0:<DTA1:*.*")
  if progmsg: print "Copying in help files..."
  s.os8_send_cmd ("\\.", "COPY RKA0:<DTA1:*.HL")

  if args.disable_fortran_ii:
    files = [ 'RKA0:{0}.SV'.format(f) for f in [
        'FORT', 'LIBSET', 'SABR'
      ]
    ]
    s.os8_send_cmd ("\\.", 'DEL ' + ','.join (files))
  else:
    if progmsg: print "Installing FORTRAN II libraries..."
    s.os8_send_cmd ("\\.", "COPY RKA0:<DTA1:*.RL")

  s.os8_send_cmd ("\\.", "ZERO RKB0:")      # must precede subsys/* copies

  s.back_to_cmd("\\.")
  
  if progmsg: print "Deleting bootable copy of DECtape image and"
  if progmsg: print "rebooting into freshly-built RK05 OS/8 system..."
  s.send_cmd ("detach dt0")
  s.send_cmd ("detach dt1")
  s.send_cmd ("boot rk0")     # must do: boot media just went away
  s.back_to_cmd("\\.")
  os.remove(boot_dt_path)

  if progmsg: print "Performing remaining copies/installs..."
  do_all_copyins (s, bin_copyins, args.debug)

  # Any further initialization of installed software is done here.
  s.send_cmd ("boot rk0")

  if progmsg: print "Configuring for 3-column DIRECT listings..."
  s.os8_send_cmd ("\\.", "SET TTY COL 3")

  if not args.disable_crt:
    # NO SCOPE mode is the default on distribution tapes.
    if progmsg: print "Configuring scope-style rubout processing..."
    s.os8_send_cmd ("\\.", "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."
    s.os8_send_cmd ("\\.", "SUBMIT SYS:LCSYS.BI")
    if progmsg: print "Patching OS/8 BASIC to cope with lower case input"
    s.os8_send_cmd ("\\.", "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?
  #
  # Note that we do not read this file from dirs.os8mi: it is either an
  # output of autosetup or overwritten by test-mkos8 in the *build*
  # tree, so that when building out of tree, reading from os8mi would
  # either give an empty file if the source tree was "clean" or a
  # mismatched version of the file from the soruce tree rather than the
  # one we want, which is in the *build* tree.
  if progmsg: print "Setting INIT message..."
  s.os8_send_file ('media/os8/init.tx')
  s.os8_send_cmd ("\\.", "CREATE INIT.CM")
  s.os8_send_cmd ("#", "A")
  s.os8_send_line("TYPE INIT.TX")
  s.os8_send_ctrl('L')
  s.os8_send_cmd ("#", "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.
    s.os8_send_cmd ("\\.", "SET SYS INIT")

  # Finish up
  if progmsg: print "Cleaning up..."
  s.os8_squish ("SYS")
  s.os8_squish ("DSK")
  s.back_to_cmd("\\.")
  s.send_cmd ("detach rk0")



#### make_src ##########################################################
# Source-disk version of make_bin() above.

def make_src (s, 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 (s, src_copyins)

  print "Generating " + _src_rk05 + " from " + str(len(src_copyins)) + \
        " source tapes..."

  bin_path = dirs.os8mo + _bin_rk05
  if (not os.path.isfile(bin_path)):
    print _bin_rk05 + " is needed to build src.  Creating..."
    make_bin(args)

  src_path = dirs.os8mo + _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:"
  s.send_cmd ("attach rk0 " + bin_path)
  s.send_cmd ("attach rk1 " + src_path)

  s.send_cmd ("boot rk0")
  
  s.os8_send_cmd ("\\.", "ZERO RKA1:")
  s.os8_send_cmd ("\\.", "ZERO RKB1:")

  s.back_to_cmd("\\.")

  do_all_copyins (s, src_copyins, args.debug)

  if progmsg: print "Cleaning up..."
  s.send_cmd ("detach rk0")
  s.send_cmd ("detach rk1")


#### parse_odt #########################################################

def parse_odt (s, com, line, debug):
  if debug: print line
  
  if line == "\\c": return "break"
  match = _odt_parse.match(line)
  if match == None:
    print "Aborting because of bad ODT line: " + line
    s.os8_send_ctrl('C')
    return "err"
  loc = match.group(1)
  old_val = match.group(2)
  new_val = match.group(3)
  expect_val_str = "\s?[0-7]{4} "
  
  if debug: print "Loc: " + loc + ", old_val: " + old_val + ", new_val: " + new_val
  s.os8_send_str (loc + "/")
  s._child.expect(expect_val_str)

  if old_val.isdigit():          # We need to check old value
    found_val = s._child.after.strip()
    if found_val != old_val:
      print "Old value: " + found_val + " does not match " + old_val + ". Aborting patch."
      # Abort out of ODT back to the OS/8 Monitor
      s.os8_send_ctrl('C')
      return "err"

  s.os8_send_line (new_val)
  return "cont"


#### futil_exit ########################################################

def futil_exit (s, com, line):
  s.os8_send_line(line)
  return "break"


#### futil_file ########################################################

def futil_file (s, com, line):
  # Redundant re-parse of line but nobody else wants args right now.
  match = _com_split_parse.match(line)
  if match == None:
    print "Aborting because of mal-formed FUTIL FILE command: " + line
    s.os8_send_ctrl('C')
    return "err"
  fname = match.group(2)
  expect_futil_file_str = "\n" + fname + "\s+(.*)$"
  s.os8_send_line (line)
  s._child.expect(expect_futil_file_str)
  result = s._child.after.strip()
  match = _com_split_parse.match(result)
  if match == None:
    print "Aborting because unexpected return status: " + result + " from: " + line
    s.os8_send_ctrl('C')
    return "err"
  if match.group(2).strip() == "LOOKUP FAILED":
    print "Aborting because of FUTIL lookup failure on: " + fname
    s.os8_send_ctrl('C')
    return "err"
  else:
    return "cont"
  
  
#### parse_futil #########################################################
#
# Very simple minded:
# If first char on line is an alpha, run the command.
# If the first char on line is number, do the substitute command.
#
# Substitute command acts like ODT.
# Future version should support the IF construct.
#
# When we encounter the EXIT command, we return success.

def parse_futil (s, com, line, debug):
  futil_specials = {
    "EXIT": futil_exit,
    "FILE": futil_file
  }

  if line[0].isdigit():
    # Treat the line as ODT
    return parse_odt(s, com, line, debug)
  else:
    match = _com_split_parse.match(line)
    if match == None:
      print "Ignoring failed FUTIL command parse of: " + line
      return "cont"
    fcom = match.group(1)
    rest = match.group(2)

    if fcom not in futil_specials:
      # Blind faith and no error checking.
      s.os8_send_line(line)
      return "cont"
    else:
      return futil_specials[fcom](s, fcom, line)


#### do_patch_file #####################################################

def do_patch_file (s, filename, debug):
  global progmsg
  
  try:
    patch_file = open(dirs.os8mi + "patches/" + filename, "r")
  except IOError:
    print filename + " not found. Skipping."
    return -1

  special_commands = {
    "ODT": parse_odt,
    "R": None,               # Get next parser.
    "FUTIL": parse_futil
  }

  inside_a_command = False
  the_command = ""
  the_command_parser = None
  
  for line in patch_file:
    line = line.rstrip()
    if line == "": continue
    elif line[0] == '#': continue     # Ignore Comments
    elif inside_a_command:
      retval = the_command_parser (s, the_command, line, debug)
      if retval == "break":
        inside_a_command = False
        s.os8_send_ctrl('C')
      elif retval == "err":
        patch_file.close()
        return -1
    elif line[0] == '.':        # New OS/8 Command
      match = _com_os8_parse.match(line)
      if match == None:
        print "Aborting patch on failed OS/8 command parse of: " + line
        return -1
      com = match.group(1)
      rest = match.group(2)

      if com in special_commands:
        if com == "R":
          # Run command is special.  Take arg as the command and run it.
          com = rest
        inside_a_command = True
        the_command = com
        the_command_parser = special_commands[com]

      # We carefully separate com and args
      # But don't make much use of that yet.
      if progmsg and debug: print line
      s.os8_send_cmd ("\\.", line[1:])  # Skip Prompt.

  patch_file.close()

  # Done.  Signal success.
  return 0


#### skip_patch ########################################################
# Returns true if the given filename matches one of the regex string
# keys of the given skips dict and the flag value for that key is set.
# See skips definition in make_patch, which calls this.

def skip_patch (fn, skips):
    for p in skips:
        if re.search (p, fn) and skips[p]: return True
    return False

      
#### make_patch ########################################################

def make_patch (s, args):
  global progmsg
  
  manifest_filename = "patch_list.txt"

  # Declare which patches to skip based on patch file name RE patterns
  # and command line options.  All patches not matching these patterns
  # apply to OS/8 files that always get installed.
  skips = {
    '^F4':            args.disable_fortran_iv,
    '^FRTS':          args.disable_fortran_iv,
    '^FUTIL.*v7': not args.disable_macrel,     # MACREL includes FUTIL V8B
    '^SABR':          args.disable_fortran_ii,
  }

  print "Generating " + _patched_rk05 + " from " + _bin_rk05

  image_path = dirs.os8mo + _patched_rk05
  if os.path.isfile(image_path):
    save_path = dirs.bin + _patched_rk05 + ".save"
    print "Pre-existing " + _patched_rk05 + " found.  Saving as " + _patched_rk05 + ".save"
    if os.path.isfile(save_path):
      print "Overwriting old " + _patched_rk05 + ".save"
      os.remove(save_path)
    os.rename(image_path, save_path)

  if progmsg: print "Copying original " +  _bin_rk05 + " to " + _patched_rk05
  try:
    shutil.copyfile(dirs.os8mo + _bin_rk05, image_path)
  except shutil.Error as e:
    print "Creation of " + _patched_rk05 + " failed with error: " + str(e)
    mkos8_abort(s)
  except IOError as e:
    print "Creation of " + _patched_rk05 + " failed with IOError: " + str(e)
    mkos8_abort(s)

  s.send_cmd ("attach rk0 " + image_path)
  s.send_cmd ("boot rk0")         # We're running OS/8. Let's patch!
  
  manifest = open (dirs.os8mi + "patches/" + manifest_filename, "r")

  for line in manifest:
    if line == "": continue
    if line[0] == '#': continue      # Allow commenting out files

    filename = line.strip()
    if skip_patch (filename, skips):
      if progmsg: print "Skipping patch file: " + filename + "."
    else:
      if progmsg: print "Applying patch file: " + filename + "...",
      success = do_patch_file (s, filename, args.debug) == 0
      if progmsg:
        if success:
          print "success."
        else:
          print "FAILED!"
  
  # If we have installed FORTRAN IV, install the patched FORLIB.RL
  # to be found on local.tu56
  if not args.disable_fortran_iv:
    forlib_tape =         "local.tu56"
    forlib_tape_path =    dirs.os8mi + forlib_tape
    if progmsg: print "FORLIB 51.10.1M: Replacing FORLIB.RL with patched version from " + forlib_tape + "..."
    # Normally we'd use check_exists, but failure to find the file is fatal.
    # With a patch, we abort but keep going.
    if (not os.path.isfile(forlib_tape_path)):
      print "Can't find tape with patch FORLIB.RL.  FAILED!"
    else:
      s.back_to_cmd("\\.")         # return to simh and mount the tape.
      s.send_cmd ("attach -r dt0 " + forlib_tape_path)
      s.os8_restart ()            # return to OS/8
      s.os8_send_cmd ("\\.", "DEL SYS:FORLIB.RL")
      s.os8_send_cmd ("\\.", "COPY SYS:<DTA0:FORLIB.RL")
      s.back_to_cmd("\\.")         # return to simh and mount the tape.
      s.send_cmd ("detach dt0")
      s.os8_restart ()            # return to OS/8
      
  # Finish up
  if progmsg: print "Cleaning up..."
  s.os8_squish ("SYS")
  s.os8_squish ("DSK")
  s.back_to_cmd("\\.")
  s.send_cmd ("detach rk0")


#### main ##############################################################
# Program entry point.  Parses the command line and drives the above.

def main ():
  # Parse the command line
  allowed_acts = ["all", "bin", "src", "patch"]
  do_all_acts = ["bin", "src"]    # Just do bin and src if act is "all".
  
  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 do_all_acts:
        acts[this] = True
      break
    acts[act] = True

  # 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
  s = simh (dirs.build, True)
  s.set_logfile (open (dirs.log + 'mkos8-' + first_act + '.log', 'w') \
      if progmsg else os.fdopen (sys.stdout.fileno (), 'w', 0))

  if acts["bin"]: make_bin (s, ap.args)
  if acts["src"]: make_src (s, ap.args)
  if acts["patch"]: make_patch (s, ap.args)
  
  s.quit ()
  if progmsg: print "Done!"


if __name__ == "__main__":
    main()
Added media/os8/.agignore.



1
2
3
+
+
+
*.tu56
*.rk05
*.rk05.save
Added media/os8/.ignore.

1
+
.agignore
Deleted media/os8/al-4549d-ba-fr4-v3d-1.1978.tu56.

cannot compute difference between binary files

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

cannot compute difference between binary files

Added media/os8/al-5642c-ba-macrel-v2-futil-v8b-by-hand.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 @BUILDTS@

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

cannot compute difference between binary files

Deleted media/os8/os8.rk05.

cannot compute difference between binary files

Added media/os8/patches/ABSLDR-21.29.1M-v6C.patch8.




















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Supersedes article dated June/July 1980
# 
# LOADER PROBLEM WITH SAVE IMAGE FILES (BS)
# 
# There is a problem in that Loader does not work with Save Image files.
# The following patch, applied via FUTIL, will correct this problem.

.R FUTIL
SET DEV SYS
SET MODE SAVE
FILE ABSLDR.SV
14105/1757 7000
14106/7004 7000
14107/7110 7000
14110/3757 7000
12200/6602 6603
WRITE
EXIT

# Note that the above patch upgrades ABSLDR from Version 6B to Version 6C,
Added media/os8/patches/BASIC.UF-31.5.1M-V5B.patch8.


















































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# BASIC. UF V5A
# BASIC. UF   INCOMPATIBLE   FROM   OS/8  V3C   (SPR  8-2537  JG)
# BASIC. UF is incompatible with OS/8 V3D BASIC due to PAGE ZERO references
# that were not updated.
# The following binary patch or source change can be made to fix this
# problem.
# This patch upgrades BASIC.UF to V5B.
.GET SYS:BASIC.UF	
.ODT	
3447/1073 1177
3467/4514 4556
3474/4514 4556
3523/4514 4556
3525/1074 1171
3531/1074 1171
3561/1073 1177
3607/4535 4526
3624/4514 4556
3636/4514 4556
3716/1073 1177
3752/4536 4524
4012/4514 4556
4021/1074 1171
# The next line appears in the patch but no such subroutine
# Call is present. Reviewing the source code of the patch:
# This location contains 1273 TAD NSAM
# The patch says change it to 4556  JMS I FIXP
# I think this location should be left unchanged.
# 4044/4514 4556
4060/1073 1177
4067/1077 1172
4160/3064 3056
4162/4534 4557
4163/0307 0311
4207/4535 4526
4216/4514 4556
4225/4514 4556
4275/4514 4556
4312/4514 4556
4403/4514 4556
4433/4514 4556
4444/4514 4556
4451/1073 1177
4476/4556 4573
4504/4514 4556
4507/4514 4556
4513/4514 4556
4532/1073 1177
\c
.SAVE SYS:BASIC.UF
Added media/os8/patches/BATCH-31.23.1M-v7B.patch8.

























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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# OS/8 EXTENSION KIT V3D  Seq 31. 23. 1 M
# BATCH V7A
# MANUAL INTERVENTION REQUIRED ERRONEOUSLY (SR)
# Problem:    The message MANUAL HELP NEEDED is sometimes printed even
# though no use is made of a terminal, paper tape reader or
# card reader in the BATCH stream.  (The message does not
# hurt, the system continues to function properly.)
# Diagnosis:   This problem was fixed in BATCH V5D and V6A for KBM
# commands that called the CD in special mode.  This fix was
# incorporated in BATCH V7A.  However, a similar problem
# exists if a CCL command does a special mode decode.  The
# problem is that routine CDSCN at location 7200 (in the
# field of BATCH) is being called with a 5200 in the AC (the
# special mode indicator), but CDSCN thinks 0 means special
# mode.  The solution is to allow either 0 or 5200 to mean
# special mode.
# Cure:       Install the following patch which upgrades BATCH to V7B:
.GET SYS:BATCH
.ODT
7201/3340 5344
7344/xxxx 1351;7440;1352;3340;5202;2600;5200
7326/4752 7000
1701/0137 0237
\c
.SAVE SYS:BATCH
Added media/os8/patches/BLOAD-31.10.1M-v5B.patch8.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# BLOAD WILL NOT BUILD CCB PROPERLY (JR)
# On some very large programs, BLOAD will not build the CCB properly when the
# /K option is used.  The following patch corrects this:

.GET SYS BLOAD
.ODT
2155/xxxx      0000
2156/xxxx      6203
2157/xxxx      7000
2160/xxxx      1000
2775/2534      2154
3027/6501      6502
\c
.SAVE SYS BLOAD

# This patch upgrades BLOAD to V5B.
Added media/os8/patches/BRTS-31.11.1M-v5B.patch8.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# IOTABLE OVERFLOW (SPR 8-2431 JR)
# Problem: The size of the IOTABLE is 15 (Octal).
# This was misread as 15 (Decimal) making entry #4 overflow into the handler.
# 
# The following patch will fix the problem and upgrades BRTS.SV to V5B.

.GET SYS BRTS
.ODT
2010/ 7106 7104\n
2011/ 7006 1065\n
2012/ 7041 7006\n
2013/ 1065 1065\n
2014/ 7041 7000
1116/ 0301 0302
\c
.SAVE SYS BRTS
Added media/os8/patches/BRTS-31.11.2-O.patch8.













1
2
3
4
5
6
7
8
9
10
11
12
13
+
+
+
+
+
+
+
+
+
+
+
+
+
# BRTS.SV V5B                                                           Supersedes article dated Mar 78
# BASIC PNT FUNCTION (JR)
# Presently the OS/8 BASIC PNT function forces the parity bit on.
# Therefore, it is impossible to output octal codes 000-077.
# The following patch removes this limitation.
.GET SYS BRTS
.ODT
1761/0162 7000
1762/1172 0174
\c
.SAVE SYS BRTS
# This is an optional patch. Optional patches do not change the version
# levels.
Added media/os8/patches/BRTS-31.11.3-O.patch8.














1
2
3
4
5
6
7
8
9
10
11
12
13
14
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# LINE SIZE ON OUTPUT OF BASIC (JR.)
# There seems to be great interest in extending basic output to 133
# characters. The following patch will extend the output line width to
# 132 chars (9 columns of number data). Basic console input will remain
# at 72 chars max - that is not easily patched since the buffer ? other
# code + data.  This restriction does not apply to file I/O - only the
# tty.
.GET SYS BRTS
.ODT
2570/7774 7767
2573/7660 7574
3375/7660 7574
\c
.SA SYS BRTS
Added media/os8/patches/BRTS-31.11.5-x.patch8.




























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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# BRTS.SV V5B Seq. 31.11.5 M
# PATCH TO BRTS FOR ADDRESSING LAB 8/B FUNCTIONS (JR)
# The correct patch to BRTS.SV for proper addressing of LAB 8/E functions for
# 0S/8 BASIC is as follows:
# 1.   Make the patch to BASIC/UF as described in the
# August-September 1978 DSN. Note that, location 4044 contains
# 1273 and not 4514 before being changed to 4556.
# 2.   Patch BRTS.SV. This patch replaces the patch described in
# the OS/8 Language »«ftr»B1!i Hanuiil  (A A-H609A-TA) ,  BASIC
# chapter, page 5-2.
.GET SYS BRTS		
.ODT		
00001/xxxx 5402	FUNCTION
00002/xxxx 4456	
01560/xxxx 3400	INI
01561/xxxx 3454	PLY
01562/xxxx 3473	DLY
01563/xxxx 3600	DIS
01564/xxxx 4000	SAM
01565/xxxx 4077	CLK
01566/xxxx 3542	CLH
01567/xxxx 3522	ADC
01570/xxxx 4400	GET
01571/xxxx 4432	PUT
01572/xxxx 4267	DRI
01573/xxxx 4311	DR0
\c		
.SA SYS BRTS		
Added media/os8/patches/CREF-21.15.1M-v5B.patch8.











1
2
3
4
5
6
7
8
9
10
11
+
+
+
+
+
+
+
+
+
+
+
# Problem:     CREF dies on source files containing a FIXTAB directive.
# Diagnosis:   Patch V4B incorrectly installed into CREF V5A.
# Solution:    Apply the following patch:
.GET SYS:CREF
.ODT
6063/2022 5270
6070/2025 2022;5314
6114/xxxx 1363;1025;3025;3425;2025;5252
2576/0301 0302
\c
.SAVE SYS:CREF
Added media/os8/patches/CREF-21.15.2M-v5C.patch8.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# INPUT   AND   OUTPUT   FILE   SPECIFICATIONS   (RY)
# 
# The  following patch allows you to specify two non-system handlers, where previously
# only one non-system handler and one system handler were required.

.GET SYS CREF
.ODT
0264/1773 1327
0327/xxxx 0000
4234/3352 3752
4352/0000 0327
2576/0302 0303
\c
.SAVE SYS CREF.SV

# This patch upgrades CREF.SV from V5B to V5C.
Added media/os8/patches/DLOG.RA.










































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/
/
/	D L O G
/	- - - -
/
/ VERSION 5A 4-27-77  PT
/
/LOGE(X)
/
/X=2^N*F
/
/LOGE(X)
 /= N*LOGE(2)+LOGE(F)
/
/
	SECT	DLOG
	JA	#DALOG
	DPCHK
/
/IF X<=0 - IT IS AN ERROR
	EXTERN	#ARGER
DALERR,	TRAP4	#ARGER
/
	TEXT	+DLOG  +
DALXR,	SETX	XRDAL
	SETB	BPDAL
BPDAL,	F 0.0
XRDAL,	F 0.0
	F 0.0
	ORG 10*3+BPDAL
	FNOP
	JA	DALXR
	0
DALRTN,	JA	.
N,	F 0.0
	F 0.0
F,	F 0.0
	F 0.0
DAL1,	F 1.0
	F 0.0
/
DT7,	7776		/1/7
	2222
	2222
	2222
	2222
	2221
DT6,	7776		/-1/6
	5252
	5252
	5252
	5252
	5252
DT5,	7776		/1/5
	3146
	3146
	3146
	3146
	3146
DT4,	7776		/-1/4
	4000
	0
	0
	0
	0
DT3,	7777		/1/3
	2525
	2525
	2525
	2525
	2524
DT2,	7777		/-1/2
	4000
	0
	0
	0
	0
/
A0,	F 1.84375
	F 0.0
A1,	F 1.65625
	F 0.0
A2,	F 1.500
	F 0.0
A3,	F 1.375
	F 0.0
A4,	F 1.250
	F 0.0
A5,	F 1.1875
	F 0.0
A6,	F 1.09375
	F 0.0
A7,	F 1.03125
	F 0.0
LA0,	0		/.6118015411059928976
	2344
	7603
	2325
	4250
	3144
LA1,	0		/.5045560107523952859
	2011
	2512
	4551
	3503
	7657
LA2,	7777		/.4054651081081643810
	3174
	6217
	5457
	7141
	1370
LA3,	7777		/.3184537311185346147
	2430
	3057
	0207
	0573
	0232
LA4,	7776		/.2231435513142097553
	3443
	7737
	0746
	5150
	4146
LA5,	7776		/.1718502569266592214
	2577
	6301
	6051
	7117
	2356
LA6,	7775		/.08961215868968712374
	2674
	1512
	1271
	2655
	1272
LA7,	7773		/.030771658666753687
	3740
	5154
	1636
	0313
	7764
D16,	F 16.0
	F 0.0
D8,	F 8.0
	F 0.0
CUM,	F 0.0
	F 0.0
DLOGE2,	0
	2613
	4413
	7676
	4347
	5715
/
/PICK UP X
	BASE	0
#DALOG,	STARTD
	FLDA	10*3
	FSTA	DALRTN
	FLDA	0
	SETX	XRDAL
	SETB	BPDAL
	BASE	BPDAL
	LDX	1,1
	FSTA	BPDAL
	FLDA%	BPDAL,1		/ADDRESS
	FSTA	BPDAL
	STARTE
	FLDA%	BPDAL		/AND X
	JLE	DALERR		/X <= 0 IS ERROR
	FSUB	DAL1		/SUB 1.0
	JNE	DALA
	FCLA			/LOG(1)=0
	JA	DALRTN
/
DALA,	FLDA%	BPDAL		/GET ARGUMENT BACK
	FSTA	XRDAL		/STORE AT X
/EXPONENT STORED IN XR0
/MANTISSA STORED IN XR1-5
/PICK UP EXP + MULTIPLY BY LOGE(2)
/
	XTA	0
	FMUL	DLOGE2
	FSTA	N		/N*LOGE(2)
/XRDAL IS NOW FRACTION IN RANGE .5<=F<1.0
/COMPUTE LOG(F) BY
/LOG(F)=LOG(A(K1)*A(K2)...(F))-(LOG(A(K1))+
/	LOG(A(K2))...)
/FIT F IN A 1/16 RANGE
/I.E. 1/2-9/16,9/16-10/16,ETC.
/MULTIPLY F BY APPROPRIATE A(K) MULTIPLIER
/KEEP RUNNING SUM OF LOG(A(K))
/CONTINUE UNTIL F>1

/
	LDX	0,0
	FLDA	XRDAL
	FSTA	F
	FCLA
	FSTA	CUM
DALB,	FLDA	F
	FMUL	D16	/16 REAL PARTS
	FSUB	D8		/NEED JUST 8
	ATX	1
	FLDA	A0,1		/GET MULTIPLIER
	FMULM	F
	FLDA	LA0,1		/ADD LOG(A(K)) TO SUM
	FADDM	CUM
	FLDA	F
	FSUB	DAL1
	JLT	DALB
/NOW F>1. USE TAYLOR SERIES
/LOG(T)=Z-(Z^2)/2+(Z^3)/3+...  WHERE Z=T-1
	FLDA	F
	FSUB	DAL1	/F-1.0
	FSTA	F
	FMUL	DT7
	FADD	DT6
	FMUL	F
	FADD	DT5
	FMUL	F
	FADD	DT4
	FMUL	F
	FADD	DT3
	FMUL	F
	FADD	DT2
	FMUL	F
	FADD	DAL1
	FMUL	F
	FSUB	CUM
	FADD	N
	JA	DALRTN

Added media/os8/patches/EDIT-21.17.1M-v12B.patch8.












1
2
3
4
5
6
7
8
9
10
11
12
+
+
+
+
+
+
+
+
+
+
+
+
# Problem: EDIT sometimes loses its TAB characters in multi-page files
# if file is exited on other than the last page.
#
# Solution: Make the following patch to OS/8 V3D of EDIT.SV.
# This raises the patch level to V12B

.GET SYS:EDIT.SV
.ODT
2014/1301 1357
2372/0301 0302
\c
.SAVE SYS:EDIT.SV
Added media/os8/patches/EDIT-21.17.2M-v12C.patch8.



















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# EDIT V12B 21.17.2 M
# EDIT Q COMMAND AFTER L COMMAND (RY)
#
# Problem:     When a "Q" command is issued after an "L" command, the output
#              is sent to the terminal instead of the file.
#
# Diagnosis:   The "Q" command has bypassed the code that resets the
#              variable 'OUTDEV in its attempt to clear the variable
#              'TABIND'
#
# Solution:    Install the following patch, that upgrades EDIT to Version
#              12C.
.GET SYS:EDIT
.ODT
2372/0302      0303
2014/nnnn     2774
2774/nnnn      3112;5776;1301
\c
.SAVE SYS:EDIT
Added media/os8/patches/EDIT-21.17.3M-v12D.patch8.













1
2
3
4
5
6
7
8
9
10
11
12
13
+
+
+
+
+
+
+
+
+
+
+
+
+
# EDIT V12C Seq 21.17.3 M
# EDIT Q COMMAND PATCH (RY)
#
# Problem:     The "Q" command does not output the first page of a file.
#
# Solution:    Install the following patch and EDIT is upgraded to Version
#              12D.
.GET SYS:EDIT
.ODT
1360/5304      5301
2372/0303      0304
\c
.SAVE SYS:EDIT
Added media/os8/patches/EDIT-21.17.4M-V12C.patch8.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# EDIT.SV   "V"   OPTION   WILL   NOT   WORK   WITH   LPT   (RY)
# 
# The "V" option of EDIT.SV will not work when the LPT interface is the
# parallel port of the DKC8-AA I/O option board. Install the following patch
# and   EDIT   will   be   upgraded   from  V12B   to   V12C.
# Correction: This is the 4th patch in the sequence, and upgrades EDIT
# from V12D to V12E

.GET SYS EDIT
.ODT
2713/6666 5374
2774/xxxx 7040;6574;5314
2714/6661 6570
2372/0304 0305
\c
.SAVE SYS EDIT.SV
Added media/os8/patches/F4-21.1.2M-v4B.patch8.























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Updated in Dec/Jan 1978 SEQ 21.1.2 N to correct referencee to F4 not FRTS.
# F4.SV V4A        Seq 2 M
# EQUIVALENCE STATEMENT (MH)
# The EQUIVALENCE statement does not always work correctly in OS/8 FORTRAN IV V3D.
# Install the following patch to correct the problem.
.GET SYS F4.SV	
.ODT	
2067/1471	1367
2070/1071	5363
2163/****	2071
2164/****	7000
2165/****	1071
2166/****	5271
2167/****	2
1130/6401	6402
\c
.SAVE SYS F4.SV	
.GET SYS PASS3.SV	
.ODT	
712/6401	6402
\c
.SAVE SYS PASS3.SV	
# This patch upgrades F4.SV to V4B.
Added media/os8/patches/F4-51.3.1M-v4C.patch8.














1
2
3
4
5
6
7
8
9
10
11
12
13
14
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Supersedes article dated Mar 78
# FORTRAN COMPILER FAILS TO RECOGNIZE " AS AN ERROR (SPR 8-2428 JB)
# Problem:  The F4 compiler fails to recognize the double quotes (") as
# an incorrect character in a subroutine call argument.
# Instead, it generates an argueless call.
# Solution:  The following patch corrects this problem and should be
# installed:
.GET SYS F4
.ODT
3343/ 7440 7640
1130/ 6402  6403
\c
.SAVE SYS F4
# This patch corrects this problem and upgrades the F4.SV to V4C.
Added media/os8/patches/F4-51.3.2M-v4x.patch8.













1
2
3
4
5
6
7
8
9
10
11
12
13
+
+
+
+
+
+
+
+
+
+
+
+
+
# F4 V4A
# FORTRAN COMPILER NOT RECOGNIZING SYNTAX ERROR (JB)
# F4 compiler does not recognize syntax error in type declaration
# statement.
# The following patch will resolve this situation.
.GET SYS F4.SV
.ODT
2520/4557   5326
2521/2200   7000
2522/5600   7000
2541/5320   5326
\c
.SAVE SYS   F4.SV
Added media/os8/patches/FORLIB-51.10.1M.patch8.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# FORTRAN IV DLOG PATCH (JR)
# 
# There is a problem with DLOG where it could not handle numbers smaller than
# 1.E-018 correctly.  The following patch fixes this problem.
# 
# Make a source change to DLOG.RA using either EDIT or TECO.   Replace
# this line:
#
# Published patch has a typo.  There is no "EADD" function. It is "FADD".
# DALA,     EADD      DAL1      /ADD BACK
#     with :
# DALA,     FLDA%     BPDAL     /GET ARGUMENT BACK
# .R RALF
# *DLOG.RL<DLOG.RA
# .R LIBRA
# *FORLIB.RL<FORLIB.RL,DLOG.RL/Z/R
Added media/os8/patches/FOTP-21.19.1M-V9B.patch8.
























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# FOTP V9A  Seq 21. 19. 1 M
# INCORRECT DIRECTORY VALIDATION  (SR)
# Problem:
# If a device contains many files and the directory contains no
# additional information words (i.e., no dates) then FOTP may
# think the directory is invalid.
# Diagnosis:
# FOTP checks the validity of a directory by several means.  One
# method is a range check on the number of file entries in the
# first directory segment.  If the directory had been built with
# 0 additional information words (/Z=100), then the segment can
# contain more entries than FOTP believes is possible.
# Solution:
# Modify FOTP so that it permits a directory segment with as many
# as 71 entries.  To do this, install the following patch:
.GET SYS:FOTP
.ODT
12375/7700     7671
14346/7700     7671
15036/7101     7102
\c
.SAVE SYS:FOTP
# This patch upgrades FOTP to Version 9B.
# Most users are not affected by this patch.
Added media/os8/patches/FRTS-51.3.3-O.patch8.




































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# OS/8 FORTRAN IV KIT V3D  Seq 51.3.3 0
# FRTS. SV V5
# FORTRAN RUNTIME SYSTEM 2 PAGE HANDLER (JM)
# The FORTRAN Run-Time System has worked wih the TD8E 2 page system handler.
# To add it to work with other 2 page system handlers and, in particular the
# RL01, the following patch must be installed.
.GET SYS:FRTS
.ODT
12675/7001 0342
12742/XXXX 7770
17526/0000 0000
17527/3763 3307
17530/1763 1333
17531/3762 3363
17532/1763 5346
17533/7001 7635
17534/3761 1570
17535/5726 7710
17546/XXXX 1763
17547/XXXX 1334
17550/XXXX 7100
17551/XXXX 1335
17552/XXXX 7630
17553/XXXX 5360
17554/XXXX 1763
17555/XXXX 0341
17556/XXXX 1307
17557/XXXX 3763
17560/XXXX 2363
17561/7642 5346
17562/7727 5726
17563/7721 0
17566/0212 6220
\c
.SAVE SYS:FRTS
This patch is optional and does not change the patch level.
Added media/os8/patches/FUTIL-31.21.1M-v7B.patch8.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# The patch given below upgrades FUTIL V7A to V7B.  It corrects the
# following problems:
# 1. Typing CTRL/U crashes FUTIL if the current partiall-typed
# line contains a semicolon.
# 2. Overlay mapping (in SAVE mode) is not done correctly.
.GET SYS FUTIL
.ODT
0310/3523 3536
0333/1523 1536
3342/3357 3362
3343/1357 1362
3351/1361 1357
3354/2357 2362
12520/0100 0200
\c
.SAVE SYS FUTIL
Added media/os8/patches/FUTIL-31.21.2M-v7D.patch8.




















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# OS/8  EXTENSION   KIT  V3D 
# FUTIL  V7B    Seq   31.21.2 M
# PATCH  TO  FIX   'SHOW  CCB'   AND MAPPING  OF   'CD'   MODULES   (SPR 8-2550)
# The  following  contains  the  corrections  to both of  these  problems.
.GET SYS FUTIL
.ODT
12024/3242 5345
12025/1642 7650
12026/7450 5236
12027/5236 1642
12145/XXXX 3242
12146/XXXX 1242
12147/XXXX 1351
12150/XXXX 5225
12151/XXXX 1175
12075/7040 7000
12520/0200 0400
\c
.SAVE SYS:FUTIL
# This patch upgrades FUTIL to V7D.
Added media/os8/patches/FUTIL-31.21.3O.patch8.

















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# -237 PATCH (SR)
# The XS format in FUTIL is useful for dumping data stored in excess 240
# packed six bit.  This is the format of PAL12 six bit strings.
# Users who would prefer the XS format to dump data stored in excess 237
# packed six bit may install the following optional patch:

.GET SYS:FUTIL
.ODT
5355/4532 4770
5360/4532 4770
5370/xxxx 5761
5762/xxxx 1365;4532;5761;7777
\c
.SAVE SYS FUTIL

# Excess 237 packed six bit is the format used by COS data files.
# Optional patches do not change FUTIL's version number.
Added media/os8/patches/LINK-40.2.1M-v1D.patch8.


















































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# THIS PATCH FIXES THE FOLLOWING BUGS:
# 1. THE WRONG 2 WORD PAIR IN THE MEMORY CONTROL BLOCK FOR A FULL 4K
# MEMORY IMAGE.
# 2. ACCESSING THE WRONG GST SYMBOL WHEN USING LOADER CODE 11 (POP AND
# STORE INTO GST).
# 3. LINK MEMORY ALLOCATION BUG WHEN DEALING WITH RESTRICTED PROGRAM
# SECTIONS.
# THIS PATCH UPGRADES LINK TO VlD
.R EPIC
*LINK.SV</1<esc>
R,1\r
O,111\r
0340/ 440\r
W\r

R,3\r
O,352 \r
5356/ 5364\r
O,355\r
1443/ 6211\n
5747/ 5363\r
O,363\r
0000/ 1443 \n
0000/ 5747\r
W\r

R,24\r
O,222\r
2441/ 7000\r
W\r

R,25\r
O,305\r
3162/ 3055\r
O,336\r
1562/ 1455 \n
3562/ 3455\r
W\r

R,46\r
O,254\r
7001/ 5271\r
O,271\r
0000/ 7450 \n
0000/ 1370\n
0000/ 7001\n
0000/ 5255\r
W\r

\c
Added media/os8/patches/LINK-40.2.2M-v1E.patch8.






















































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# THIS PATCH FIXES THE FOLLOWING BUGS:
# 1. ERROR IN COMPUTING JOB STATUS WORD FOR MEMORY IMAGE
# 2. EAD TECHNIQUE IN COMBINING MULTIPLE NULL PAGES
# 3. BAD FIELD NUMBER IN OVERLAY DATA TABLE FOR OVRDRV
# 4. BAD OVERLAY/LEVEL LENGTH IN OVERLAY DATA TABLE FOR OVRDRV
# 
# THIS PATCH UPGRADES LINK TO VIE
.R EPIC
*LINK.SV</1<esc>
R,1\r
O,111\r
0440/ 0540\r
W\r

R,24\r
O,31\r
1227/ 1775\n
3776/ 7450\n
1775/ 5257\n
7450/ 7041\n
5257/ 3244\n
7041/ 7344\n
3244/ 3137\n
7344/ 1227\n
3137/ 3776\n
O,56\r
5242/ 5240\r
O,314\r
1035/ 5715\n
3035/ 5065\r
W\r

R,25\r
O,57\r
7450/ 1043\n
5265/ 7450\n
1043/ 5271\r
O,64\r
7100/ 5246\r
O,70\r
7630/ 400 \n
5305/ 7100\n
1341/ 1043\n
1371/ 1270\n
7041/ 3043\n
3043/ 7430\n
7100/ 5337\r
O,100\r
7041/ 7161\r
O,102\r
7620/ 7670\r
O,111\r
5332/ 5246\r
O,117\r
7100/ 1341\n
1341/ 7041\n
1044/ 7100\n
7620/ 5273\r
W\r

R,31\r
O,2\r
7041/ 376\n
1063/ 7041\n
0376/ 1063\r
O,65 \r
0000/ 3275\n
0000/ 1275\n
0000/ 7040\n
0000/ 35\n
0000/ 1275\n
0000/ 3035\n
0000/ 5674\n
0000/ 2716\r
W\r

R,42\r
O,50\r
1042/ 1542\n
7041/ 7001\n
1542/ 5360\n
7710/ 7700\r
O,160\r
0000/ 7110\n
0000/ 7041\n
0000/ 1042\n
0000/ 5253\r
W\r

R,46\r
O,111\r
7112/ 7000\n
7010/ 7000\r
O,125\r
7001/ 7000\n
7110/ 7000\r
O,264 \r
1041/ 1042\r
O,273\r
7001/ 3042\n
5255/ 1042\n
0000/ 7001\n
0000/ 5255\r
W\r

R,50\r
O,214\r
1411/ 5243\r
O,243\r
0000/ 1411\n
0000/ 7001\n
0000/ 7110\n
0000/ 5215\r
W\r

?
# (Question mark, if output by EPIC here, may be ignored)
\c
Added media/os8/patches/LINK-40.2.3M-v1F.patch8.
















































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# THIS PATCH FIXES THE FOLLOWING BUGS:
# 1. LOSS OF DATA IN THE .SV IMAGE
# 2. BUG IN COMPUTATION OF RSECTS ON THE SAME PAGE AS ANOTHER PROGRAM
# 3. BAD MEMORY CONTROL BLOCKS WHEN USING /M OPTION
# 4. NEW SYSTEM ERROR (2760) FOR BAD SYMBOL TYPE
# THIS PATCH UPGRADES LINK TO VlF
.R EPIC
*LINK.SV</1<esc>
R,1\r
O,111\r
0540/ 640\r
W\r

R,24\r
O,357\r
0000/4514\n
0000/5357\r
W\r

R,25\r
O,147(CP)
1101/2757\n
1101/2757\n
W\r

R,26\r
O,74\r
5235/5250\r
O,301\r
7041/7161\r
O,303\r
7740/7660\r
W\r

R,46\r
O,35 \r
0374/7200\r
W\r

R,47\r
O,326 \r
7041/5342\n
7100/7141\r
O,340\r
1411/5741\n
7041/5524\n
7100/7440\n
1410/5327\n
7640/5765\n
7420/7670\r
0,365 \r
0000/5537(DR)
0,367 \r
1401/1201\r
W\r

R,50\r
O,124\r
0000/7320\n
0000/1411\n
0000/7440\n
0000/7041\n
0000/3013\n
0000/1410\n
0000/7450\n
0000/7020\n
0000/1013\n
0000/5736\n
0000/5345\n
0000/2010\n
0000/2010\n
0000/5742\n
0000/5355\n
W\r

?
# (Question mark, if output by EPIC here, may be ignored)
\c


Added media/os8/patches/LQP-21.49.1M-vB.patch8.


















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Supersedes article dated March 1978
# 
# LQP01 HANDLER FAILS TO RECOGNIZE TABS (SPR 8-2441  JM)
# 
# The LQP.BN handler as distributed does not recognize the TABS character.  Any
# listing or text that uses TABS will not be printed correctly.
# 
# The method to patch this problem is through the BUILD procedure.  This will
# fix this problem and maintain the correct version in the saved copy of BUILD.
# This is done as follows:

.R BUILD
LOAD DSK:LQP.BN #  (OR DEVICE THAT DISTRIBUTED LQP.BN IS ON)
$ALTER LQP,324=7640
$BOOT
.SAVE SYS BUILD

# This patch corrects this problem and upgrades the LQP.BN to VB.
Added media/os8/patches/MACREL-40.5.1M-v1D.patch8.



















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# IN MACREL V1C, SECT LENGTHS ARE WRONG FOR SECTS WHICH CONTAIN RELOC
# STATEMENTS AND CURRENT PAGE LITERALS. THE FOLLOWING PATCH CORRECTS
# THIS PROBLEM AND UPGRADES MACREL TO VlD.
.GET SYS:MACREL
.ODT
305/3303 5325
320/4772 5772
372/5546 5547
322/3036 1044;1106;5721
356/1044 4321
363/1044 4321
5455/0000 303
5546/0000 364
5552/5746 5356
5555/1044 5762;1655;3036;1655;5746;363
325/0000 1106;3303;5306
13136/0303 304
\c
.SAVE SYS:MACREL
Added media/os8/patches/MACREL-40.5.2M-v1E.patch8.















































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# PATCH VIE TO MACREL (SR)
# THIS PATCH FIXES THE FOLLOWING BUGS:
# 1. THE 'DEVICE' DIRECTIVE SOMETIMES PRODUCES RELOCATABLE TEXT.  THE
# TEXT PRODUCED SHOULD ALWAYS BE ABSOLUTE.
# 2. AN UNKNOWN KEYWORD AFTER A .LIST DIRECTIVE FAILS TO PRODUCE THE
# ERROR MESSAGE:  "UNKNOWN LIST CONDITION".
# 3. > AT THE END OF A DECLARATION CAUSES AN ERRONEOUS ERROR MESSAGE TO
# BE PRINTED.
# 4. CERTAIN DIRECTIVES DO NOT PRINT IN THE LISTING WHEN THEY CONTAIN A
# SYNTACTIC ERROR.
# 5. DATES LATER THAN DECEMBER 31, 1977 DO NOT PRINT CORRECTLY IN THE
# HEADER LINE.
# 6. THE THIRD CHARACTER IN OS/8 TEXT PACKING IS COMING OUT AS THE FIRST
# CHARACTER OF THE TRIPLE.
# 7. EVERY THIRD CHARACTER IN OS/8 TEXT PACKING IS IGNORING THE
# 7BIT/8BIT ENABLE CONDITION.
# THIS PATCH UPGRADES MACREL TO VIE.
#
.GET SYS:MACOVR
.ODT
11531/1033  5345
11545/xxxx  3030;1033;5332
12541/xxxx  1026;1347;7650;5763;1026;5740;7702
12637/1026  4763
12763/xxxx  2140
10763/1302  1333
10553/7775  7774
10751/7006  0173;4767
10767/5762  2156
10557/1137  7440;1764;7006;7006;5756;2332
\c
.SAVE SYS:MACOVR

.GET SYS:MACREL
.ODT
13136/0304 305
0242/5566 5301
7220/0177 4621;2112
2113/xxxx 0177;3725;1726;7012;7012;0327;1725;3725;2312;5712;7342;7777;30
\c
.SAVE SYS:MACREL

.GET SYS:MACERR
.ODT
21335/5544 5542
\c
.SAVE SYS:MACERR
Added media/os8/patches/MCPIP-21.21M-v6B.patch8.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Problem: New files created by MCPIP on a magtape or cassette after
# January 1, 1978 will be entered with the wrong creation date.
# (New files created on other devices will have their dates correct.)
# MCPIP fails to look at the date extension bits when writing a magtape
# or cassette header record.
# 
# The following patch fixes this problem and upgrades MCPIP to V6B:

.GET SYS MCPIP
.ODT
14031/ 6601 6602
07464/ 1360 5342
07542/ xxxx 3302;4747;1302;1360;5265;6137
06140/ xxxx 1745;7012;7012;0346;5737;7777;0030
\c
.SAVE SYS MCPIP
Added media/os8/patches/MSBAT-31.22.1M-v3B.patch8.


















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# MSBAT V3A  SEQ. 31.22.1 M
# DIM STATEMENT NOT WORKING IN MSBAT (SR)
# PROBLEM:
# MSBAT V3A is converting a 'DIM' punch in a mark sense
# card into a dimension statement.  This statement is not
# recognized by BASIC.
# DIAGNOSOS:
# The punch should be translated to 'DIM', not
# 'DIMENSION'.
# SOLUTION:
# Apply the following patch:
.GET SYS:MSBAT
.ODT
4015/1505 1500
3671/6301 6302
\c
.SAVE SYS:MSBAT
# This patch upgrades MSBAT to V3B.
Added media/os8/patches/OVRDRV-40.6.1M-v1B-8srccom.










































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
PATCH V1B TO OVRDRV.MA (ES)
THE FOLLOWING IS A SOURCE COMPARE OF THE DISTRIBUTED OVERLAY-DRIVER
(OVRDRV.MA) VERSES THE CHANGES NECESSARY TO MAKE IT COMPATIBLE WITH
THE BUG FIXES CORRECTED IN PATCH IE OF LINK.SV.  THE CHANGES TO
OVRDRV.MA ONLY HAVE TO BE MADE IF YOU ARE USING THE LINK OVERLAY
STRUCTURE.

SRCCOM V4A
1)       /OVRDRV - OVERLAY DRIVER
2)       /OVRDRV - OVERLAY DRIVER
1)001    /COPYRIGHT (C) 1977 BY DIGITAL EQUIPMENT CORPORATION
1)       /
****
2)001    /COPYRIGHT (C) 1977,1978 BY DIGITAL EQUIPMENT CORPORATION
2)       /
********
1)002    /VIA
1)       /THIS SECT IS TWO LOCATIONS AND CONTAINS THE TRANSFER VECTOR
         TO SWAPER
****
2)002    /V1B
2)        /THIS SECT IS TWO LOCATIONS AND CONTAINS THE TRANSFER VECTOR
         TO SWAPER
********
1)002    SWAP,    6101			      /VERSION NUMBER
1)                DCA	   AC		      /SAVE CALLING AC
****
2*002    SWAP,    6102			      /VERSION NUMBER
2)                DCA	   AC		      /SAVE CALLING AC
********
1)003		  ISZ      TEMP		      /TIMES (THE NUMBER OF THE
		  	   		      OVERLAY)

1)                JMP      .-2
1)       LOAD2,   TAD I	   RELBLK	      /PLUS (RELATIVE BLOCK OF
	 	      	   		      LEVEL)
****
2)003             JMP I	   .+1		      /TIMES (THE NUMBER OF THE
		  	   		      OVERLAY)


2)                PATCH
2)       LOAD2,   TAD I	   RELBLK	      /PLUS (RELATIVE BLOCK OF
	 	      	   		      LEVEL)
********
1)003             RTR			      /POSITION
1)004             TAD I    LENGTH	      /GET LENGTH
1)                RTR
1)		  RTR
1)		  RTR
1)		  DCA      REDCNT	      /FORM CONTROL WORD
****
2)003	  	  RAR			      /POSITION
2)004		  TAD I   LENGTH	      /GET LENGTH
2)		  RTL
2)		  RTL
2)		  RTL
2)		  DCA     REDCNT              /FORM CONTROL WORD
********
1)006    /THIS AREA CONTAINS OVERLAY DATA FOR MAIN AND THE 7 LEVELS
****
2)006	 /PATCH TO FIX BLOCK POSITION CALCULATION
2)       PATCH,  IAC                         /CONVERT PAGES TO BLOCKS
2)	 	 CLL RAF
2)		 DCA     PTEMP
2)		 TAD     PTEMP               /MULTIPLY BLOCK LENGTH
2)		 ISZ I   PPNT                /BY OVEFLAY NUMBER
2)		 JMP     .-2
2)		 JMP I   .+1
2)		 LOAD 2
2)	 PPNT,   TEMP
2)	 PTEMP,  0
2)	 /THIS AREA CONTAINS OVERLAY DATA FOR MAIN AND THE 7 LEVELS
********
Added media/os8/patches/PAL8-21.22.1M-v10B.patch8.

















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# PAL8 V10A  Seq 21. 22. 1 M
# INCORRECT CORE SIZE ROUTINE (SR)
# Problem:      PALS's core size routine fails on certain machines such as
# an 8K PDP-8L.
# Diagnosis:    There was no room in PAL8 for the standard size routine.
# The routine used by PAL8 includes a typo.
# TAD I (FLD4 should read TAD I FLD4 and,
# DCA I (FLD4 should read DCA I FLD4.
# Solution:     Apply the following patch:
.GET SYS:PAL8
.ODT
5675/3755 3715
5677/1755 1715
1533/0301 302
\c
.SAVE SYS:PAL8
# This patch upgrades PAL8 to V10B.
Added media/os8/patches/PAL8-21.22.2M-v10C.patch8.





















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# PAL8 V10B  Seq 21. 22. 2 M
# ERRONEOUS LINK GENERATION NOTED ON PAGE DIRECTIVE (SR)
# Problem:
# An apostrophe (') is sometimes printed to the right of
# the binary column on the listing line for a PAGE
# directive (pseudo-op).  Such a symbol is meaningless
# in this case.
# Diagnosis:
# This occurs if the previous line had a link generated
# in it.  The PAGE directive code fails to reset the
# links generated flag (LININD).
# Cure:
# Install the following patch which upgrades PAL8 to V10C:

.GET SYS:PAL8
.ODT
0463/5550 5367
0567/xxxx 3070;5550
1533/0302 0303
\c
.SAVE SYS PAL8
Added media/os8/patches/PAL8-21.22.3M-v10D.patch8.


















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# EXPUNGE PATCH TO PAL8 (DBB)
# 
# Problem: A symbol definition following an EXPUNGE directive causes a
# symbol table exceeded (SE) error in some cases.
# 
# Diagnosis: The EXPUNGE directive code in PAL8 improperly counts the
# number of symbols that it deletes from the symbol table.
# 
# Solution: Install the following patch which upgrades PAL8 to V10D.

.GET SYS PAL8
.ODT
1471/4572 5373;7106;7650;5307
1573/xxxx 4572;1020;5272
1533/0303 0304
\c
.SAVE SYS PAL8

Added media/os8/patches/PAL8-21.22.4M.patch8.
































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Supersedes article dated June/July 1980
# TABS ARE TRANSLATED INCORRECTLY (KW)
# 
# Problem: Tabs following a label (PAL8) that are 6 characters long
# are translated into spaces incorrectly.
# 
# Diagnosis: This problem is evident only when CREF listings are
# requested during a PAL8 assembly.
# 
# Solution: The following optional patch will correct this problem,
# improving the appearance of CREF output listings. Since
# this patch is optional, no change has been made to the
# version number of PAL8.SV.

.R FUTIL
SET DEV SYS
SET MODE SAVE
FILE PAL8.SV
1363/7440 5764
1364/4527 4321
4321/XXXX 7200
4322/XXXX 1363
4323/XXXX 4762
4324/XXXX 7200
4325/XXXX 4761
4326/XXXX 5760
4360/XXXX 1372
4361/XXXX 0735
4362/XXXX 1000
4363/XXXX 0211
WRITE
EXIT
Added media/os8/patches/PIP-21.23.1M-v12B.patch8.





























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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# PIP /Y OPTION DOES NOT WORK PROPERLY WHEN TRANSFERRING A SYSTEM HEAD FROM A
# DEVICE WHICH IS NOT CO-RESIDENT WITH SYS. (ES)
# Problem:  PIP /Y option always reads absolute block 7 of the input devices
# even if the input is from a file.
# The following patch:
# 1) Fixes /Y problems in PIP
# 2) Adds RL01 Disk to PIP device tables
# 3) Upgrades patch level to V12B (from V11A - an incorrect revision code)
.GET SYS PIP
.ODT
13626/0000 17
13631/0000 4027
16012/1373 7001
16051/4764 7410
16112/3400 1600
16134/3243 7200
16151/7400 5600
16152/0016 0007
16153/7774 7770
16167/6254 6210
16210/1377 3032
16211/3020 1766
16212/1376 3024
16213/3344 5763
16622/6161 6162
16623/0100 0200
\c
.SAVE SYS PIP

Added media/os8/patches/PIP10-21.24.1M-v3B.patch8.




















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
PIP10 V3A Seq 21.24.1 M
Supersedes article dated Dec 78/Jan 79
DATE '78 PATCH TO PIP10 (RY)
Problem:     When PIP10 creates a new file on a PDP-10 DECtape, the file
gets the wrong date.
Diagnois:    PIP10 does not understand about the new OS/8 extended date
bits for today's date.
Cure:        Install the following patch which fixes this problem until 1984.
.GET SYS:PIP10
.ODT
2612/1023 4760;1116
2760/nnnn 1554
1555/nnnn 1765;0176;7112;7012;3116;1023;0156;5754;7777
4320/6301 6302
\c
.SAVE SYS:PIP10
# This patch corrects the above problem and upgrades PIP10 to
# V3B.
# This article replaces and supercedes the same sequence number
# published in the Dec 78-Jan 79 DSN.
Added media/os8/patches/SABR-21.91.1M-v18B.patch8.


















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# SABR V18A  Seq 21.91.1 M
# LINE BUFFER PROBLEM IN SABR (DBB)
# Problem: When a SABR input line generates code across a page boundary,
# SABR puts out an altered version of the line next to the code
# at the beginning of the next page in the listing file.
# Diagnosis: The routine to enable the buffer to be placed in the listing
# file is in error.
# Solution:    Install the following patch.
.GET SYS SABR
.ODT
14755/6201 1362;5757;5375;3362;5754
15375/0000 6201;5777;6173;
16173/0000 3777;6211;5776;4760;1646
17033/7001 7002
\c
.SAVE SYS SABR
# The underlined  text  is  computer  generated.   This  patch
# corrects the problem and upgrades SABR to Version 18B.
Added media/os8/patches/SET-21.26.1M-v1C.patch8.




















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# The commands SET TTY SCOPE and SET SYS INIT
# ruin systems which use a 2-page system
# handler.
# SET modifies block 0 of the system device to
# handle these commands.  However, in the case
# of two-page system handlers, the correct
# image is stored in block 66 instead.
# Install the following patch which creates
# once-only code in SET VlB to check for a
# 2-page system handler and modify itself
# accordingly.
.GET SYS:SET
.ODT
0507/6102 6103
0240/5632 5357
0357/xxxx 1765;1366;7650;4767;3362;5632;7612;7775;4400
4401/0000 1207;3460;2202;2210;5201;5600;0066;7774
0060/xxxx 0713;0725;3444;3453
\c
.SAVE SYS:SET
Added media/os8/patches/SET-21.26.2M-v1D.patch8.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# If SET is run directly ( .R SET) and TTY SCOPE
# had previously been SET, then rubouts to a SET
# command fail to properly erase characters from
# the screen.
# The scope rubout code is failing to send the
# initial backspace character to the display
# terminal.
# Install the following patch to SET VIC:
# This patch upgrades SET to VlD.
.GET SYS:SET
.ODT
0507/6103 6104
2337/5274 5370
2370/xxxx 1056;7650;5274;5271
\c
.SAVE SYS:SET
Added media/os8/patches/SET-21.26.3M-v1E.patch8.





















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# SET VlD
# Seq 21. 26. 3 M
# 1 of 1
# PARSING OF - IN TTY WIDTH OPTION (SR)
# Problem:   The valid command SET TTY WIDTH=80 results in a syntax error
# Diagnosis:  The code that checks for an optional equals sign is failing to
# advance the character scan pointer.
# Solution:  Install the following patch:
.GET SYS:SET
.ODT
5763/5754 4564;7200;5754
0507/6104 6105
\c
.SAVE SYS:SET
# This patch upgrades SET to Version VIE.  In both the commands
#       SET TTY WIDTH=n
# and
#       SET CDR CODE=02x
# the equals sign is optional, and may be replaced by one or more spaces.
# If the equals sign is specified, it may also be optionally preceded or
# followed by spaces.
Added media/os8/patches/TECO-31.20.01O.patch8.













1
2
3
4
5
6
7
8
9
10
11
12
13
+
+
+
+
+
+
+
+
+
+
+
+
+
# In OS/8 TECO VS, the default value for the EU flag is 0
# (unless .SET TTY SCOPE has been specified to the KBM
# in which case the default value is -1).
# 
# Users who wish to permanently change the default value to be -1
# (no case flagging) may install the following patch:

.GET SYS TECO
.ODT
4576/ 0000 7777
2245/ 7650 7200
\c
.SAVE SYS TECO
Added media/os8/patches/TECO-31.20.02O.patch8.


















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# CHANGING THE DEFAULT EH VALUE for one-line error printouts.
# 
# In OS/8 TECO V5, as on the PDP-10 and PDP-11, the default value of the
# EH flag is 0, which is the same as 2. This value causes the one-line
# form of an error message to print upon encountering an error.
# This could be annoying to experienced users with slow terminals.
# Naturally, you can change this at run-time by executing the
# 1EH command which causes only the 3-character error message cod
# to print thereafter.
# 
# Users who wish to cause the default value of the EH flag
# to be permanently set to 1, can install the following patch:

.GET SYS TECO
.ODT
4572/0000 1
\c
.SAVE SYS TECO
Added media/os8/patches/TECO-31.20.03O.patch8.




























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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# REMOVING YANK PROTECTION (SR)
# 
# Probably the most common way to lose data in TECO is to accidentally
# type 'Y' when you meant to type 'T'. OS/8 TECO VS has included
# what is known as Yank protection. Yank protection causes the error message
# 
# ?YCA Y Command Aborted
# 
# to be produced any time that the following 3 conditions are met:
#     (a) The current command is Y or
#     (b) There is text in the text buffer
#     (c) There is an output file open.
#     
# If all these conditions are met, it is presumed that you are about
# to lose some important data, and so the command is rejected.
# Note that the Yank is always legal if the text buffer is empty
# or if no output file is open. If you really want to do the Yank,
# you can always type HKY, a command which will always succeed.
# 
# Users who do not wish to have Yank protection can patch it out of TECO
# by installiing the following patch:

.GET SYS TECO
.ODT
2032/ 7640 7610

\c
.SAVE SYS TECO
Added media/os8/patches/TECO-31.20.04O.patch8.




















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# SCOPE SUPPORT FOR VT05 USERS
# 
# Version 5 of TECO supports scope terminals (such as VT50, VT52, VT78, etc.)
# by sending appropriate escape sequences to the terminal.
# For example, when typing the immediate mode command ^U during a multi-line
# command string, TECO will physically erase the current line from the screen.
# This support will not work properly on terminals which do not support these
# escape sequences.
# 
# For our users with VT05's, we offer the following patch which will
# permit TECO to work properly on a VT05 terminal:

.GET SYS TECO
.ODT
2771/ 4552 7200
1446/ 0101 0032
1437/ 0113 0036
5405/ 0113 0036
\c
.SAVE SYS TECO
Added media/os8/patches/TECO-31.20.05M-v5A.patch8.

















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# PROBLEM WITH AY COMMAND (SR)
# 
# Problem: On rare occasions, the Y command will fail, giving the error message
# 
#     ?NAY Numeric Argument with Y
# 
# even though no numeric argument was specified.
# For example, the AY command (Append then Yank) consistently fails in this manner.
# The test for a numeric argument is incorrect.
# The following patch corrects this problem and upgrades TECO to 5A:

.GET SYS TECO
.ODT
2022/ 1024 2024;7610
4573/ 0005 0765
\c
.SAVE SYS TECO
Added media/os8/patches/TECO-31.20.06M-v5B.patch8.

























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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# CONDITIONALS INSIDE ITERATIONS
# 
# Problem: TECO does not properly handle unsatisfied conditionals
# if other conditionals are encountered within an inner iteration
# while scanning for the terminating single quote.
# 
# While scanning for the matching single quote, TECO keeps an iteration count
# for each level of nested iterations which it finds.
# TECO then ignores any single quotes which occur at a 'nest' level greater than 0.
# (All conditionals must end at the same macro level that they begin.)
# The problem is that TECO incorrectly bumps the conditional count
# whenever it sees a double quote.
# This should not be done for double quotes occurring at a non-zero level.
# 
# The following patch fixes this problem by causing TECO to
# ignore double quotes within iterations while scanning for
# a terminating single quote. This ?atch upgrades TECO to VSB:

.GET SYS TECO
.ODT
6077/ 7240 5356
6156/ xxxx 7200;1763;7650;7240;5300;3331
4573/ 0765 0766
\c
.SAVE SYS TECO
Added media/os8/patches/TECO-31.20.07M.v5C.patch8.

























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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# ECHOING OF WARNING BELLS (SR)
# 
# Problem: When typing in command characters to TECO,
# if you come within 10 characters of running out of memory, TECO warns you by
# ringing the terminal bell after each character input.
# (This gives you a small margin in which to clean up your command,
# e.g. by typing two altmodes.)
# Should you persist in typing after the ten warnings,
# upon typing the 11th character, TECO prints the error message
# 
#     ?QMO Q-Register Memory Overflow.
#     
# This mechanism works correctly except that the ringing of the bell
# is accompanied by the printing of the (unwanted) two-character sequence "^G".
# TECO is ringing the bell by calling TYPTCV instead of TPUT.
# 
# The following patch corrects this problem, by causing
# only a warning bell to ring. This patch upgrades TECO to V5C.

.GET SYS TECO
.ODT
0365/ 4521 4552
4573/ 0766 0767
\c
.SAVE SYS TECO
Added media/os8/patches/TECO-31.20.08M-v5.04.patch8.












































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# CTRL/U SOMETIMES FAILS AFTER * (SR)
# PROBLEMS:
# (I)   If a command line contains the character '*', then a subsequent
# use of the immediate mode command, ^U, will reprint the entire
# command string as well as erasing the current line.  (This will
# not hurt you - but it is annoying.)
# (II)  If on a scope terminal, a command line contains the character
# '*', then rubbing out a tab, line feed, vertical tab, or form
# feed will cause the entire command string to be reprinted.
# (III) The bell-space and bell-star (^G<space> and ^G* ) commands were
# not documented because they did not work properly.
# The immediate mode command, ^G<space> causes the current line of
# the commmand string to be retyped.
# The immediate mode command, ^G* causes the entire command string
# to be retyped.
# Note that the ^G (bell) character cannot be entered in up-arrow
# mode.
# (IV)  The ^G* command incorrectly prints out the contents of all your
# Q-Pegisters.
# (V)   When in scope mode, if you rub-out back to the first line of the
# command string, and if there is text in some Q-register, the '*'
# representing TECO's last prompt vanishes from the screen.
# (VI)  The ^G<space> command works improperly on 12K machines when
# there are more than 2900 characters stored away in Q-registers.
# ANALYSIS:
# Poltergeists in TECO.
# DISPOSITION:
# The following patch fixes all these bugs in TECO.  It also makes
# the ^G<space> and ^G* commands work properly.  This patch
# upgrades TECO to version 5.04.
.GET SYS:TECO
.ODT
1341/1435   1464;1464
1431/5235   5264
1435/4265   7510;5313;1072;5304
1500/7240   1072;7040;1050;5235
1463/4265   5266;1175;3331
1524/1175   6032;5775;1175;5253
4570/1454   1526
0255/5772   5004
0004/xxxx   1577;4540;5407;1464
4573/0767   0770
\c
.SAVE SYS:TECO
Added media/os8/patches/TECO-31.20.10M-5.05.patch8.















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# TECO computes the product n*0 incorrectly.
# Complementing a 13-bit 0 sets the link.
# TECO fails to account for this.
# The following patch to TECO V5.04 fixes this bug by
# zeroing the link before starting the multiply.
# This patch upgrades TECO to V5.05
# Just as in V3C TECO (Version 4), multiplication by
# negative numbers is not supported and unpredictable
# results will occur if a multiplicand is less than 0
.GET SYS:TECO
.ODT
1311/7010 7110
4573/0770 0771
\c
.SAVE SYS:TECO
Added media/os8/patches/TECO-31.20.11M-v5.06.patch8.














1
2
3
4
5
6
7
8
9
10
11
12
13
14
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Q-REGISTERS DON'T WORK IN 8K (SR)
# TECO doesn't work properly on 8K machines.
# The code which changes the handling of Q-register
# storage in the 8K case is faulty.
# This patch upgrades TECO to Version 5.06.
.GET SYS:TECO
.ODT
5762/0122 7777
5771/xxxx 122;127;102;107;7777
5710/3362 3371;24
# Correction: Original patch had version change wrong.
4573/0771 772
\c
.SAVE SYS:TECO
Added media/os8/patches/TECO-31.20.12M-v5.07.patch8.































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# CAN'T SKIP OVER A "W" (SR)
# PROBLEM:
# If the letter w (as in PW) occurs inside a piece of
# TECO code which is being skipped (say because it is
# part of an unsatisfied conditional), TECO V5.06
# will blow up.
# DIAGNOSIS:
# The appropriate skip table does not end with the
# required negative number.  This table flows into
# the skip table for skipping the second letter of an
# E command (R, w, B, or G).  The corresponding
# entries in the dispatch table are all harmless
# (positive) except for 'W' which causes SORT to
# branch to 'death'.
# SOLUTION:
# The following patch inserts a -1 indicator to
# properly terminate the table:
# This patch upgrades TECO to V5.07.
.GET SYS:TECO
.ODT
5264/7240 1360
5461/7346 7344
6250/7346 7344
# Correction: original patch had version wrong.
4573/0772 0773
5227/1760 1642
5242/6201 7777
5360/7777 7776
5331/5266 5264
\c
.SAVE SYS:TECO
Added media/os8/patches/TECO-31.20.13M-v5.08.patch8.
























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# UNSPECIFIED ITERATIONS AFTER INSERTS (SR)
# Problem:
# If an iteration has no iteration count specified, and the
# previous command was "on insert, then the iteration is
# skipped.  For example, the command IA$<L> will not work
# properly.
# Diagnosis:
# The insert code destructively tests the number flag (NFLG)
# and if it was 0, sets it to a 1.  It is never reset to 0.
# The iteration code sees a 1 and thinks a number is
# present.  Looking for one, it finds 0 and thinks the
# iteration count is 0 meaning skip this iteration.
# Cure:
# The insert code should reset (zero) the number flag.  This
# was not a problem in TECO V4 because 0<> was the same as
# <> then.  Apply the following patch (which upgrades TECO
# to version 5.08):
.GET SYS:TECO
.ODT
2616/6032 5776;3167
2674/5565 5617
4573/0773 0774
\c
.SAVE SYS:TECO
Added media/os8/patches/patch_list.txt.
































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Most of these patches have been verified
# by reading the source code.
# The patches that remain commented out are not recommended
# and the reason why appears in the line above it.
#
# ABSLDR-21.29.1M is against v6A but we have v4A.
# v6A came with OS/8 Devices Extension kit QF026
# Binary DECtape: AL-H525A-BA, Source not on DECtape
# DO NOT APPLY THIS PATCH.
## ABSLDR-21.29.1M-v6C.patch8
BASIC.UF-31.5.1M-V5B.patch8
BATCH-31.23.1M-v7B.patch8
BLOAD-31.10.1M-v5B.patch8
BRTS-31.11.1M-v5B.patch8
# BRTS 31.11.2O disables 8th bit parity. Recommended.
BRTS-31.11.2-O.patch8
# BRTS 31.11.3O enables 132 column output. Recommended.
BRTS-31.11.3-O.patch8
BRTS-31.11.5-x.patch8
CREF-21.15.1M-v5B.patch8
CREF-21.15.2M-v5C.patch8
EDIT-21.17.1M-v12B.patch8
EDIT-21.17.2M-v12C.patch8
EDIT-21.17.3M-v12D.patch8
# EDIT 21.17.4 overwrites patch in 21.17.2. DO NOT APPLY THIS PATCH
# EDIT-21.17.4M-V12C.patch8
F4-21.1.2M-v4B.patch8
F4-51.3.1M-v4C.patch8
F4-51.3.2M-v4x.patch8
# FORLIB 51.10.1M is hard-coded into mkos8 to copy a new
# FORLIB.RL made with instructions from the patch.
# So the patch will not apply but is listed here for completeness.
## FORLIB-51.10.1M.patch8
FOTP-21.19.1M-V9B.patch8
# FRTS-51.3.3-O is to enable FRTS to work with 2-page system
# handlers.  I've read the code but do not fully understand it.
# It is plausable that it generalizes on the code that makes
# the 2-page TD8E handler work.  But it could also be a
# patch tha tONLY works with the OS/8 Devices Extension kit QF026.
# We are enabling the patch for not.  If further testing shows
# that it breaks TD8E support, we will turn it off.
FRTS-51.3.3-O.patch8
# The two FUTIL patches only get applied to FUTIL V7 which comes with
# OS/8 V3D to bring it up to V7D.  MACREL V2 comes with FUTIL V8B, so
# these patches are skipped by mkos8 using an RE match on the file name
# when the user does not pass --disable-os8-macrel to configure.
FUTIL-31.21.1M-v7B.patch8
FUTIL-31.21.2M-v7D.patch8
# FUTIL 31.21.3O switches XS format. Recommend to leave it out.
# FUTIL-31.21.3O.patch8
# LQP 21.49.1 consists of commands run in BUILD.
# The auto-apply system won't apply it.
# It has not been validated. It uses hardware we don't have.
## LQP-21.49.1M-vB.patch8
MCPIP-21.21M-v6B.patch8
MSBAT-31.22.1M-v3B.patch8
PAL8-21.22.1M-v10B.patch8
PAL8-21.22.2M-v10C.patch8
PAL8-21.22.3M-v10D.patch8
# PAL8 21.22.4M is for V12 PAL8. It BREAKS LS output in V10!
# DO NOT APPLY THIS PATCH!
# PAL8-21.22.4M.patch8
PIP-21.23.1M-v12B.patch8
PIP10-21.24.1M-v3B.patch8
SABR-21.91.1M-v18B.patch8
SET-21.26.1M-v1C.patch8
SET-21.26.2M-v1D.patch8
SET-21.26.3M-v1E.patch8
# TECO 31.20.1 Unconditional no case flagging. Not recommended
# TECO-31.20.01O.patch8
# TECO 31.20.2 Turns off verbose errors. Not recommended.
# TECO-31.20.02O.patch8
# TECO 31.20.3 Turns off Yank overwrite warning. Not recommended.
# TECO-31.20.03O.patch8
# TECO 31.20.4 Special support for VT05. Not recommended.
# TECO-31.20.04O.patch8
TECO-31.20.05M-v5A.patch8
TECO-31.20.06M-v5B.patch8
TECO-31.20.07M.v5C.patch8
TECO-31.20.08M-v5.04.patch8
TECO-31.20.10M-5.05.patch8
TECO-31.20.11M-v5.06.patch8
TECO-31.20.12M-v5.07.patch8
TECO-31.20.13M-v5.08.patch8
#
# MACREL, LINK, and OVDRV patches have not been validated.
# The Version numbers dont all match. Some wont apply.
# More work is needed before they are deemed safe.
# NOT Recommended.
## MACREL-40.5.1M-v1D.patch8
## MACREL-40.5.2M-v1E.patch8
## LINK-40.2.1M-v1D.patch8
## LINK-40.2.2M-v1E.patch8
## LINK-40.2.3M-v1F.patch8
# OVRDRV 40.6.1 is a source level patch
## OVRDRV-40.6.1M-v1B-8srccom
Changes to media/os8/subsys/advent.tu56.

cannot compute difference between binary files

Added media/os8/subsys/al-4545d-sa-fr4-v3d-1.tu56.

cannot compute difference between binary files

Added media/os8/subsys/al-4546d-sa-fr4-v3d-2.tu56.

cannot compute difference between binary files

Added media/os8/subsys/al-4547d-sa-fr4-v3d-3.tu56.

cannot compute difference between binary files

Added media/os8/subsys/al-4549d-ba-fr4-v3d-1.1978.tu56.

cannot compute difference between binary files

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

cannot compute difference between binary files

Changes to 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/uwfocal-v4e-1.tu56.

cannot compute difference between binary files

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

cannot compute difference between binary files

Deleted palbart/LICENSE.md.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
# palbart License

The following was extracted from the top of [`palbart.c`][1] in this
directory:

---------

This is free software.  There is no fee for using it.  You may make any
changes that you wish and also give it away.  If you can make commercial
product out of it, fine, but do not put any limits on the purchaser's
right to do the same.  If you improve it or fix any bugs, it would be
nice if you told me and offered me a copy of the new version.

---------

[1]: https://tangentsoft.com/pidp8i/doc/trunk/palbart/palbart.c
Deleted palbart/palbart.1.
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








































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.\"                                      Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH PALBART 1 "January 16, 2000"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh        disable hyphenation
.\" .hy        enable hyphenation
.\" .ad l      left justify
.\" .ad b      justify to both left and right margins
.\" .nf        disable filling
.\" .fi        enable filling
.\" .br        insert line break
.\" .sp <n>    insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
palbart \- BART enhanced PDP8 crossassembler
.SH SYNOPSIS
.B palbart
.RI [options] inputfile
.br
.SH DESCRIPTION
This manual page documents briefly the
.B palbart
command.
It is a cross-assembler to for PDP/8 assembly language programs.
It will produce an output file in bin format, rim format, and using the
appropriate pseudo-ops, a combination of rim and bin formats.
A listing file is always produced and with an optional symbol table
and/or a symbol cross-reference (concordance).  The permanent symbol
table can be output in a form that may be read back in so a customized
permanent symbol table can be produced.  Any detected errors are output
to a separate file giving the filename in which they were detected
along with the line number, column number and error message as well as
marking the error in the listing file.
.PP
The following file name extensions are used:
.PP
 .pal    source code (input)
.PP
 .lst    assembly listing (output)
.PP
 .bin    assembly output in DEC's bin format (output)
.PP
 .rim    assembly output in DEC's rim format (output)
.PP
 .err    assembly errors detected (if any) (output)
.PP
 .prm    permanent symbol table in form suitable for reading after the EXPUNGE pseudo-op.

.PP
.SH OPTIONS
A summary of options is included below.
.TP
.B \-d
Show symbol table at end of assembly
.TP
.B \-h
Display help.
.TP
.B \-l
Allow generation of literals (default is no literal generation)
Show version of program.
.TP
.B \-p
Generate a file with the permanent symbols in it.
(To get the current symbol table, assemble a file than has only
a $ in it.)
.TP
.B \-r
Produce output in rim format (default is bin format)
.TP
.B \-v
Display version information.
.TP
.B \-x
Generate a cross-reference (concordance) of user symbols.

.SH  DIAGNOSTICS
Assembler error diagnostics are output to an error file and inserted
in the listing file.  Each line in the error file has the form
.PP
<filename>(<line>:<col>) : error:  <message> at Loc = <loc>
.PP
An example error message is:
.br
bintst.pal(17:9) : error:  undefined symbol "UNDEF" at Loc = 07616
.PP
The error diagnostics put in the listing start with a two character
error code (if appropriate) and a short message.  A carat '^' is
placed under the item in error if appropriate.
An example error message is:
.PP
      17 07616 3000          DCA     UNDEF
.br
   UD undefined                      ^
.br
      18 07617 1777          TAD  I  DUMMY
.PP
When an indirect is generated, an at character '@' is placed after the
the instruction value in the listing as an indicator as follows:
.PP
      14 03716 1777@         TAD     OFFPAG
.PP
Undefined symbols are marked in the symbol table listing by prepending
a '?' to the symbol.  Redefined symbols are marked in the symbol table
listing by prepending a '#' to the symbol.  Examples are:
.PP
   #REDEF   04567
.br
    SWITCH  07612
.br
   ?UNDEF   00000
.PP
Refer to the code for the diagnostic messages generated.

.SH BUGS
Only a minimal effort has been made to keep the listing format
anything like the PAL-8 listing format.
The operation of the conditional assembly pseudo-ops may not function
exactly as the DEC versions.  I did not have any examples of these so
the implementation is my interpretation of how they should work.
.PP
The RIMPUNch and BINPUNch pseudo-ops do not change the binary output
file type that was specified on startup.  This was intentional and
and allows rim formatted data to be output prior to the actual binary
formatted data.  On UN*X style systems, the same effect can be achieved
ing the "cat" command, but on DOS/Windows systems, doing this was
a major chore.
.PP
The floating point input does not generate values exactly as the DEC
compiler does.  I worked out several examples by hand and believe that
this implementation is slightly more accurate.  If I am mistaken,
let me know and, if possible, a better method of generating the values.
.br

.SH HISTORICAL NOTE
This assembler was written to support the fleet of PDP-8 systems
used by the Bay Area Rapid Transit System.  As of early 1997,
this includes about 40 PDP-8/E systems driving the train destination
signs in passenger stations.

.SH REFERENCES
This assembler is based on the pal assember by:
.br
Douglas Jones <jones@cs.uiowa.edu> and
.br
Rich Coon <coon@convexw.convex.com>

.SH DISCLAIMER
See the symbol table for the set of pseudo-ops supported.
.PP
See the code for pseudo-ops that are not standard for PDP/8 assembly.
.PP
Refer to DEC's "Programming Languages (for the PDP/8)" for complete
documentation of pseudo-ops.
.PP
Refer to DEC's "Introduction to Programming (for the PDP/8)" or a
lower level introduction to the assembly language.

.SH WARRANTY
If you don't like it the way it works or if it doesn't work, that's
tough.  You're welcome to fix it yourself.  That's what you get for
using free software.

.SH COPYRIGHT NOTICE
This is free software.  There is no fee for using it.  You may make
any changes that you wish and also give it away.  If you can make
a commercial product out of it, fine, but do not put any limits on
the purchaser's right to do the same.  If you improve it or fix any
bugs, it would be nice if you told me and offered me a copy of the
new version.
Gary Messenbrink <gam@rahul.net>

.SH VERSIONS
 Version  Date    by   Comments
.br
   v1.0  12Apr96  GAM  Original
.br
   v1.1  18Nov96  GAM  Permanent symbol table initialization error.
.br
   v1.2  20Nov96  GAM  Added BINPUNch and RIMPUNch pseudo-operators.
.br
   v1.3  24Nov96  GAM  Added DUBL pseudo-op (24 bit integer constants).
.br
   v1.4  29Nov96  GAM  Fixed bug in checksum generation.
.br
   v2.1  08Dec96  GAM  Added concordance processing (cross reference).
.br
   v2.2  10Dec96  GAM  Added FLTG psuedo-op (floating point constants).
.br
   v2.3   2Feb97  GAM  Fixed paging problem in cross reference output.
.br
   v2.4  11Apr97  GAM  Fixed problem with some labels being put in cross reference multiple times.

.SH AUTHOR
This manual page was written by Vince Mulhollon <vlm@execpc.com>,
for the Debian GNU/Linux system (but may be used by others).
Deleted palbart/palbart.c.
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
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/******************************************************************************/
/*                                                                            */
/* Program:  PAL (BART version)                                               */
/* File:     pal.c                                                            */
/* Author:   Gary A. Messenbrink                                              */
/*           gam@rahul.net                                                    */
/*                                                                            */
/* Purpose:  A 2 pass PDP-8 pal-like assembler.                               */
/*                                                                            */
/* PAL(1)                                                                     */
/*                                                                            */
/* NAME                                                                       */
/*    pal - a PDP/8 pal-like assembler.                                       */
/*                                                                            */
/* SYNOPSIS:                                                                  */
/*    pal [ -$ -d -h -e -l -p -r -t -v -x ] inputfile                         */
/*                                                                            */
/* DESCRIPTION                                                                */
/*    This is a cross-assembler to for PDP/8 assembly language programs.      */
/*    It will produce an output file in bin format, rim format, and using the */
/*    appropriate pseudo-ops, a combination of rim and bin formats.           */
/*    A listing file is always produced and with an optional symbol table     */
/*    and/or a symbol cross-reference (concordance).  The permanent symbol    */
/*    table can be output in a form that may be read back in so a customized  */
/*    permanent symbol table can be produced.  Any detected errors are output */
/*    to a separate file giving the filename in which they were detected      */
/*    along with the line number, column number and error message as well as  */
/*    marking the error in the listing file.                                  */
/*    The following file name extensions are used:                            */
/*       .pal    source code (input)                                          */
/*       .lst    assembly listing (output)                                    */
/*       .bin    assembly output in DEC's bin format (output)                 */
/*       .rim    assembly output in DEC's rim format (output)                 */
/*       .err    assembly errors detected (if any) (output)                   */
/*       .prm    permanent symbol table in form suitable for reading after    */
/*               the EXPUNGE pseudo-op.                                       */
/*                                                                            */
/* OPTIONS                                                                    */
/*    -$   Allow files to not end with $                                      */
/*    -d   Dump the symbol table at end of assembly                           */
/*    -h   Show help                                                          */
/*    -e   Don't allow generation of links                                    */
/*    -l   Allow generation of links (default is link generation)             */
/*    -n   No redefinition of permanent symbols with labels                   */
/*    -p   Generate a file with the permanent symbols in it.                  */
/*         (To get the current symbol table, assemble a file than has only    */
/*          a $ in it.)                                                       */
/*    -r   Produce output in rim format (default is bin format)               */
/*    -tN  Set tab stops to N spaces (default is 8)                           */
/*    -v   Display program version.                                           */
/*    -x   Generate a cross-reference (concordance) of user symbols.          */
/*                                                                            */
/* DIAGNOSTICS                                                                */
/*    Assembler error diagnostics are output to an error file and inserted    */
/*    in the listing file.  Each line in the error file has the form          */
/*                                                                            */
/*       <filename>(<line>:<col>) : error:  <message> at Loc = <loc>          */
/*                                                                            */
/*    An example error message is:                                            */
/*                                                                            */
/*       bintst.pal(17:9) : error:  undefined symbol "UNDEF" at Loc = 07616   */
/*                                                                            */
/*    The error diagnostics put in the listing start with a two character     */
/*    error code (if appropriate) and a short message.  A carat '^' is        */
/*    placed under the item in error if appropriate.                          */
/*    An example error message is:                                            */
/*                                                                            */
/*          17 07616 3000          DCA     UNDEF                              */
/*       UD undefined                      ^                                  */
/*          18 07617 1777          TAD  I  DUMMY                              */
/*                                                                            */
/*    When an indirect is generated, an at character '@' is placed after the  */
/*    the instruction value in the listing as an indicator as follows:        */
/*                                                                            */
/*          14 03716 1777@         TAD     OFFPAG                             */
/*                                                                            */
/*    Undefined symbols are marked in the symbol table listing by prepending  */
/*    a '?' to the symbol.  Redefined symbols are marked in the symbol table  */
/*    listing by prepending a '#' to the symbol.  Examples are:               */
/*                                                                            */
/*       #REDEF   04567                                                       */
/*        SWITCH  07612                                                       */
/*       ?UNDEF   00000                                                       */
/*                                                                            */
/*    Refer to the code for the diagnostic messages generated.                */
/*                                                                            */
/* BUGS                                                                       */
/*    This program will accept source that real PAL will not. To ensure       */
/*    valid source assemble on real or simulated PDP-8.                       */
/*    Different PAL versions have different permanent symbols defined. This   */
/*    program define more than and PAL version. By default redefining them    */
/*    as a label is not an error. It is for normal PAL. The -n flag will      */
/*    make redefining an error.                                               */
/*                                                                            */
/*    Only a minimal effort has been made to keep the listing format          */
/*    anything like the PAL-8 listing format.                                 */
/*    The operation of the conditional assembly pseudo-ops may not function   */
/*    exactly as the DEC versions.  I did not have any examples of these so   */
/*    the implementation is my interpretation of how they should work.        */
/*                                                                            */
/*    The RIMPUNch and BINPUNch pseudo-ops do not change the binary output    */
/*    file type that was specified on startup.  This was intentional and      */
/*    and allows rim formatted data to be output prior to the actual binary   */
/*    formatted data.  On UN*X style systems, the same effect can be achieved */
/*    by using the "cat" command, but on DOS/Windows systems, doing this was  */
/*    a major chore.                                                          */
/*                                                                            */
/*    The floating point input does not generate values exactly as the DEC    */
/*    compiler does.  I worked out several examples by hand and believe that  */
/*    this implementation is slightly more accurate.  If I am mistaken,       */
/*    let me know and, if possible, a better method of generating the values. */
/*                                                                            */
/*    CDF .-.                                                                 */
/*       Generates 2201 when assembled at 5000. This looks like a bug in OS/8 */
/*       PAL                                                                  */
/*                                                                            */
/* BUILD and INSTALLATION                                                     */
/*    The current version has only been built under Linux.                    */
/*    Earlier versions have been built and successfully executed on:          */
/*      a.  Linux (80486 CPU)using gcc                                        */
/*      b.  RS/6000 (AIX 3.2.5)                                               */
/*      c.  Borland C++ version 3.1  (large memory model)                     */
/*      d.  Borland C++ version 4.52 (large memory model)                     */
/*    with no modifications to the source code.                               */
/*                                                                            */
/*    On UNIX type systems, store the the program as the pal command          */
/*    and on PC type systems, store it as pal.exe                             */
/*                                                                            */
/* HISTORICAL NOTE:                                                           */
/*    This assembler was written to support the fleet of PDP-8 systems        */
/*    used by the Bay Area Rapid Transit System.  As of early 1997,           */
/*    this includes about 40 PDP-8/E systems driving the train destination    */
/*    signs in passenger stations.                                            */
/*                                                                            */
/* REFERENCES:                                                                */
/*    This assembler is based on the pal assembler by:                         */
/*       Douglas Jones <jones@cs.uiowa.edu> and                               */
/*       Rich Coon <coon@convexw.convex.com>                                  */
/*                                                                            */
/* DISCLAIMER:                                                                */
/*    See the symbol table for the set of pseudo-ops supported.               */
/*    See the code for pseudo-ops that are not standard for PDP/8 assembly.   */
/*    Refer to DEC's "Programming Languages (for the PDP/8)" for complete     */
/*    documentation of pseudo-ops.                                            */
/*    Refer to DEC's "Introduction to Programming (for the PDP/8)" or a       */
/*    lower level introduction to the assembly language.                      */
/*                                                                            */
/* WARRANTY:                                                                  */
/*    If you don't like it the way it works or if it doesn't work, that's     */
/*    tough.  You're welcome to fix it yourself.  That's what you get for     */
/*    using free software.                                                    */
/*                                                                            */
/* COPYRIGHT NOTICE:                                                          */
/*    This is free software.  There is no fee for using it.  You may make     */
/*    any changes that you wish and also give it away.  If you can make       */
/*    a commercial product out of it, fine, but do not put any limits on      */
/*    the purchaser's right to do the same.  If you improve it or fix any     */
/*    bugs, it would be nice if you told me and offered me a copy of the      */
/*    new version.                                                            */
/*                                                                            */
/*                                                                            */
/* Amendments Record:                                                         */
/*  Version  Date    by   Comments                                            */
/*  ------- -------  ---  --------------------------------------------------- */
/*    v1.0  12Apr96  GAM  Original                                            */
/*    v1.1  18Nov96  GAM  Permanent symbol table initialization error.        */
/*    v1.2  20Nov96  GAM  Added BINPUNch and RIMPUNch pseudo-operators.       */
/*    v1.3  24Nov96  GAM  Added DUBL pseudo-op (24 bit integer constants).    */
/*    v1.4  29Nov96  GAM  Fixed bug in checksum generation.                   */
/*    v2.1  08Dec96  GAM  Added concordance processing (cross reference).     */
/*    v2.2  10Dec96  GAM  Added FLTG psuedo-op (floating point constants).    */
/*    v2.3   2Feb97  GAM  Fixed paging problem in cross reference output.     */
/* DJG: I started with the 2.5 RK version but found when looking on the net   */
/* later that multiple diverging version existed. I have tried to combine     */
/* the fixed into one version. I took the version info below from the versions*/
/* I pulled from.                                                             */
/* http://dustyoldcomputers.com/pdp-common/reference/host/index.html          */
/* http://www.dunnington.u-net.com/public/PDP-8/palbart.c                     */
/* http://sourcecodebrowser.com/palbart/2.4/palbart-2_84_8c_source.html       */
/* http://packages.qa.debian.org/p/palbart.html                               */
/*    v2.4  11Apr97  GAM  Fixed problem with some labels being put in cross   */
/*                        reference multiple times.                           */
/*                        Started with RK version, Attempted to merge         */
/*                        GAM V2.4 and PNT change DJG                         */
/*    v2.4  29Oct07  RK   Added 4 character tabstop; IOTs for TA8/E.          */
/*    v2.4  19Jan03  PNT  Added ASCII pseudo-op, like TEXT but not packed.    */
/*    v2.5  03Nov07  RK   Fixed buffer overflow problem in readLine and       */
/*                        increased symbol table size                         */
/*    v2.6  14Jul03  PNT  Added missing TTY symbols, and "1st TTY" symbols.   */
/*    v2.7  14Jun13  DJG  David Gesswein djg@pdp8online.com                   */
/*                        Merged other changes found online giving duplicate  */
/*                         Versions in the history                            */
/*                         Didn't copy over deleting -l literal flag          */
/*                        All fixes to make it match OS/8 PAL8 better         */
/*                        Fixed handling of IFDEF type conditionals           */
/*                        Fixed excessive redefined symbol errors             */
/*                         PAL8 uses 12 bit symbols and this program label    */
/*                         symbols are 15 bit.                                */
/*                        Added FILENAME and DEVNAME psuedo ops               */
/*                        Added OPR and KCF instructions. Fixed RMF           */
/*                        Allowed space after =                               */
/*                        Prevented I and D from being deleted by EXPUNGE     */
/*                        Allowed permanent symbols to be redefined with error*/
/*                         PAL8 updates without message. Error is just warning*/
/*                        Fixed certain cases of memory reference generation  */
/*                        Allowed unary +                                     */
/*                        Fixed " character literal at end of line            */
/*                        Fixed errors in reloc handling                      */
/*                        Fixed [CDF CIF type expressions                     */
/*                        Made title default to first line                    */
/*                        Fixed checksum when nopunch used                    */
/*                        Fixed FIXTAB                                        */
/*                        Probably added more subtle bugs                     */
/*    v2.8  15Jun13  DJG  Merged versions found on net. See above             */
/*                        Added * to RELOC addresses in listing               */
/*                        Changed default to literal/links on. Added -e to    */
/*                         turn off                                           */
/*                        Fixed PAGE when RELOC used                          */
/*                        Changed SPF to TFL and SPI to TSK                   */
/*                        Make error when changing permanent symbol to label  */
/*                         if -e flag is used                                 */
/*                        Allow space oring in IFZERO etc                     */
/*                        Fixed handling of page zero overflow                */
/*    v2.9  23Jun13  DJG  Fixed properly all pages literal handling           */
/*                         changing page doesn't cause loss of last literal   */
/*                         location used.                                     */
/*                        Fixed bin generation if no origin set               */
/*    v2.9a 01Jul13  DJG  Fixed Comment. Binaries not updated                 */
/*    v2.10 08Feb14  DJG  Changed trailer to 8 bytes since pip didn't like    */
/*                        trailer of one 0x80                                 */
/*    v2.11 19Apr15  DPI  Fixed incorrect link generation with impled 0200    */
/*                        starting address. Patch from Doug Ingrams           */
/*    v2.12 28Apr15  DJG  Fixed incorrect handling of reloc, expressions with */
/*                        undefined symbols. Fixed conditional assembly with  */
/*                        undefined symbols. Added new flag to allow file to  */
/*                        not end with $                                      */
/*    v2.13 02May15  DPI  Fixed bug in readLine when removing \r from a blank */
/*                        line.  Changed -s to -$ in -h display.  Corrected   */
/*                        version comment.                                    */
/*    v2.13 03May15  DJG  Moved TITLE, BANK to new additional option.         */
/* Change release variable below when you update. Send changes back to        */
/*   David Gesswein, djg@pdp8online.com.                                      */
/******************************************************************************/

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

char *release = "pal-2.13, 03 May 2015";

/* Set to 1 and use -e flag to make ( and [ literals errors */
#define LITERAL_ERROR        0

#define LINELEN              132
#define LIST_LINES_PER_PAGE  55         /* Includes 5 line page header.       */
#define NAMELEN             128
#define SYMBOL_COLUMNS        5
#define SYMLEN                7
#define SYMBOL_TABLE_SIZE  4096
#define TITLELEN             63
#define XREF_COLUMNS          8

#define ADDRESS_FIELD  00177
#define FIELD_FIELD   070000
#define INDIRECT_BIT   00400
#define LAST_PAGE_LOC  00177
#define OP_CODE        07000
#define PAGE_BIT       00200

#ifdef  PAGE_SIZE
#undef  PAGE_SIZE
#endif
#define PAGE_SIZE      00200

#define PAGE_FIELD     07600
#define PAGE_ZERO_END  00200

/* Macro to get the number of elements in an array.                           */
#define DIM(a) (sizeof(a)/sizeof(a[0]))

/* Macro to get the address plus one of the end of an array.                  */
#define BEYOND(a) ((a) + DIM(A))

#define is_blank(c) ((c==' ') || (c=='\t') || (c=='\f') || (c=='>'))
#define isend(c)   ((c=='\0')|| (c=='\n'))
#define isdone(c)  ((c=='/') || (isend(c)) || (c==';'))

/* Macros for testing symbol attributes.  Each macro evaluates to non-zero    */
/* (true) if the stated condition is met.                                      */
/* Use these to test attributes.  The proper bits are extracted and then      */
/* tested.                                                                    */
#define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION)
#define M_DEFINED(s)     ((s & DEFINED) == DEFINED)
#define M_DUPLICATE(s)   ((s & DUPLICATE) == DUPLICATE)
#define M_FIXED(s)       ((s & FIXED) == FIXED)
#define M_LABEL(s)       ((s & LABEL) == LABEL)
#define M_MRI(s)         ((s & MRI) == MRI)
#define M_MRIFIX(s)      ((s & MRIFIX) == MRIFIX)
#define M_PSEUDO(s)      ((s & PSEUDO) == PSEUDO)
#define M_REDEFINED(s)   ((s & REDEFINED) == REDEFINED)
#define M_UNDEFINED(s)   (!M_DEFINED(s))
#define M_PERM_REDEFINED(s)   ((s & PERM_REDEFINED) == PERM_REDEFINED)

/* This macro is used to test symbols by the conditional assembly pseudo-ops. */
#define M_DEF(s) (M_DEFINED(s))
#define M_COND(s) (M_CONDITIONAL(s))
#define M_DEFINED_CONDITIONALLY(t) (M_DEF(t) && ((pass==1) ||!M_COND(t)))

typedef unsigned char BOOL;
typedef unsigned char BYTE;
typedef short    int  WORD16;
typedef long     int  WORD32;

#ifndef FALSE
  #define FALSE 0
  #define TRUE (!FALSE)
#endif

/* Line listing styles.  Used to control listing of lines.                    */
enum linestyle_t
{
  LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL
};
typedef enum linestyle_t LINESTYLE_T;

/* Symbol Types.                                                              */  
/* Note that the names that have FIX as the suffix contain the FIXED bit      */
/* included in the value.                                                     */
/*                                                                            */
/* The CONDITION bit is used when processing the conditional assembly PSEUDO- */
/* OPs (e.g., IFDEF).  During pass 1 of the assembly, the symbol is either    */
/* defined or undefined.  The condition bit is set when the symbol is defined */
/* during pass 1 and reset on pass 2 at the location the symbol was defined   */
/* during pass 1.  When processing conditionals during pass 2, if the symbol  */
/* is defined and the condition bit is set, the symbol is treated as if it    */
/* were undefined.  This gives consistent behavior of the conditional         */
/* pseudo-ops during both pass 1 and pass 2.                                  */
enum symtyp
{
  UNDEFINED = 0000,
  DEFINED   = 0001,
  FIXED     = 0002,
  MRI       = 0004    | DEFINED,
  LABEL     = 0010    | DEFINED,
  REDEFINED = 0020    | DEFINED,
  DUPLICATE = 0040    | DEFINED,
  PSEUDO    = 0100    | FIXED | DEFINED,
  CONDITION = 0200    | DEFINED,
  PERM_REDEFINED = 0400,
  MRIFIX    = MRI     | FIXED | DEFINED,
  DEFFIX    = DEFINED | FIXED
};
typedef enum symtyp SYMTYP;

enum pseudo_t
{
  BANK,    BINPUNCH, DECIMAL, DUBL,    EJECT,    ENPUNCH, EXPUNGE, FIELD,
  FIXMRI,  FIXTAB,   FLTG,    IFDEF,   IFNDEF,   IFNZERO, IFZERO,  NOPUNCH,
  OCTAL,   PAGE,     PAUSE,   RELOC,   RIMPUNCH, SEGMNT,  TEXT,    TITLE,
  XLIST,   ZBLOCK, FILENAME, DEVICE, ASCII
};
typedef enum pseudo_t PSEUDO_T;

struct sym_t
{
  SYMTYP  type;
  char    name[SYMLEN];
  WORD16  val;
  int     xref_index;
  int     xref_count;
};
typedef struct sym_t SYM_T;

struct lpool_t
{
  WORD16  loc;
  WORD16  last_punched;
  WORD16  pool[PAGE_SIZE];
};
typedef struct lpool_t LPOOL_T;

struct emsg_t
{
  char  *list;
  char  *file;
};
typedef struct emsg_t EMSG_T;

struct errsave_t
{
  char  *mesg;
  int    col;
};
typedef struct errsave_t ERRSAVE_T;

struct fltg_
{
  WORD16 exponent;
  WORD32 mantissa;
};
typedef struct fltg_ FLTG_T;

/*----------------------------------------------------------------------------*/

/* Function Prototypes                                                        */

int     binarySearch( char *name, int start, int symbol_count );
int     compareSymbols( const void *a, const void *b );
void    conditionFalse( void );
void    conditionTrue( void );
SYM_T  *defineLexeme( int start, int term, WORD16 val, SYMTYP type );
SYM_T  *defineSymbol( char *name, WORD16 val, SYMTYP type, WORD16 start);
void    endOfBinary( void );
void    errorLexeme( EMSG_T *mesg, int col );
void    errorMessage( EMSG_T *mesg, int col );
void    errorSymbol( EMSG_T *mesg, char *name, int col );
SYM_T  *eval( void );
WORD32  evalDubl( WORD32 initial_value );
FLTG_T *evalFltg( void );
SYM_T  *evalSymbol( void );
void    getArgs( int argc, char *argv[] );
WORD32  getDublExpr( void );
WORD32  getDublExprs( void );
FLTG_T *getFltgExpr( void );
FLTG_T *getFltgExprs( void );
SYM_T  *getExpr( void );
WORD16  getExprs( void );
WORD16  incrementClc( void );
void    inputDubl( void );
void    inputFltg( void );
WORD16  insertLiteral( LPOOL_T *pool, WORD16 value, int fieldpage_index );
char   *lexemeToName( char *name, int from, int term );
void    listLine( void );
SYM_T  *lookup( char *name );
void    moveToEndOfLine( void );
void    nextLexBlank( void );
void    nextLexeme( void );
void    normalizeFltg( FLTG_T *fltg );
void    onePass( void );
void    printCrossReference( void );
void    printErrorMessages( void );
void    printLine(char *line, WORD16 loc, WORD16 val, LINESTYLE_T linestyle);
void    printPageBreak( void );
void    printPermanentSymbolTable( void );
void    printSymbolTable( void );
BOOL    pseudoOperators( PSEUDO_T val );
void    punchChecksum( void );
void    punchLocObject( WORD16 loc, WORD16 val );
void    punchLiteralPool( LPOOL_T *p, BOOL punch_page0 );
void    punchOutObject( WORD16 loc, WORD16 val );
void    punchLeader( int count );
void    punchObject( WORD16 val );
void    punchOrigin( WORD16 loc );
void    readLine( void );
void    saveError( char *mesg, int cc );
BOOL    testForLiteralCollision( WORD16 loc );
void    topOfForm( char *title, char *sub_title );

/*----------------------------------------------------------------------------*/

/* Table of pseudo-ops (directives) which are used to setup the symbol        */
/* table on startup and when the EXPUNGE pseudo-op is executed.               */
SYM_T pseudo[] =
{
  { PSEUDO, "ASCII",  ASCII   },    /* Put 8-bit ASCII into memory (see TEXT) */
  { PSEUDO, "BINPUN", BINPUNCH },   /* Output in Binary Loader format.        */
  { PSEUDO, "DECIMA", DECIMAL },    /* Read literal constants in base 10.     */
  { PSEUDO, "DEVICE", DEVICE },     /* Pack 6 bit device name into memory     */
  { PSEUDO, "DUBL",   DUBL    },    /* Ignored (unsupported).                 */
  { PSEUDO, "EJECT",  EJECT   },    /* Eject a page in the listing.           */
  { PSEUDO, "ENPUNC", ENPUNCH },    /* Turn on object code generation.        */
  { PSEUDO, "EXPUNG", EXPUNGE },    /* Remove all symbols from symbol table.  */
  { PSEUDO, "FIELD",  FIELD   },    /* Set origin to memory field.            */
  { PSEUDO, "FILENA", FILENAME },   /* Pack 6 bit filename into memory.       */
  { PSEUDO, "FIXMRI", FIXMRI  },    /* Like =, but creates mem ref instruction*/
  { PSEUDO, "FIXTAB", FIXTAB  },    /* Mark current symbols as permanent.     */
  { PSEUDO, "FLTG",   FLTG    },    /* Ignored (unsupported).                 */
  { PSEUDO, "IFDEF",  IFDEF   },    /* Assemble if symbol is defined.         */
  { PSEUDO, "IFNDEF", IFNDEF  },    /* Assemble if symbol is not defined.     */
  { PSEUDO, "IFNZER", IFNZERO },    /* Assemble if symbol value is not 0.     */
  { PSEUDO, "IFNZRO", IFNZERO },    /* Assemble if symbol value is not 0.     */
  { PSEUDO, "IFZERO", IFZERO  },    /* Assemble if symbol value is 0.         */
  { PSEUDO, "NOPUNC", NOPUNCH },    /* Turn off object code generation.       */
  { PSEUDO, "OCTAL",  OCTAL   },    /* Read literal constants in base 8.      */
  { PSEUDO, "PAGE",   PAGE    },    /* Set orign to page +1 or page n (0..37).*/
  { PSEUDO, "PAUSE",  PAUSE   },    /* Ignored                                */
  { PSEUDO, "RELOC",  RELOC   },    /* Assemble to run at a different address.*/
  { PSEUDO, "RIMPUN", RIMPUNCH },   /* Output in Read In Mode format.         */
  { PSEUDO, "SEGMNT", SEGMNT  },    /* Like page, but with page size=1K words.*/
  { PSEUDO, "TEXT",   TEXT    },    /* Pack 6 bit trimmed ASCII into memory.  */
  { PSEUDO, "XLIST",  XLIST   },    /* Toggle listing generation.             */
  { PSEUDO, "ZBLOCK", ZBLOCK  },    /* Zero a block of memory.                */
  { PSEUDO, "TITLE",  TITLE   },    /* Use the text string as a listing title.*/
  { PSEUDO, "BANK",   BANK    }     /* Like field, select some 32K out of 128K*/
};
/* Number o extended pseudo operators to ignore unless command option specified
 * to enable */
#define NUMBER_ADDITIONAL_PSEUDO 2

/* Symbol Table                                                               */
/* The table is put in lexical order on startup, so symbols can be            */
/* inserted as desired into the initial table.                                */
/* really_permanent_symbols aren't removed by EXPUNGE                         */
SYM_T really_permanent_symbols[] =
{
  { MRIFIX, "I",      00400   },    /* INDIRECT ADDRESSING                    */
  { MRIFIX, "Z",      00000   }     /* PAGE ZERO ADDRESS                      */
};

SYM_T permanent_symbols[] =
{
  /* Memory Reference Instructions                                            */
  { MRIFIX, "AND",    00000   },    /* LOGICAL AND                            */
  { MRIFIX, "TAD",    01000   },    /* TWO'S COMPLEMENT ADD                   */
  { MRIFIX, "ISZ",    02000   },    /* INCREMENT AND SKIP IF ZERO             */
  { MRIFIX, "DCA",    03000   },    /* DEPOSIT AND CLEAR ACC                  */
  { MRIFIX, "JMP",    05000   },    /* JUMP                                   */
  { MRIFIX, "JMS",    04000   },    /* JUMP TO SUBROUTINE                     */
  /* Floating Point Interpreter Instructions                                  */
  { MRIFIX, "FEXT",   00000   },    /* FLOATING EXIT                          */
  { MRIFIX, "FADD",   01000   },    /* FLOATING ADD                           */
  { MRIFIX, "FSUB",   02000   },    /* FLOATING SUBTRACT                      */
  { MRIFIX, "FMPY",   03000   },    /* FLOATING MULTIPLY                      */
  { MRIFIX, "FDIV",   04000   },    /* FLOATING DIVIDE                        */
  { MRIFIX, "FGET",   05000   },    /* FLOATING GET                           */
  { MRIFIX, "FPUT",   06000   },    /* FLOATING PUT                           */
  { FIXED,  "FNOR",   07000   },    /* FLOATING NORMALIZE                     */
  { FIXED,  "FEXT",   00000   },    /* EXIT FROM FLOATING POINT INTERPRETER   */
  { FIXED,  "SQUARE", 00001   },    /* SQUARE C(FAC)                          */
  { FIXED,  "SQROOT", 00002   },    /* TAKE SQUARE ROOT OF C(FAC)             */
  /* Group 1 Operate Microinstrcutions                                        */
  { FIXED,  "OPR",    07000   },    /* NO OPERATION                           */
  { FIXED,  "NOP",    07000   },    /* NO OPERATION                           */
  { FIXED,  "IAC",    07001   },    /* INCREMENT AC                           */
  { FIXED,  "RAL",    07004   },    /* ROTATE AC AND LINK LEFT ONE            */
  { FIXED,  "RTL",    07006   },    /* ROTATE AC AND LINK LEFT TWO            */
  { FIXED,  "RAR",    07010   },    /* ROTATE AC AND LINK RIGHT ONE           */
  { FIXED,  "RTR",    07012   },    /* ROTATE AC AND LINK RIGHT TWO           */
  { FIXED,  "CML",    07020   },    /* COMPLEMENT LINK                        */
  { FIXED,  "CMA",    07040   },    /* COMPLEMEMNT AC                         */
  { FIXED,  "CLL",    07100   },    /* CLEAR LINK                             */
  { FIXED,  "CLA",    07200   },    /* CLEAR AC                               */
  /* Group 2 Operate Microinstructions                                        */
  { FIXED,  "BSW",    07002   },    /* Swap bytes in AC (PDP/8e)              */
  { FIXED,  "HLT",    07402   },    /* HALT THE COMPUTER                      */
  { FIXED,  "OSR",    07404   },    /* INCLUSIVE OR SR WITH AC                */
  { FIXED,  "SKP",    07410   },    /* SKIP UNCONDITIONALLY                   */
  { FIXED,  "SNL",    07420   },    /* SKIP ON NON-ZERO LINK                  */
  { FIXED,  "SZL",    07430   },    /* SKIP ON ZERO LINK                      */
  { FIXED,  "SZA",    07440   },    /* SKIP ON ZERO AC                        */
  { FIXED,  "SNA",    07450   },    /* SKIP ON NON=ZERO AC                    */
  { FIXED,  "SMA",    07500   },    /* SKIP MINUS AC                          */
  { FIXED,  "SPA",    07510   },    /* SKIP ON POSITIVE AC (ZERO IS POSITIVE) */
  /* Combined Operate Microinstructions                                       */
  { FIXED,  "CIA",    07041   },    /* COMPLEMENT AND INCREMENT AC            */
  { FIXED,  "STL",    07120   },    /* SET LINK TO 1                          */
  { FIXED,  "GLK",    07204   },    /* GET LINK (PUT LINK IN AC BIT 11)       */
  { FIXED,  "STA",    07240   },    /* SET AC TO -1                           */
  { FIXED,  "LAS",    07604   },    /* LOAD ACC WITH SR                       */
  /* MQ Instructions (PDP/8e)                                                 */
  { FIXED,  "MQL",    07421   },    /* Load MQ from AC, then clear AC.        */
  { FIXED,  "MQA",    07501   },    /* Inclusive OR MQ with AC                */
  { FIXED,  "SWP",    07521   },    /* Swap AC and MQ                         */
  { FIXED,  "ACL",    07701   },    /* Load MQ into AC                        */
  /* Program Interrupt                                                        */
  { FIXED,  "IOT",    06000   },
  { FIXED,  "ION",    06001   },    /* TURN INTERRUPT PROCESSOR ON            */
  { FIXED,  "IOF",    06002   },    /* TURN INTERRUPT PROCESSOR OFF           */
  /* Program Interrupt, PDP-8/e                                               */
  { FIXED,  "SKON",   06000   },    /* Skip if interrupt on and turn int off. */
  { FIXED,  "SRQ",    06003   },    /* Skip on interrupt request.             */
  { FIXED,  "GTF",    06004   },    /* Get interrupt flags.                   */
  { FIXED,  "RTF",    06005   },    /* Restore interrupt flags.               */
  { FIXED,  "SGT",    06006   },    /* Skip on greater than flag.             */
  { FIXED,  "CAF",    06007   },    /* Clear all flags.                       */
  /* Keyboard/Reader                                                          */
  { FIXED,  "KCF",    06030   },    /* CLEAR KEYBOAR FLAG                     */
  { FIXED,  "KSF",    06031   },    /* SKIP ON KEYBOARD FLAG                  */
  { FIXED,  "KCC",    06032   },    /* CLEAR KEYBOARD FLAG & READ CHAR        */
  { FIXED,  "KRS",    06034   },    /* READ KEYBOARD BUFFER (STATIC)          */
  { FIXED,  "KIE",    06035   },    /* AC11 TO KEYBD/RDR INT ENABLE F/F       */
  { FIXED,  "KRB",    06036   },    /* READ KEYBOARD BUFFER & CLEAR FLAG      */
  /* Teleprinter/Punch                                                        */
  { FIXED,  "TFL",    06040   },    /* SET TELEPRINTER/PUNCH FLAG             */
  { FIXED,  "TSF",    06041   },    /* SKIP ON TELEPRINTER FLAG               */
  { FIXED,  "TCF",    06042   },    /* CLEAR TELEPRINTER FLAG                 */
  { FIXED,  "TPC",    06044   },    /* LOAD TELEPRINTER & PRINT               */
  { FIXED,  "TSK",    06045   },    /* SKIP IF TELETYPE INTERRUPT             */
  { FIXED,  "TLS",    06046   },    /* LOAD TELPRINTER & CLEAR FLAG           */
  /* High Speed Paper Tape Reader                                             */
  { FIXED,  "RSF",    06011   },    /* SKIP ON READER FLAG                    */
  { FIXED,  "RRB",    06012   },    /* READ READER BUFFER AND CLEAR FLAG      */
  { FIXED,  "RFC",    06014   },    /* READER FETCH CHARACTER                 */
  /* PC8-E High Speed Paper Tape Reader & Punch                               */
  { FIXED,  "RPE",    06010   },    /* Set interrupt enable for reader/punch  */
  { FIXED,  "PCE",    06020   },    /* Clear interrupt enable for rdr/punch   */
  { FIXED,  "RCC",    06016   },    /* Read reader buffer, clear flags & buf, */
                                    /* and fetch character.                   */
  /* High Speed Paper Tape Punch                                              */
  { FIXED,  "PSF",    06021   },    /* SKIP ON PUNCH FLAG                     */
  { FIXED,  "PCF",    06022   },    /* CLEAR ON PUNCH FLAG                    */
  { FIXED,  "PPC",    06024   },    /* LOAD PUNCH BUFFER AND PUNCH CHARACTER* */
  { FIXED,  "PLS",    06026   },    /* LOAD PUNCH BUFFER AND CLEAR FLAG       */

  /* DECassette TU60 (RK 20071008)                                            */
  { FIXED,  "KCLR",   06700   },    /* Clear all (clear A and B)              */
  { FIXED,  "KSDR",   06701   },    /* Skip if data flag set                  */
  { FIXED,  "KSEN",   06702   },    /* Skip if EOT/BOT, not ready, or empty   */
  { FIXED,  "KSBF",   06703   },    /* Skip if ready flag set                 */
  { FIXED,  "KLSA",   06704   },    /* AC4-11 -> A, clear A, -(AC4-11) -> A   */
  { FIXED,  "KSAF",   06705   },    /* Skip on any flag or error              */
  { FIXED,  "KGOA",   06706   },    /* Assert status A and transfer data to AC*/
  { FIXED,  "KRSB",   06707   },    /* Transfer B -> AC4-11                   */

  /* DECtape Transport Type TU55 and DECtape Control Type TC01                */
  { FIXED,  "DTRA",   06761   },    /* Contents of status register is ORed    */
                                    /* into AC bits 0-9                       */
  { FIXED,  "DTCA",   06762   },    /* Clear status register A, all flags     */
                                    /* undisturbed                            */
  { FIXED,  "DTXA",   06764   },    /* Status register A loaded by exclusive  */
                                    /* OR from AC.  If AC bit 10=0, clear     */
                                    /* error flags; if AC bit 11=0, DECtape   */
                                    /* control flag is cleared.               */
  { FIXED,  "DTLA",   06766   },    /* Combination of DTCA and DTXA           */
  { FIXED,  "DTSF",   06771   },    /* Skip if error flag is 1 or if DECtape  */
                                    /* control flag is 1                      */
  { FIXED,  "DTRB",   06772   },    /* Contents of status register B is       */
                                    /* ORed into AC                           */
  { FIXED,  "DTLB",   06774   },    /* Memory field portion of status         */
                                    /* register B loaded from AC bits 6-8     */
  /* Disk File and Control, Type DF32                                         */
  { FIXED,  "DCMA",   06601   },    /* CLEAR DISK MEMORY REQUEST AND          */
                                    /* INTERRUPT FLAGS                        */
  { FIXED,  "DMAR",   06603   },    /* LOAD DISK FROM AC, CLEAR AC READ       */
                                    /* INTO CORE, CLEAR INTERRUPT FLAG        */
  { FIXED,  "DMAW",   06605   },    /* LOAD DISK FROM AC, WRITE ONTO DISK     */
                                    /* FROM CORE, CLEAR INTERRUPT FLAG        */
  { FIXED,  "DCEA",   06611   },    /* CLEAR DISK EXTENDED ADDRESS AND        */
  { FIXED,  "DSAC",   06612   },    /* SKIP IF ADDRESS CONFIRMED FLAG = 1     */
                                    /* MEMORY ADDRESS EXTENSION REGISTER      */
  { FIXED,  "DEAL",   06615   },    /* CLEAR DISK EXTENDED ADDRESS AND        */
                                    /* MEMORY ADDRESS EXTENSION REGISTER      */
                                    /* AND LOAD SAME FROM AC                  */
  { FIXED,  "DEAC",   06616   },    /* CLEAR AC, LOAD AC FROM DISK EXTENDED   */
                                    /* ADDRESS REGISTER, SKIP IF ADDRESS      */
                                    /* CONFIRMED FLAG = 1                     */
  { FIXED,  "DFSE",   06621   },    /* SKIP IF PARITY ERROR, DATA REQUEST     */
                                    /* LATE, OR WRITE LOCK SWITCH FLAG = 0    */
                                    /* (NO ERROR)                             */
  { FIXED,  "DFSC",   06622   },    /* SKIP IF COMPLETION FLAG = 1 (DATA      */
                                    /* TRANSFER COMPLETE)                     */
  { FIXED,  "DMAC",   06626   },    /* CLEAR AC, LOAD AC FROM DISK MEMORY     */
                                    /* ADDRESS REGISTER                       */
  /* Disk File and Control, Type RF08                                         */
  { FIXED,  "DCIM",   06611   },
  { FIXED,  "DIML",   06615   },
  { FIXED,  "DIMA",   06616   },
  { FIXED,  "DISK",   06623   },
  { FIXED,  "DCXA",   06641   },
  { FIXED,  "DXAL",   06643   },
  { FIXED,  "DXAC",   06645   },
  { FIXED,  "DMMT",   06646   },
  /* Memory Extension Control, Type 183                                       */
  { FIXED,  "CDF",    06201   },    /* CHANGE DATA FIELD                      */
  { FIXED,  "CIF",    06202   },    /* CHANGE INSTRUCTION FIELD               */
  { FIXED,  "CDI",    06203   },    /* Change data & instrution field.        */
  { FIXED,  "RDF",    06214   },    /* READ DATA FIELD                        */
  { FIXED,  "RIF",    06224   },    /* READ INSTRUCTION FIELD                 */
  { FIXED,  "RIB",    06234   },    /* READ INTERRUPT BUFFER                  */
  { FIXED,  "RMF",    06244   },    /* RESTORE MEMORY FIELD                   */
  /* Memory Parity, Type MP8/I (MP8/L)                                        */
  { FIXED,  "SMP",    06101   },    /* SKIP IF MEMORY PARITY FLAG = 0         */
  { FIXED,  "CMP",    06104   },    /* CLEAR MEMORY PAIRTY FLAG               */
  /* Memory Parity, Type MP8-E (PDP8/e)                                       */
  { FIXED,  "DPI",    06100   },    /* Disable parity interrupt.              */
  { FIXED,  "SNP",    06101   },    /* Skip if no parity error.               */
  { FIXED,  "EPI",    06103   },    /* Enable parity interrupt.               */
  { FIXED,  "CNP",    06104   },    /* Clear parity error flag.               */
  { FIXED,  "CEP",    06106   },    /* Check for even parity.                 */
  { FIXED,  "SPO",    06107   },    /* Skip on parity option.                 */
  /* Data Communications Systems, Type 680I                                   */
  { FIXED,  "TTINCR", 06401   },    /* The content of the line select         */
                                    /* register is incremented by one.        */
  { FIXED,  "TTI",    06402   },    /* The line status word is read and       */
                                    /* sampled.  If the line is active for    */
                                    /* the fourth time, the line bit is       */
                                    /* shifted into the character assembly    */
                                    /* word.  If the line bit is active for   */
                                    /* a number of times less than four,      */
                                    /* the count is incremented.  If the      */
                                    /* line is not active, the active/inac-   */
                                    /* tive status of the line is recorded    */
  { FIXED,  "TTO",    06404   },    /* The character in the AC is shifted     */
                                    /* right one position, zeros are shifted  */
                                    /* into vacated positions, and the orig-  */
                                    /* inal content of AC11 is transferred    */
                                    /* out of the computer on the TTY line.   */
  { FIXED,  "TTCL",   06411   },    /* The line select register is cleared.   */
  { FIXED,  "TTSL",   06412   },    /* The line select register is loaded by  */
                                    /* an OR transfer from the content of     */
                                    /* of AC5-11, the the AC is cleared.      */
  { FIXED,  "TTRL",   06414   },    /* The content of the line select regis-  */
                                    /* ter is read into AC5-11 by an OR       */
                                    /* transfer.                              */
  { FIXED,  "TTSKP",  06421   },    /* Skip if clock flag is a 1.             */
  { FIXED,  "TTXON",  06424   },    /* Clock 1 is enabled to request a prog-  */
                                    /* ram interrupt and clock 1 flag is      */
                                    /* cleared.                               */
  { FIXED,  "TTXOF",  06422   },    /* Clock 1 is disabled from causing a     */
                                    /* program interrupt and clock 1 flag     */
                                    /* is cleared.                            */
};      /* End-of-Symbols for Permanent Symbol Table                          */

/* Global variables                                                           */
SYM_T *symtab;                  /* Symbol Table                               */
int    symbol_top;              /* Number of entries in symbol table.         */

SYM_T *fixed_symbols;           /* Start of the fixed symbol table entries.   */
int    number_of_fixed_symbols;

/*----------------------------------------------------------------------------*/

WORD16 *xreftab;                /* Start of the concordance table.            */

ERRSAVE_T error_list[20];
int     save_error_count;

#define GET_PAGE_INDEX(x) (((x) & 07600) >> 7)
#define MAX_PAGES 32
LPOOL_T cp[MAX_PAGES];             /* Storage for page constants.       */
int     max_page_used[MAX_PAGES];

char   s_detected[] = "detected";
char   s_error[]    = "error";
char   s_errors[]   = "errors";
char   s_no[]       = "No";
char   s_page[]     = "Page";
char   s_symtable[] = "Symbol Table";
char   s_xref[]     = "Cross Reference";
char   s_generated[] = "generated";
char   s_link[]    = "link";
char   s_links[]   = "links";

/* Assembler diagnostic messages.                                             */
/* Some attempt has been made to keep continuity with the PAL-III and         */
/* MACRO-8 diagnostic messages.  If a diagnostic indicator, (e.g., IC)        */
/* exists, then the indicator is put in the listing as the first two          */
/* characters of the diagnostic message.  The PAL-III indicators where used   */
/* when there was a choice between using MACRO-8 and PAL-III indicators.      */
/* The character pairs and their meanings are:                                */
/*      DT  Duplicate Tag (symbol)                                            */
/*      IC  Illegal Character                                                 */
/*      ID  Illegal Redefinition of a symbol.  An attempt was made to give    */
/*          a symbol a new value not via =.                                   */
/*      IE  Illegal Equals  An equal sign was used in the wrong context,      */
/*          (e.g., A+B=C, or TAD A+=B)                                        */
/*      II  Illegal Indirect  An off page reference was made, but a literal   */
/*          could not be generated because the indirect bit was already set.  */
/*      IR  Illegal Reference (address is not on current page or page zero)   */
/*      ND  No $ (the program terminator) at end of file.                     */
/*      PE  Current, Non-Zero Page Exceeded (literal table flowed into code)  */
/*      RD  ReDefintion of a symbol                                           */
/*      ST  Symbol Table full                                                 */
/*      UA  Undefined Address (undefined symbol)                              */
/*      ZE  Zero Page Exceeded (see above, or out of space)                   */
EMSG_T  duplicate_label     = { "DT duplicate",  "duplicate label" };
EMSG_T  illegal_blank       = { "IC illegal blank", "illegal blank" };
EMSG_T  illegal_character   = { "IC illegal char",  "illegal character" };
EMSG_T  illegal_expression  = { "IC in expression", "illegal expression" };
EMSG_T  label_syntax        = { "IC label syntax",  "label syntax" };
EMSG_T  not_a_number        = { "IC numeric syntax", "numeric syntax of" };
EMSG_T  number_not_radix    = { "IC radix", "number not in current radix"};
EMSG_T  symbol_syntax       = { "IC symbol syntax", "symbol syntax" };
EMSG_T  illegal_equals      = { "IE illegal =",  "illegal equals" };
EMSG_T  illegal_indirect    = { "II off page",   "illegal indirect" };
EMSG_T  illegal_reference   = { "IR off page",   "illegal reference" };
EMSG_T  undefined_symbol    = { "UD undefined",  "undefined symbol" };
EMSG_T  redefined_symbol    = { "RD redefined",  "redefined symbol" };
EMSG_T  illegal_redefine    = { "ID redefined",  "Illegal redefine of symbol" };
EMSG_T  literal_overflow    = { "PE page exceeded",
                                   "current page literal capacity exceeded" };
EMSG_T  pz_literal_overflow = { "ZE page exceeded",
                                   "page zero capacity exceeded" };
EMSG_T  dubl_overflow       = { "dubl overflow",  "DUBL value overflow" };
EMSG_T  fltg_overflow       = { "fltg overflow",  "FLTG value overflow" };
EMSG_T  zblock_too_small    = { "expr too small", "ZBLOCK value too small" };
EMSG_T  zblock_too_large    = { "expr too large", "ZBLOCK value too large" };
EMSG_T  end_of_file         = { "ND no $ at EOF", "No $ at End-of-File" };
EMSG_T  no_pseudo_op        = { "not implemented",
                                   "not implemented pseudo-op" };
EMSG_T  illegal_field_value = { "expr out of range",
                                   "field value not in range of 0 through 7" };
EMSG_T  literal_gen_off     = { "literals off",
                                                "literal generation is off" };
EMSG_T  no_literal_value    = { "no value",  "no literal value" };
EMSG_T  text_string         = { "no delimiter",
                                    "text string delimiters not matched" };
EMSG_T  in_rim_mode         = { "not OK in rim mode"
                                    "FIELD pseudo-op not valid in RIM mode" };
EMSG_T  lt_expected         = { "'<' expected",  "'<' expected" };
EMSG_T  symbol_table_full   = { "ST Symbol Tbl Full",
                                                    "Symbol Table Full" };
/*----------------------------------------------------------------------------*/

FILE   *errorfile;
FILE   *infile;
FILE   *listfile;
FILE   *listsave;
FILE   *objectfile;
FILE   *objectsave;

char    errorpathname[NAMELEN];
char    filename[NAMELEN];
char    listpathname[NAMELEN];
char    objectpathname[NAMELEN];
char   *pathname;
char    permpathname[NAMELEN];

int     tabstops;               /* number of characters to expand a tab to   */
int     list_lineno;
int     list_pageno;
char    list_title[LINELEN];
BOOL    list_title_set;         /* Set if TITLE pseudo-op used.               */
char    line[LINELEN];          /* Input line.                                */
int     lineno;                 /* Current line number.                       */
int     page_lineno;            /* print line number on current page.         */
BOOL    listed;                 /* Listed flag.                               */
BOOL    listedsave;

int     cc;                     /* Column Counter (char position in line).    */
WORD16  checksum;               /* Generated checksum                         */
BOOL    binary_data_output;     /* Set true when data has been output.        */
WORD16  clc;                    /* Location counter                           */
WORD16  cplc;                   /* Current page literal counter.              */
char    delimiter;              /* Character immediately after eval'd term.   */
int     errors;                 /* Number of errors found so far.             */
int     links;                  /* Number of links generated so far.          */
BOOL    error_in_line;          /* TRUE if error on current line.             */
int     errors_pass_1;          /* Number of errors on pass 1.                */
WORD16  field;                  /* Current field                              */
WORD16  fieldlc;                /* location counter without field portion.    */
BOOL    fltg_input;             /* TRUE when doing floating point input.      */
BOOL    indirect_generated;     /* TRUE if an off page address generated.     */
int     last_xref_lexstart;     /* Column where last xref symbol was located. */
int     last_xref_lineno;       /* Line where last xref symbol was located.   */
int     lexstartprev;           /* Where previous lexeme started.             */
int     lextermprev;            /* Where previous lexeme ended.               */
int     lexstart;               /* Index of current lexeme on line.           */
int     lexterm;                /* Index of character after current lexeme.   */
BOOL    literals_ok;            /* Generate literals, ignore ID redefine err  */
BOOL    perm_redef_error;       /* Make redefining perm sym with labels error */
int     maxcc;                  /* Current line length.                       */
BOOL    overflow;               /* Overflow flag for math routines.           */
int     pass;                   /* Number of current pass.                    */
BOOL    print_permanent_symbols;
WORD16  pzlc;                   /* Page Zero literal counter.                 */
WORD16  radix;                  /* Default number radix.                      */
WORD16  reloc;                  /* The relocation distance.                   */
BOOL    rim_mode;               /* Generate rim format, defaults to bin       */
BOOL    dollar_not_required;    /* $ not required at end of file              */
BOOL    additional_enabled;     /* True if extended functions over PAL8       */
                                /* enabled                                    */
BOOL    symtab_print;           /* Print symbol table flag                    */
BOOL    xref;

FLTG_T  fltg_ac;                /* Value holder for evalFltg()                */
SYM_T   sym_eval = { DEFINED, "", 0 };       /* Value holder for eval()       */
SYM_T   sym_getexpr = { DEFINED, "", 0 };    /* Value holder for getexpr()    */
SYM_T   sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator       */


/******************************************************************************/
/*                                                                            */
/*  Function:  main                                                           */
/*                                                                            */
/*  Synopsis:  Starting point.  Controls order of assembly.                   */
/*                                                                            */
/******************************************************************************/
int main( int argc, char *argv[] )
{
  int     ix;
  int     space;

  /* Set the default values for global symbols.                               */
  binary_data_output = FALSE;
  fltg_input = FALSE;
  literals_ok = TRUE;
  perm_redef_error = FALSE;
  print_permanent_symbols = FALSE;
  rim_mode = FALSE;
  dollar_not_required = FALSE;
  additional_enabled = FALSE;
  symtab_print = FALSE;
  xref = FALSE;
  pathname = NULL;

  /* Get the options and pathnames                                            */
  getArgs( argc, argv );

  /* Setup the error file in case symbol table overflows while installing the */
  /* permanent symbols.                                                       */
  errorfile = fopen( errorpathname, "w" );
  if (errorfile == NULL) {
    fprintf( stderr, "Could not open error file %s: %s\n", errorpathname, strerror(errno));
    exit( -1 );

  }
  errors = 0;
  save_error_count = 0;
  pass = 0;             /* This is required for symbol table initialization.  */
  symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE );

  if( symtab == NULL )
  {
    fprintf( stderr, "Could not allocate memory for symbol table.\n");
    exit( -1 );
  }

  /* Place end marker in symbol table.                                        */
  symtab[0] = sym_undefined;
  symbol_top = 0;
  number_of_fixed_symbols = symbol_top;
  fixed_symbols = &symtab[symbol_top - 1];

  /* Enter the pseudo-ops into the symbol table                               */
  for( ix = 0; ix < DIM( pseudo ) - 
        (additional_enabled ? 0 : NUMBER_ADDITIONAL_PSEUDO) ; ix++ )
  {
    defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 );
  }

  /* Enter the predefined symbols into the table.                             */
  /* Also make them part of the permanent symbol table.                       */
  for( ix = 0; ix < DIM( really_permanent_symbols ); ix++ )
  {
    defineSymbol( really_permanent_symbols[ix].name,
                  really_permanent_symbols[ix].val,
                  really_permanent_symbols[ix].type | DEFFIX , 0 );
  }

  /* Enter the predefined symbols into the table.                             */
  /* Also make them part of the permanent symbol table.                       */
  for( ix = 0; ix < DIM( permanent_symbols ); ix++ )
  {
    defineSymbol( permanent_symbols[ix].name,
                  permanent_symbols[ix].val,
                  permanent_symbols[ix].type | DEFFIX , 0 );
  }

  number_of_fixed_symbols = symbol_top;
  fixed_symbols = &symtab[symbol_top - 1];

  /* Do pass one of the assembly                                              */
  checksum = 0;
  pass = 1;
  page_lineno = LIST_LINES_PER_PAGE;
  onePass();
  errors_pass_1 = errors;

  /* Set up for pass two                                                      */
  rewind( infile );
  /*Opened in main errorfile = fopen( errorpathname, "w" );*/
  objectfile = fopen( objectpathname, "wb" );
  if (objectfile == NULL) {
    fprintf( stderr, "Could not open object file %s: %s\n", objectpathname, strerror(errno));
    exit( -1 );

  }
  objectsave = objectfile;

  listfile = fopen( listpathname, "w" );
  if (listfile == NULL) {
    fprintf( stderr, "Could not open list file %s: %s\n", listpathname, strerror(errno));
    exit( -1 );

  }
  listsave = NULL;

  punchLeader( 0 );
  checksum = 0;

  /* Do pass two of the assembly                                              */
  errors = 0;
  save_error_count = 0;
  page_lineno = LIST_LINES_PER_PAGE;

  if( xref )
  {
    /* Get the amount of space that will be required for the concordance.     */
    for( space = 0, ix = 0; ix < symbol_top; ix++ )
    {
      symtab[ix].xref_index = space;    /* Index into concordance table.      */
      space += symtab[ix].xref_count + 1;
      symtab[ix].xref_count = 0;        /* Clear the count for pass 2.        */

    }
    /* Allocate the necessary space.                                          */
    xreftab = (WORD16 *) malloc( sizeof( WORD16 ) * space );

    /* Clear the cross reference space.                                       */
    for( ix = 0; ix < space; ix++ )
    {
      xreftab[ix] = 0;
    }
  }
  pass = 2;
  onePass();

  /* Undo effects of NOPUNCH for any following checksum                       */
  objectfile = objectsave;
  punchChecksum();

  /* Works great for trailer.                                                 */
  punchLeader( 8 );

  /* undo effects of XLIST for any following output to listing file.          */
  if( listfile == NULL )
  {
    listfile = listsave;
  }

  /* Display value of error counter.                                          */
  if( errors == 0 )
  {
    fprintf( listfile, "\n      %s %s %s\n", s_no, s_detected, s_errors );
  }
  else
  {
    fprintf( errorfile, "\n      %d %s %s\n", errors, s_detected,
                                        ( errors == 1 ? s_error : s_errors ));
    fprintf( listfile, "\n      %d %s %s\n", errors, s_detected,
                                        ( errors == 1 ? s_error : s_errors ));
    fprintf( stderr,   "      %d %s %s\n", errors, s_detected,
                                        ( errors == 1 ? s_error : s_errors ));
  }
  /* Display value of link counter.                                          */
  if( links == 0 )
  {
    fprintf( listfile, "      %s %s %s\n", s_no, s_links, s_generated );
  }
  else
  {
    fprintf( errorfile, "      %d %s %s\n", links, 
                                        ( links == 1 ? s_link : s_links ), 
                                        s_generated);
    fprintf( listfile, "      %d %s %s\n", links, 
                                        ( links == 1 ? s_link : s_links ), 
                                        s_generated);
    fprintf( stderr, "      %d %s %s\n", links, 
                                        ( links == 1 ? s_link : s_links ), 
                                        s_generated);
  }

  if( symtab_print )
  {
    printSymbolTable();
  }

  if( print_permanent_symbols )
  {
    printPermanentSymbolTable();
  }

  if( xref )
  {
    printCrossReference();
  }

  fclose( objectfile );
  fclose( listfile );
  fclose( errorfile );
  if( errors == 0 && errors_pass_1 == 0 )
  {
    remove( errorpathname );
  }

  return( errors != 0 );
} /* main()                                                                   */

/******************************************************************************/
/*                                                                            */
/*  Function:  getArgs                                                        */
/*                                                                            */
/*  Synopsis:  Parse command line, set flags accordingly and setup input and  */
/*             output files.                                                  */
/*                                                                            */
/******************************************************************************/
void getArgs( int argc, char *argv[] )
{
  int  len;
  int  ix, jx;

  /* Set the defaults                                                         */
  errorfile = NULL;
  infile = NULL;
  listfile = NULL;
  listsave = NULL;
  objectfile = NULL;
  objectsave = NULL;
  tabstops = 8;

  for( ix = 1; ix < argc; ix++ )
  {
    if( argv[ix][0] == '-' )
    {
      for( jx = 1; argv[ix][jx] != 0; jx++ )
      {
        switch( argv[ix][jx] )
        {
        case '$':
          dollar_not_required = TRUE;
          break;

        case 'd':
          symtab_print = TRUE;
          break;

        case 'a':
          additional_enabled = TRUE;
          break;

        case 'r':
          rim_mode = TRUE;
          break;

        case 'e':
          literals_ok = FALSE;
          break;

        case 'l':
          literals_ok = TRUE;
          break;

        case 'n':
          perm_redef_error = TRUE;
          break;

        case 'p':
          print_permanent_symbols = TRUE;
          break;

        /* Added -tN; RK 20071029 */
        /* Damn, this is ugly, we should use getopt() */
        case 't':
          if (argv [ix][jx + 1]) {
            tabstops = atoi (argv [ix] + (jx + 1));
            /* advance past numbers */
            for (jx++; argv [ix][jx]; jx++) ;
            jx--;
          } else {
            ix++;
            if (ix >= argc) {
              fprintf( stderr, "%s: missing argument for -t, expected number of tabsopts\n", argv[0] );
              exit( -1 );
            }
            for (jx = 0; argv [ix][jx]; jx++) ;
            jx--;
            tabstops = atoi (argv [ix]);
          }
          break;

        case 'x':
          xref = TRUE;
          break;

        case 'v':
          fprintf( stderr, "%s\n", release );
          fflush( stderr );
          exit( -1 );
          break;

        default:
          fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] );
        case 'h':
          fprintf( stderr, " -$ -- allow file to not end with $\n" );
          fprintf( stderr, " -a -- enable additional function not in PAL8\n" );
          fprintf( stderr, " -d -- dump symbol table\n" );
          fprintf( stderr, " -e -- error if link generated\n" );
          fprintf( stderr, " -h -- show this help\n" );
          fprintf( stderr, " -l -- generate literal/link (default)\n" );
          fprintf( stderr, " -n -- no redefining with label permanent symbols\n" );
          fprintf( stderr, " -p -- output permanent symbols to file\n" );
          fprintf( stderr, " -r -- output rim format file\n" );
          fprintf( stderr, " -t N -- set tab stops to N\n" );
          fprintf( stderr, " -v -- display version\n" );
          fprintf( stderr, " -x -- output cross reference to file\n" );
          fflush( stderr );
          exit( -1 );
        } /* end switch                                                       */
      } /* end for                                                            */
    }
    else
    {
      if( pathname != NULL )
      {
        fprintf( stderr, "%s: too many input files\n", argv[0] );
        exit( -1 );
      }
      pathname = &argv[ix][0];
    }
  } /* end for                                                                */

  if( pathname == NULL )
  {
    fprintf( stderr, "%s:  no input file specified\n", argv[0] );
    exit( -1 );
  }

  len = strlen( pathname );
  if( len > NAMELEN - 5 )
  {
    fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname );
    exit( -1 );
  }

  /* Now open the input file.                                                 */
  if(( infile = fopen( pathname, "r" )) == NULL )
  {
    fprintf( stderr, "%s: cannot open \"%s\": %s\n", argv[0], pathname, strerror(errno) );
    exit( -1 );
  }

  /* Now make the pathnames                                                   */
  /* Find last '.', if it exists.                                             */
  jx = len - 1;
  while( pathname[jx] != '.'  && pathname[jx] != '/'
      && pathname[jx] != '\\' && jx >= 0 )
  {
    jx--;
  }

  switch( pathname[jx] )
  {
  case '.':
    break;

  case '/':
  case '\\':
    jx = len;
    break;

  default:
    break;
  }

  /* Add the pathname extensions.                                             */
  strncpy( objectpathname, pathname, jx );
  objectpathname[jx] = '\0';
  strcat( objectpathname, rim_mode ? ".rim" : ".bin" );

  strncpy( listpathname, pathname, jx );
  listpathname[jx] = '\0';
  strcat( listpathname, ".lst" );

  strncpy( errorpathname, pathname, jx );
  errorpathname[jx] = '\0';
  strcat( errorpathname, ".err" );

  strncpy( permpathname, pathname, jx );
  permpathname[jx] = '\0';
  strcat( permpathname, ".prm" );

  /* Extract the filename from the path.                                      */
  if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' )
  {
    pathname[1] = '\\';         /* MS-DOS style pathname                      */
  }

  jx = len - 1;
  while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 )
  {
    jx--;
  }
  strcpy( filename, &pathname[jx + 1] );

} /* getArgs()                                                                */

/******************************************************************************/
/*                                                                            */
/*  Function:  clearLiteralTable                                              */
/*                                                                            */
/*  Synopsis:  Clear the cp and max_page_used data storing literal            */
/*             information.                                                   */
/*                                                                            */
/******************************************************************************/
void clearLiteralTable()
{
  int i;

  for (i = 0; i < DIM(cp); i++)
  {
    cp[i].loc = 0200;             /* Points to end of page for [] operands.  */
    cp[i].last_punched = 0200;    /* Points to end of page for [] operands.  */
  }
  memset(max_page_used, 0, sizeof(max_page_used));
}

/******************************************************************************/
/*                                                                            */
/*  Function:  onePass                                                        */
/*                                                                            */
/*  Synopsis:  Do one assembly pass.                                          */
/*                                                                            */
/******************************************************************************/
void onePass()
{
  char    name[SYMLEN];
  WORD16  newclc;
  BOOL    scanning_line;
  int     start;
  SYM_T  *sym;
  int     term;
  WORD16  val;

  clc = 0200;                   /* Default starting address is 200 octal.     */
  field = 0;
  fieldlc = clc & 07777;
  reloc = 0;
  clearLiteralTable();
  listed = TRUE;
  lineno = 0;
  list_pageno = 0;
  list_lineno = 0;
  last_xref_lexstart = 0;
  last_xref_lineno = 0;
  list_title_set = FALSE;
  radix = 8;                    /* Initial radix is octal (base 8).           */

  if( !rim_mode )
  {
    /* Put out initial origin if not in rim mode.                             */
    punchOrigin( clc );
  }

  while( TRUE )
  {
    readLine();
    nextLexeme();

    scanning_line = TRUE;
    while( scanning_line )
    {
      if( isend( line[lexstart] ))
      {
        scanning_line = FALSE;
      }
      else
      {
        switch( line[lexstart] )
        {
        case '/':
          scanning_line = FALSE;
          break;

        case ';':
          nextLexeme();
          break;

        case '$':
          endOfBinary();
          return;

        case '*':
          nextLexeme();             /* Skip '*', (set origin symbol)          */
          newclc = ((getExpr())->val & 07777 ) | field;
          /* Do not change Current Location Counter if an error occurred.     */
          if( !error_in_line )
          {
            if(( newclc & 07600 ) != ( clc & 07600 ) ) 
            {
              /* Current page has changed.                                    */
              punchLiteralPool( cp, 0 );
            }
            clc = newclc - reloc;
            fieldlc = clc & 07777;

            if( !rim_mode )
            {
              /* Not rim mode, put out origin.                                */
              punchOrigin( clc );
            }
            printLine( line, 0, fieldlc, LINE_VAL );
          }
          break;

        default:
          switch( line[lexterm] )
          {
          case ',':
            if( isalpha( line[lexstart] ))
            {
              /* Use lookup so symbol will not be counted as reference.       */
              sym = lookup( lexemeToName( name, lexstart, lexterm ));
              if( M_DEFINED( sym->type ))
              {
                if( (sym->val & 07777) != ( ( clc+reloc ) & 07777) && pass == 2 )
                {
                  errorSymbol( &duplicate_label, sym->name, lexstart );
                }
                sym->type = sym->type | DUPLICATE;
              }
              /* Must call define on pass 2 to generate concordance.          */
              defineLexeme( lexstart, lexterm, ( clc + reloc ), LABEL );
            }
            else
            {
              errorLexeme( &label_syntax, lexstart );
            }
            nextLexeme();           /* skip label                             */
            nextLexeme();           /* skip comma                             */
            break;

          case '=':
            if( isalpha( line[lexstart] ))
            {
              start = lexstart;
              term = lexterm;
              delimiter = line[lexterm];
              nextLexBlank();       /* skip symbol                            */
              nextLexeme();         /* skip trailing =, allow blank           */
              delimiter = line[lexterm];
              val = getExprs();
              defineLexeme( start, term, val, DEFINED );
              printLine( line, 0, val, LINE_VAL );
            }
            else
            {
              errorLexeme( &symbol_syntax, lexstartprev );
              nextLexeme();         /* skip symbol                            */
              nextLexeme();         /* skip trailing =                        */
              getExprs();           /* skip expression                        */
            }
            break;

          default:
            if( isalpha( line[lexstart] ))
            {
              sym = evalSymbol();
              val = sym->val;
              if( M_PSEUDO( sym->type ))
              {
                nextLexeme();         /* Skip symbol                          */
                scanning_line = pseudoOperators( (PSEUDO_T)val & 07777 );
              }
              else
              {
                /* Identifier is not a pseudo-op, interpret as load value     */
                punchOutObject( clc, getExprs() & 07777 );
                incrementClc();
              }
            }
            else
            {
              /* Identifier is a value, interpret as load value               */
              punchOutObject( clc, getExprs() & 07777 );
              incrementClc();
            }
            break;
          } /* end switch                                                     */
          break;
        } /* end switch                                                       */
      } /* end if                                                             */
    } /* end while( scanning_line )                                           */
  } /* end while( TRUE )                                                      */
} /* onePass()                                                                */


/******************************************************************************/
/*                                                                            */
/*  Function:  fixMRIInstruction                                              */
/*                                                                            */
/*  Synopsis:  Now that we have the final value figure out if page 0, current */
/*             page, or indirect needed and max final instruction             */
/*                                                                            */
/******************************************************************************/
WORD16 fixMRIInstruction(WORD16 instruction, WORD16 value)
{
        /* Now have the address part of the MRI instruction.                  */
        if( value < 00200 )
        {
          instruction |= value;        /* Page zero MRI.                             */
        }
        else if( (( fieldlc + reloc ) & 07600 ) <= value
             && value <= ((( fieldlc + reloc ) & 07600) | 0177 ))
        {
          instruction |= ( PAGE_BIT | (value & ADDRESS_FIELD )); /* Current page MRI */
        }
        else
        {
          if(( instruction & INDIRECT_BIT ) == INDIRECT_BIT )
          {
            /* Already indirect, can't generate                               */
            errorSymbol( &illegal_indirect, NULL, lexstartprev );
          }
          else
          {
            if( literals_ok )
            {
              /* Now fix off page reference.                                  */
              /* Search current page literal pool for needed instruction.           */
              /* Set Indirect Current Page                                    */
              instruction |= ( 00600 | insertLiteral( cp, value, GET_PAGE_INDEX(clc) ));
              indirect_generated = TRUE;
              if (pass == 2)
              {
                links++;
              }
            }
            else
            {
              errorSymbol( &illegal_reference, NULL, lexstartprev );
              instruction |= ( value & 0177 );
            }
          }
        }
   return instruction;
}
/******************************************************************************/
/*                                                                            */
/*  Function:  getExprs                                                       */
/*                                                                            */
/*  Synopsis:  Or together a list of blank separated expressions, from the    */
/*             current lexeme onward.  Leave the current lexeme as            */
/*             the last one in the list.                                      */
/*                                                                            */
/******************************************************************************/
WORD16 getExprs()
{
  SYM_T  *symv;
  SYM_T  *symt;
  WORD16  temp;
  SYMTYP  temp_type;
  WORD16  value;
  SYMTYP  value_type;
  BOOL    MRI_held = FALSE;
  WORD16  held_value = 0;

  symv = getExpr();
  value = symv->val;
  value_type = symv->type;

  while( TRUE )
  {
    if( isdone( line[lexstart] ))
    {
      if (MRI_held)
      {
        value = fixMRIInstruction(value, held_value);
      }
      return( value );
    }
    switch( line[lexstart] )
    {
    case ')':
    case ']':
    case '<':
      if (MRI_held)
      {
        value = fixMRIInstruction(value, held_value);
      }
      return( value );

    default:
      break;
    }

    /* Interpret space as logical or                                          */
    symt = getExpr();
    temp = symt->val & 07777;
    temp_type = symt->type;

    switch( value_type & (MRI | MRIFIX))
    {
    case MRI:
    case MRIFIX:
      /* Previous symbol was a Memory Reference Instruction.                  */
      switch( temp_type & (MRI | MRIFIX) )
      {
      case MRI:
      case MRIFIX:
        /* If we have held value don't or in more MRI's to instuction, they */
        /* are now instuction value */
        if (MRI_held)
        {
          held_value |= temp;
        }
        else
        { 
          /* Current symbol is also a Memory Reference Instruction.           */
          value |= temp;          /* Just OR the MRI instructions.            */
        }
        break;

      default:
        held_value |= temp;
        MRI_held = TRUE;
        break;
      }
      break;

    default:
        if (value_type == UNDEFINED || temp_type == UNDEFINED) {
            value = 0;
        } else {
            value |= temp;          /* Normal 12 bit value.                       */
         }
        break;
    }
  } /* end while                                                              */
} /* getExprs()                                                               */


/******************************************************************************/
/*                                                                            */
/*  Function:  getExpr                                                        */
/*                                                                            */
/*  Synopsis:  Get an expression, from the current lexeme onward, leave the   */
/*             current lexeme as the one after the expression.  Expressions   */
/*             contain terminal symbols (identifiers) separated by operators. */
/*                                                                            */
/******************************************************************************/
SYM_T *getExpr()
{
  SYM_T *sym;
  delimiter = line[lexterm];


  if( line[lexstart] == '-' )
  {
    nextLexBlank();
    sym_getexpr = *(eval());
    sym_getexpr.val = ( - sym_getexpr.val ) & 07777;
  }
  else
  {
    if( line[lexstart] == '+' )
    {
      nextLexBlank();
    }
    sym_getexpr = *(eval());
    sym_getexpr.val = sym_getexpr.val & 07777;
  }

  if( is_blank( delimiter ))
  {
    return( &sym_getexpr );
  }

  /* Here we assume the current lexeme is the operator separating the         */
  /* previous operator from the next, if any.                                 */
  while( TRUE )
  {
    /* assert line[lexstart] == delimiter                                     */
    if( is_blank( delimiter ))
    {
      return( &sym_getexpr );
    }

    switch( line[lexstart] )
    {
    case '+':                   /* add                                        */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val += sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '-':                   /* subtract                                   */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val -= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '^':                   /* multiply                                   */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val *= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '%':                   /* divide                                     */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val /= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '&':                   /* and                                        */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val &= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '!':                   /* or                                         */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val |= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    default:
      if( isend( line[lexstart] ))
      {
        return( &sym_getexpr );
      }

      switch( line[lexstart] )
      {
      case '/':
      case ';':
      case ')':
      case ']':
      case '<':
        break;

      case '=':
        errorMessage( &illegal_equals, lexstart );
        moveToEndOfLine();
        sym_getexpr.val = 0;
        break;

      default:
        errorMessage( &illegal_expression, lexstart );
        moveToEndOfLine();
        sym_getexpr.val = 0;
        break;
      }
      return( &sym_getexpr );
    }
  } /* end while                                                              */
} /* getExpr()                                                                */


/******************************************************************************/
/*                                                                            */
/*  Function:  eval                                                           */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme, set delimiter and advance.*/
/*                                                                            */
/******************************************************************************/
SYM_T *eval()
{
  WORD16  digit;
  int     from;
  WORD16  loc;
  SYM_T  *sym;
  WORD16  val;

  val = 0;

  delimiter = line[lexterm];
  if( isalpha( line[lexstart] ))
  {
    sym = evalSymbol();
    if( M_UNDEFINED( sym->type ) && pass == 2 )
    {
      errorSymbol( &undefined_symbol, sym->name, lexstart );
      nextLexeme();
      return( sym );
    }
    else
    {
      nextLexeme();
      return( sym );
    }
  }
  else if( isdigit( line[lexstart] ))
  {
    from = lexstart;
    val = 0;
    while( from < lexterm )
    {
      if( isdigit( line[from] ))
      {
        digit = (WORD16) line[from++] - (WORD16) '0';
        if( digit < radix )
        {
          val = val * radix + digit;
        }
        else
        {
          errorLexeme( &number_not_radix, from - 1 );
          val = 0;
          from = lexterm;
        }
      }
      else
      {
        errorLexeme( &not_a_number, lexstart );
        val = 0;
        from = lexterm;
      }
    }
    nextLexeme();
    sym_eval.val = val;
    return( &sym_eval );
  }
  else
  {
    switch( line[lexstart] )
    {
    case '"':                   /* Character literal                          */
      if( cc + 1 <= maxcc )
      {
        val = line[lexstart + 1] | 0200;
        delimiter = line[lexstart + 2];
        cc = lexstart + 2;
      }
      else
      {
        errorMessage( &no_literal_value, lexstart );
      }
      nextLexeme();
      break;

    case '.':                   /* Value of Current Location Counter          */
      val = (clc & 07777) + reloc;
      nextLexeme();
      break;

    case '[':                   /* Generate literal on page zero.             */
      if( !literals_ok && LITERAL_ERROR)
      {
        errorMessage( &literal_gen_off, lexstart );
      }
      nextLexBlank();           /* Skip bracket                               */
      val = getExprs() & 07777;

      if( line[lexstart] == ']' )
      {
        nextLexBlank();         /* Skip end bracket                           */
      }

      sym_eval.val = (literals_ok || !LITERAL_ERROR) ? insertLiteral( cp , val,
        GET_PAGE_INDEX(field)) : 0;
      return( &sym_eval );

    case '(':                   /* Generate literal on current page.          */
      if( !literals_ok && LITERAL_ERROR)
      {
        errorMessage( &literal_gen_off, lexstart );
      }

      nextLexBlank();           /* Skip paren                                 */
      val = getExprs() & 07777;

      if( line[lexstart] == ')' )
      {
        nextLexBlank();         /* Skip end paren                             */
      }

      loc = (literals_ok || !LITERAL_ERROR) ? insertLiteral( cp, val, GET_PAGE_INDEX(clc) ) : 0;
      sym_eval.val = loc + (( clc + reloc ) & 077600 );
      return( &sym_eval );

    default:
      switch( line[lexstart] )
      {
      case '=':
        errorMessage( &illegal_equals, lexstart );
        moveToEndOfLine();
        break;

      default:
        errorMessage( &illegal_character, lexstart );
        break;
      }
      val = 0;                  /* On error, set value to zero.               */
      nextLexBlank();           /* Go past illegal character.                 */
    }
  }
  sym_eval.val = val;
  return( &sym_eval );
} /* eval()                                                                   */


/******************************************************************************/
/*                                                                            */
/*  Function:  inputDubl                                                      */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a double word.          */
/*                                                                            */
/******************************************************************************/
void inputDubl()
{
  WORD32 dublvalue;
  BOOL   scanning_line;

  scanning_line = TRUE;
  do
  {
    while( scanning_line )
    {
      if( isend( line[lexstart] ))
      {
        scanning_line = FALSE;
      }
      else
      {
        switch( line[lexstart] )
        {
        case '/':
          scanning_line = FALSE;
          break;

        case ';':
          nextLexeme();
          break;

        case '+':
          delimiter = line[lexterm];
          nextLexBlank();
        case '-':
        default:
          if( isdigit( line[lexstart] ) || line[lexstart] == '-' )
          {
            dublvalue = getDublExprs();
            punchOutObject( clc, (WORD16)(( dublvalue >> 12 ) & 07777 ));
            incrementClc();
            punchOutObject( clc, (WORD16)( dublvalue & 07777 ));
            incrementClc();
          }
          else
          {
            return;             /* Non-numeric input, back to assembly.       */
          }
          break;
        } /* end switch                                                       */
      } /* end if                                                             */

      if( error_in_line )
      {
        return;                 /* Error occurred, exit DUBL input mode.      */
      }
    } /* end while( scanning_line )                                           */
    readLine();
    nextLexeme();
    scanning_line = TRUE;
  }
  while( TRUE );
} /* inputDubl()                                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  getDublExprs                                                   */
/*                                                                            */
/*  Synopsis:  Get a DUBL expression.                                         */
/*                                                                            */
/******************************************************************************/
WORD32 getDublExprs()
{
  WORD32 dublvalue;

  dublvalue = getDublExpr();

  while( TRUE )
  {
    if( isdone( line[lexstart] ))
    {
      return( dublvalue );
    }
    else
    {
      errorMessage( &illegal_expression, lexstart - 1 );
      return( 0 );
    }
  } /* end while                                                              */
} /* getDublExprs()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  getDublExpr                                                    */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a double word.  The     */
/*             number is always considered to have a decimal radix.           */
/*                                                                            */
/******************************************************************************/
WORD32 getDublExpr()
{
  WORD32 dublvalue;

  delimiter = line[lexterm];
  if( line[lexstart] == '-' )
  {
    nextLexBlank();
    dublvalue = evalDubl( 0 );
    nextLexeme();
    /* Test for any value greater than 23 bits in length.                     */
    if( (unsigned long int)dublvalue > 040000000L )
    {
      errorMessage( &dubl_overflow, lexstart );
      dublvalue = 0;
    }
    dublvalue = -dublvalue;
  }
  else
  {
    dublvalue = evalDubl( 0 );
    nextLexeme();
    /* Test for any value greater than 23 bits in length.                     */
    if( (unsigned long int)dublvalue > 037777777L )
    {
      errorMessage( &dubl_overflow, lexstart );
      dublvalue = 0;
    }
  }

  if( is_blank( delimiter ))
  {
    return( dublvalue );
  }

  /* Here we assume the current lexeme is the operator separating the         */
  /* previous operator from the next, if any.                                 */
  while( TRUE )
  {
    /* assert line[lexstart] == delimiter                                     */
    if( is_blank( delimiter ))
    {
      errorMessage( &illegal_expression, lexstart );
      moveToEndOfLine();
      dublvalue = 0;
      return( dublvalue );
    }

    switch( line[lexstart] )
    {
    case '+':                   /* add                                        */
    case '-':                   /* subtract                                   */
    case '^':                   /* multiply                                   */
    case '%':                   /* divide                                     */
    case '&':                   /* and                                        */
    case '!':                   /* or                                         */
      errorMessage( &illegal_expression, lexstart );
      moveToEndOfLine();
      dublvalue = 0;
      break;

    default:
      if( isend( line[lexstart] ))
      {
        return( dublvalue );
      }

      switch( line[lexstart] )
      {
      case '/':
      case ';':
        break;

      default:
        errorMessage( &illegal_expression, lexstart );
        moveToEndOfLine();
        dublvalue = 0;
        break;
      }
      return( dublvalue );
    }
  } /* end while                                                              */
} /* getDublExpr()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  evalDubl                                                       */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a double word.  The     */
/*             number is always considered to have a decimal radix.           */
/*                                                                            */
/******************************************************************************/
WORD32 evalDubl( WORD32 initial_value )
{
  WORD32  digit;
  int     from;
  WORD32  dublvalue;
  WORD32  olddublvalue;

  overflow = FALSE;
  delimiter = line[lexterm];
  from = lexstart;
  dublvalue = initial_value;

  while( from < lexterm )
  {
    if( isdigit( line[from] ))
    {
      olddublvalue = dublvalue;
      digit = (WORD32)( line[from++] - '0' );
      dublvalue = dublvalue * 10 + digit;
      if( dublvalue < olddublvalue )
      {
        overflow = TRUE;
      }
    }
    else
    {
      errorLexeme( &not_a_number, from );
      dublvalue = 0;
      from = lexterm;
    }
  }
  return( dublvalue );
} /* evalDubl()                                                               */


/******************************************************************************/
/*                                                                            */
/*  Function:  inputFltg                                                      */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a Floating Point const. */
/*                                                                            */
/******************************************************************************/
void inputFltg()
{
  FLTG_T *fltg;
  BOOL    scanning_line;

  fltg_input = TRUE;            /* Set lexeme scanner for floating point.     */
  scanning_line = TRUE;
  while( TRUE )
  {
    while( scanning_line )
    {
      if( isend( line[lexstart] ))
      {
        scanning_line = FALSE;
      }
      else
      {
        switch( line[lexstart] )
        {
        case '/':
          scanning_line = FALSE;
          break;

        case ';':
          nextLexeme();
          break;

        case '+':
          delimiter = line[lexterm];
          nextLexBlank();
        case '-':
        default:
          if( isdigit( line[lexstart] ) || line[lexstart] == '-' )
          {
            fltg = getFltgExprs();
            punchOutObject( clc, ( fltg->exponent & 07777 ));
            incrementClc();
            punchOutObject( clc, (WORD16)(( fltg->mantissa >> 12 ) & 07777 ));
            incrementClc();
            punchOutObject( clc, (WORD16)( fltg->mantissa & 07777 ));
            incrementClc();
          }
          else
          {
            fltg_input = FALSE; /* Reset lexeme scanner.                      */
            return;             /* Non-numeric input, back to assembly.       */
          }
          break;
        } /* end switch                                                       */
      } /* end if                                                             */

      if( error_in_line )
      {
        fltg_input = FALSE;     /* Reset lexeme scanner.                      */
        return;                 /* Error occurred, exit FLTG input mode.      */
      }
    } /* end while( scanning_line )                                           */
    readLine();
    nextLexeme();
    scanning_line = TRUE;
  }
} /* inputFltg()                                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  getFltgExprs                                                   */
/*                                                                            */
/*  Synopsis:  Get a FLTG expression.                                         */
/*                                                                            */
/******************************************************************************/
FLTG_T *getFltgExprs()
{
  FLTG_T *fltg;

  fltg = getFltgExpr();

  while( TRUE )
  {
    if( isdone( line[lexstart] ))
    {
      return( fltg );
    }
    else
    {
      errorMessage( &illegal_expression, lexstart - 1 );
      return( 0 );
    }
  } /* end while                                                              */
} /* getFltgExprs()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  getFltgExpr                                                    */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a double word.  The     */
/*             number is always considered to have a decimal radix.           */
/*                                                                            */
/******************************************************************************/
FLTG_T *getFltgExpr()
{
  FLTG_T *fltg;

  delimiter = line[lexterm];
  fltg = evalFltg();
  /* Test for any value greater than 23 bits in length.                     */
  if( (unsigned long int)fltg->mantissa> 077777777L )
  {
    errorMessage( &fltg_overflow, lexstart );
  }

  if( is_blank( delimiter ))
  {
    return( fltg );
  }

  /* Here we assume the current lexeme is the operator separating the         */
  /* previous operator from the next, if any.                                 */
  while( TRUE )
  {
    /* assert line[lexstart] == delimiter                                     */
    if( is_blank( delimiter ))
    {
      errorMessage( &illegal_expression, lexstart );
      moveToEndOfLine();
      fltg = 0;
      return( fltg );
    }

    switch( line[lexstart] )
    {
    case '+':                   /* add                                        */
    case '-':                   /* subtract                                   */
    case '^':                   /* multiply                                   */
    case '%':                   /* divide                                     */
    case '&':                   /* and                                        */
    case '!':                   /* or                                         */
      errorMessage( &illegal_expression, lexstart );
      moveToEndOfLine();
      fltg = NULL;
      break;

    default:
      if( isend( line[lexstart] ))
      {
        return( fltg );
      }

      switch( line[lexstart] )
      {
      case '/':
      case ';':
        break;

      default:
        errorMessage( &illegal_expression, lexstart );
        moveToEndOfLine();
        fltg = NULL;
        break;
      }
      return( fltg );
    }
  } /* end while                                                              */
} /* getFltgExpr()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  evalFltg                                                       */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a floating point value. */
/*             Floating point input is alwasy considered decimal.             */
/*             The general format of a floating point number is:              */
/*                +-ddd.dddE+-dd  where each d is a decimal digit.            */
/*                                                                            */
/******************************************************************************/
FLTG_T *evalFltg()
{
  int      current_state;
  int      current_col;
  WORD16   exponent;
  FLTG_T  *fltg;
  WORD32   input_value;
  BOOL     negate;
  BOOL     negate_exponent;
  int      next_state;
  int      right_digits;

  /* This uses a lexical analyzer to parse the floating point format.         */
  static BYTE state_table[][10] =
  {
    /*  0   1   2   3   4   5   6  Oolumn index                               */
    /*  +   -   d   .   E  sp   p      State  Comment                         */
     {  2,  1,  3,  4, 10, 10, 10  },  /*  0  Initial state.                  */
     { 11, 11,  3,  4, 11, 11, 11  },  /*  1  -                               */
     { 11, 11,  3,  4, 11, 11, 11  },  /*  2  +                               */
     { 10, 10, 10,  4,  6, 10, 10  },  /*  3  # (+-ddd)                       */
     { 11, 11,  5, 11, 11, 10, 10  },  /*  4  . (+-ddd.)                      */
     { 11, 11, 11, 11,  6, 10, 11  },  /*  5  # (+-ddd.ddd)                   */
     {  8,  7,  9, 11, 11, 11, 11  },  /*  6  E (+-ddd.dddE)                  */
     { 11, 11,  9, 11, 11, 11, 11  },  /*  7  - (+-ddd.dddE-                  */
     { 11, 11,  9, 11, 11, 11, 11  },  /*  8  + (+-ddd.dddE+                  */
     { 11, 11, 11, 11, 11, 10, 11  }   /*  9  # (+-ddd.dddE+-dd               */
                                       /* 10  Completion state                */
                                       /* 11  Error state.                    */
  };

  delimiter = line[lexterm];
  fltg = &fltg_ac;
  fltg->exponent = 0;
  fltg->mantissa = 0;
  input_value = 0;
  negate = FALSE;
  negate_exponent = FALSE;
  next_state = 0;
  exponent = 0;
  right_digits = 0;
  current_state = 0;

  while( TRUE )
  {
    /* Classify character.  This is the column index.                         */
    switch( line[lexstart] )
    {
    case '+':
      current_col = 0;
      break;

    case '-':
      current_col = 1;
      break;

    case '.':
      current_col = 3;
      break;

    case 'E':  case 'e':
      current_col = 4;
      break;

    default:
      if( isdigit( line[lexstart] ))
      {
        current_col = 2;
      }
      else if( isdone( line[lexstart] ))
      {
        current_col = 5;
      }
      else
      {
        current_col = 6;
      }
      break;
    }

    next_state = state_table[current_state][current_col];

    switch( next_state )
    {
    case 1:                             /*  -                                 */
      negate = TRUE;
    case 2:                             /*  +                                 */
      delimiter = line[lexterm];        /* Move past the + or - character.    */
      nextLexBlank();
      break;

    case 3:                             /* Number  (+-ddd)                    */
      input_value = evalDubl( 0 );      /* Integer part of the number.        */
      nextLexeme();                     /* Move past previous lexeme.         */
      break;

    case 4:
      delimiter = line[lexterm];
      nextLexBlank();                   /* Move past the . character.         */
      break;

    case 5:                             /* .  (+-ddd.ddd)                     */
                                        /* Fractional part of the number.     */
      input_value = evalDubl( input_value );
      right_digits = lexterm - lexstart;/* Digit count to right of decimal.   */
      nextLexeme();                     /* Move past previous lexeme.         */
      break;

    case 6:                             /* E  (+-ddd.dddE)                    */
      delimiter = line[lexterm];        /* Move past the E.                   */
      nextLexBlank();
      break;

    case 7:                             /* -  (+-ddd.dddE-)                   */
      negate_exponent = TRUE;
    case 8:                             /* +  (+-ddd.dddE+)                   */
      delimiter = line[lexterm];        /* Move past the + or - character.    */
      nextLexBlank();
      break;

    case 9:                             /* #  (+-ddd.dddE+-dd)                */
      exponent = (int)evalDubl( 0 );    /* Exponent of floating point number. */
      if( negate_exponent ) { exponent = - exponent; }
      nextLexeme();                     /* Move past previous lexeme.         */
      break;

    case 10:                            /* Floating number parsed, convert    */
                                        /* the number.                        */
      /* Get the exponent for the number as input.                            */
      exponent -= right_digits;

      /* Remove trailing zeros and adjust the exponent accordingly.           */
      while(( input_value % 10 ) == 0 )
      {
        input_value /= 10;
        exponent++;
      }

      /* Convert the number to floating point.  The number is calculated with */
      /* a 27 bit mantissa to improve precision.  The extra 3 bits are        */
      /* discarded after the result has been calculated.                      */
      fltg->exponent = 26;
      fltg->mantissa = input_value << 3;
      normalizeFltg( fltg );


      while( exponent != 0 )
      {
        if( exponent < 0 )
        {
          /* Decimal point is to the left. */
          fltg->mantissa /= 10;
          normalizeFltg( fltg );
          exponent++;
        }
        else if( exponent > 0 )
        {
          /* Decimal point is to the right. */
          fltg->mantissa *= 10;
          normalizeFltg( fltg );
          exponent--;
        }
      }

      /* Discard the extra precsion used for calculating the number.          */
      fltg->mantissa >>= 3;
      fltg->exponent -= 3;
      if( negate )
      {
        fltg->mantissa = (- fltg->mantissa ) & 077777777L;
      }
      return( fltg );

    case 11:                            /* Error in format.                   */
      /* Not a properly constructued floating point number.                   */
      return( fltg );
    default:
      break;
    }
    /* Set state for next pass through the loop.                              */
    current_state = next_state;
  }
} /* evalFltg()                                                               */



/******************************************************************************/
/*                                                                            */
/*  Function:  normalizeFltg                                                  */
/*                                                                            */
/*  Synopsis:  Normalize a PDP-8 double precision floating point number.      */
/*                                                                            */
/******************************************************************************/
void normalizeFltg( FLTG_T *fltg )
{
  /* Normalize the floating point number.                                     */
  if( fltg->mantissa != 0 )
  {
    if(( fltg->mantissa & ~0x3FFFFFFL ) == 0 )
    {
      while(( fltg->mantissa & ~0x1FFFFFFL ) == 0 )

      {
        fltg->mantissa <<= 1;
        fltg->exponent--;
      }
    }
    else
    {
      while(( fltg->mantissa & ~0x3FFFFFFL ) != 0 )
      {
        fltg->mantissa >>= 1;
        fltg->exponent++;
      }
    }
  }
  else
  {
    fltg->exponent = 0;
  }
  return;
}


/******************************************************************************/
/*                                                                            */
/*  Function:  incrementClc                                                   */
/*                                                                            */
/*  Synopsis:  Set the next assembly location.  Test for collision with       */
/*             the literal tables.                                            */
/*                                                                            */
/******************************************************************************/
WORD16 incrementClc()
{
  testForLiteralCollision( clc );

  /* Incrementing the location counter is not to change field setting.        */
  clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 );
  fieldlc = clc & 07777;
  return( clc );
} /* incrementClc()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  testForLiteralCollision                                        */
/*                                                                            */
/*  Synopsis:  Test the given location for collision with the literal tables. */
/*                                                                            */
/******************************************************************************/
BOOL testForLiteralCollision( WORD16 loc )
{
  WORD16  pagelc;
  BOOL    result = FALSE;
  WORD16  tmppage;
  int     tmpfield;

  tmpfield = GET_PAGE_INDEX(loc);
  tmppage = loc & 07600;
  pagelc  = loc & 00177;

  if ( pagelc > max_page_used[tmpfield] ) 
  {
     max_page_used[tmpfield] = pagelc;
  }
  if ( pagelc >= cp[tmpfield].loc )
  {
    if ( tmppage == 0 )
    {
      errorMessage( &pz_literal_overflow, -1 );
    }
    else
    {
      errorMessage( &literal_overflow, -1 );
    }
    result = TRUE;
  }

  return( result );
} /* testForLiteralCollision()                                                */


/******************************************************************************/
/*                                                                            */
/*  Function:  readLine                                                       */
/*                                                                            */
/*  Synopsis:  Get next line of input.  Print previous line if needed.        */
/*                                                                            */
/******************************************************************************/
void readLine()
{
  WORD16  ix;
  WORD16  iy;
  char   inpline[LINELEN];

  listLine();                   /* List previous line if needed.              */
  lineno++;                     /* Count lines read.                          */
  indirect_generated = FALSE;   /* Mark no indirect address generated.        */
  listed = FALSE;               /* Mark as not listed.                        */
  cc = 0;                       /* Initialize column counter.                 */
  lexstartprev = 0;

  error_in_line = FALSE;
  if(( fgets( inpline, LINELEN - 1, infile )) == NULL )
  {
    inpline[0] = '$';
    inpline[1] = '\n';
    inpline[2] = '\0';
    if (!dollar_not_required) {
       error_in_line = TRUE;
    }
  }

  /* Remove any tabs from the input line by inserting the required number     */
  /* of spaces to simulate N character tab stops, where N defaults to 8 and   */
  /* is set by the command line option -t. (RK 20071029)                      */
  /* Ignore \r if there is one.  (DPI 20150501)                               */
  for( ix = 0, iy = 0; inpline[ix] != '\0' && iy < (LINELEN - 2); ix++ )
  {
    switch( inpline[ix] )
    {
    case '\t':
      do
      {
        line[iy] = ' ';
        iy++;
      }
      while(( iy % tabstops ) != 0 && iy < (LINELEN - 2));
      break;

    case '\r':        /* dont copy the carriage return */
       break;

    default:
      line[iy] = inpline[ix];
      iy++;
      break;
    }
  }
  if (iy >= (LINELEN - 2)) {
  	line [iy] = '\n';
  	iy++;
  } 
  line[iy] = '\0';

  maxcc = iy;                   /* Save the current line length.              */
  /* Save the first line for possible use as the listing title.               */
  if( lineno == 1 )
  {
    strcpy( list_title, line );
  }
} /* readLine()                                                               */


/******************************************************************************/
/*                                                                            */
/*  Function:  listLine                                                       */
/*                                                                            */
/*  Synopsis:  Output a line to the listing file.                             */
/*                                                                            */
/******************************************************************************/
void listLine()
/* generate a line of listing if not already done!                            */
{
  if( listfile != NULL && listed == FALSE )
  {
    printLine( line, 0, 0, LINE );
  }
} /* listLine()                                                               */


/******************************************************************************/
/*                                                                            */
/*  Function:  printPageBreak                                                 */
/*                                                                            */
/*  Synopsis:  Output a Top of Form and listing header if new page necessary. */
/*                                                                            */
/******************************************************************************/
void printPageBreak()
{
  if( page_lineno >= LIST_LINES_PER_PAGE )
         /*  ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */
  {
    if( !list_title_set )
    {
      /* strcpy( list_title, line ); */
      if( list_title[strlen(list_title) - 1] == '\n' )
      {
        list_title[strlen(list_title) - 1] = '\0';
      }
      if( strlen( list_title ) > TITLELEN )
      {
        list_title[TITLELEN] = '\0';
      }
      list_title_set = TRUE;
    }
    topOfForm( list_title, NULL );

  }
} /* printPageBreak()                                                         */


/******************************************************************************/
/*                                                                            */
/*  Function:  printLine                                                      */
/*                                                                            */
/*  Synopsis:  Output a line to the listing file with new page if necessary.  */
/*                                                                            */
/******************************************************************************/
void printLine( char *line, WORD16 loc, WORD16 val, LINESTYLE_T linestyle )
{
  char rlc;

  if( listfile == NULL )
  {
    save_error_count = 0;
    return;
  }

  printPageBreak();

  list_lineno++;
  page_lineno++;

  if (reloc == 0)
  {
    rlc = ' ';
  }
  else
  {
    rlc = '*';
  }

  switch( linestyle )
  {
  default:
  case LINE:
    fprintf(listfile, "%5d              ", lineno );
    fputs( line, listfile );
    listed = TRUE;
    break;

  case LINE_VAL:
    fprintf(listfile, "%5d        %4.4o  ", lineno, val );
    fputs( line, listfile );
    listed = TRUE;
    break;

  case LINE_LOC_VAL:
    if( !listed )
    {
      if( indirect_generated )
      {
        fprintf( listfile, "%5d %5.5o%c %4.4o@ ", lineno, loc, rlc, val );
      }
      else
      {
        fprintf( listfile, "%5d %5.5o%c %4.4o  ", lineno, loc, rlc, val );
      }
      fputs( line, listfile );
      listed = TRUE;
    }
    else
    {
      fprintf( listfile, "      %5.5o%c %4.4o\n", loc, rlc, val );
    }
    break;

  case LOC_VAL:
    fprintf( listfile, "      %5.5o%c %4.4o\n", loc, rlc, val );
    break;
  }
  printErrorMessages();
} /* printLine()                                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  printErrorMessages                                             */
/*                                                                            */
/*  Synopsis:  Output any error messages from the current list of errors.     */
/*                                                                            */
/******************************************************************************/
void printErrorMessages()
{
  WORD16  ix;
  WORD16  iy;

  if( listfile != NULL )
  {
    /* If any errors, display them now.                                       */
    for( iy = 0; iy < save_error_count; iy++ )
    {
      printPageBreak();
      fprintf( listfile, "%-18.18s", error_list[iy].mesg );
      if( error_list[iy].col >= 0 )
      {
        for( ix = 0; ix < error_list[iy].col; ix++ )
        {
          if( line[ix] == '\t' )
          {
            putc( '\t', listfile );
          }
          else
          {
            putc( ' ', listfile );
          }
        }
        fputs( "^", listfile );
        list_lineno++;
        page_lineno++;
      }
      fputs( "\n", listfile );
    }
  }
  save_error_count = 0;
} /* printErrorMessages()                                                     */


/******************************************************************************/
/*                                                                            */
/*  Function:  endOfBinary                                                    */
/*                                                                            */
/*  Synopsis:  Outputs both literal tables at the end of a binary segment.    */
/*                                                                            */
/******************************************************************************/
void endOfBinary()
{
  /* Punch page 0 also.                                   */
  punchLiteralPool( cp, 1 );
  if( error_in_line )
  {
    listed = TRUE;
    clc = ( clc & 070000 ) + (( clc - 1 ) & 07777 );
    errorMessage( &end_of_file, -1 );
    clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 );
  }
  else
  {
    listLine();             /* List line if not done yet.                     */
  }
  return;
} /* endOfBinary()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchChecksum                                                  */
/*                                                                            */
/*  Synopsis:  Output a checksum if the current mode requires it and an       */
/*             object file exists.                                            */
/*                                                                            */
/******************************************************************************/
void punchChecksum()
{
  /* If the assembler has output any BIN data output the checksum.            */
  if( binary_data_output && !rim_mode )
  {
    punchLocObject( 0, checksum );
  }
  binary_data_output = FALSE;
  checksum = 0;
} /* punchChecksum()                                                          */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchLeader                                                    */
/*                                                                            */
/*  Synopsis:  Generate 2 feet of leader on object file, as per DEC           */
/*             documentation.  Paper tape has 10 punches per inch.            */
/*                                                                            */
/******************************************************************************/
void punchLeader( int count )
{
  int ix;

  /* If value is zero, set to the default of 2 feet of leader.                */
  count = ( count == 0 ) ? 240 : count;

  if( objectfile != NULL )
  {
    for( ix = 0; ix < count; ix++ )
    {
      fputc( 0200, objectfile );
    }
  }
} /* punchLeader()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchOrigin                                                    */
/*                                                                            */
/*  Synopsis:  Output an origin to the object file.                           */
/*                                                                            */
/******************************************************************************/
void punchOrigin( WORD16 loc )
{
  punchObject((( loc >> 6 ) & 0077 ) | 0100 );
  punchObject( loc & 0077 );
} /* punchOrigin()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchObject                                                    */
/*                                                                            */
/*  Synopsis:  Put one character to object file and include it in checksum.   */
/*                                                                            */
/******************************************************************************/
void punchObject( WORD16 val )
{
  val &= 0377;
  if( objectfile != NULL )
  {
    fputc( val, objectfile );
    checksum += val;
  }
  binary_data_output = TRUE;
} /* punchObject()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchOutObject                                                 */
/*                                                                            */
/*  Synopsis:  Output the current line and then then punch value to the       */
/*             object file.                                                   */
/*                                                                            */
/******************************************************************************/
void punchOutObject( WORD16 loc, WORD16 val )
{
  /* Adding reloc makes printout agree with PAL8 where is prints the */
  /* relocated address, not the address in the BIN file */
  printLine( line,( ( field | loc ) + reloc ), val, LINE_LOC_VAL );
  punchLocObject( loc, val );
} /* punchOutObject()                                                         */

/******************************************************************************/
/*                                                                            */
/*  Function:  punchLocObject                                                 */
/*                                                                            */
/*  Synopsis:  Output the word (with origin if rim format) to the object file.*/
/*                                                                            */
/******************************************************************************/
void punchLocObject( WORD16 loc, WORD16 val )
{
  if( rim_mode )
  {
    punchOrigin( loc );
  }
  punchObject(( val >> 6 ) & 0077 );
  punchObject( val & 0077 );
} /* punchLocObject()                                                         */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchLiteralPool                                               */
/*                                                                            */
/*  Synopsis:  Output the current page data.                                  */
/*                                                                            */
/******************************************************************************/
void punchLiteralPool( LPOOL_T *p, BOOL punch_page0 )
{
  WORD16  loc;
  WORD16  tmplc;
  int lpool_page = 0; /* Silence false uninitialized error from GCC */
  int i;

  for (i = MAX_PAGES-1; i >= 0;  i--) {
    lpool_page = (i << 7) & 07600;

    if ( p[i].loc != p[i].last_punched && (punch_page0 || lpool_page != 0) )
    {
      if( !rim_mode )
      {
        /* Put out origin if not in rim mode.                                 */
        punchOrigin( p[i].loc | lpool_page );
      }
      /* Put the literals in the object file.                                 */
      for( loc = p[i].loc; loc < p[i].last_punched; loc++ )
      {
        tmplc = loc + lpool_page;
        printLine( line, (field | tmplc), p[i].pool[loc], LOC_VAL );
        punchLocObject( tmplc, p[i].pool[loc] );
      }
      p[i].last_punched = p[i].loc;
    }
  }
} /* punchLiteralPool()                                                       */


/******************************************************************************/
/*                                                                            */
/*  Function:  insertLiteral                                                  */
/*                                                                            */
/*  Synopsis:  Add a value to the given literal pool if not already in pool.  */
/*             Return the location of the value in the pool.                  */
/*                                                                            */
/******************************************************************************/
WORD16 insertLiteral( LPOOL_T *pool, WORD16 value, int fieldpage_index )
{
  WORD16  ix;
  LPOOL_T *p;

  p = &pool[fieldpage_index];

  /* Search the literal pool for any occurence of the needed value.           */
  ix = PAGE_SIZE - 1;
  while( ix >= p->loc && p->pool[ix] != value )
  {
    ix--;
  }

  /* Check if value found in literal pool. If not, then insert value.         */
  if( ix < p->loc )
  {
    (p->loc)--;
    p->pool[p->loc] = value;
    ix = p->loc;
    if( max_page_used[fieldpage_index] >= p->loc ) {
      if ( (fieldpage_index & 017) == 0 )
      {
        errorMessage( &pz_literal_overflow, -1 );
      }
      else
      {
        errorMessage( &literal_overflow, -1 );
      }
    }
  }
  return( ix );
} /* insertLiteral()                                                          */


/******************************************************************************/
/*                                                                            */
/*  Function:  printSymbolTable                                               */
/*                                                                            */
/*  Synopsis:  Output the symbol table.                                       */
/*                                                                            */
/******************************************************************************/
void printSymbolTable()
{
  int    col;
  int    cx;
  char  *fmt;
  int    ix;
  char   mark;
  int    page;
  int    row;
  int    symbol_base;
  int    symbol_lines;

  symbol_base = number_of_fixed_symbols;

  for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ )
  {
    topOfForm( list_title, s_symtable );
    symbol_lines = LIST_LINES_PER_PAGE - page_lineno;

    for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++)
    {
      list_lineno++;
      page_lineno++;
      fprintf( listfile, "%5d", list_lineno );

      for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ )
      {
        /* Get index of symbol for the current line and column                         */
        cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row;
        cx += symbol_base;

        /* Make sure that there is a symbol to be printed.                    */
        if( number_of_fixed_symbols <= cx && cx < symbol_top )
        {
          switch( symtab[cx].type & LABEL )
          {
          case LABEL:
            fmt = " %c%-6.6s %5.5o ";
            break;

          default:
            fmt = " %c%-6.6s  %4.4o ";
            break;
          }

          switch( symtab[cx].type & ( DEFINED | REDEFINED ))
          {
          case UNDEFINED:
            mark = '?';
            break;

          case REDEFINED:
            mark = '#';
            break;

          default:
            mark = ' ';
            break;
          }
          fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val );
          ix++;
        }
      }
      fprintf( listfile, "\n" );
    }
  }
} /* printSymbolTable()                                                       */


/******************************************************************************/
/*                                                                            */
/*  Function:  printPermanentSymbolTable                                      */
/*                                                                            */
/*  Synopsis:  Output the permanent symbol table to a file suitable for       */
/*             being input after the EXPUNGE pseudo-op.                       */
/*                                                                            */
/******************************************************************************/
void printPermanentSymbolTable()
{
  int     ix;
  FILE   *permfile;
  char  *s_type;

  if(( permfile = fopen( permpathname, "w" )) == NULL )
  {
    exit( 2 );
  }

  fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" );
  fprintf( permfile, "        EXPUNGE\n/\n" );
  /* Print the memory reference instructions first.                           */
  s_type = "FIXMRI";
  for( ix = 0; ix < symbol_top; ix++ )
  {
    if( M_MRI( symtab[ix].type ))
    {
      fprintf( permfile, "%-7s %s=%4.4o\n",
                                    s_type, symtab[ix].name, symtab[ix].val );
    }
  }

  s_type = " ";
  for( ix = 0; ix < symbol_top; ix++ )
  {
    if( M_FIXED( symtab[ix].type ))
    {
      if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type ))
      {
        fprintf( permfile, "%-7s %s=%4.4o\n",
                                    s_type, symtab[ix].name, symtab[ix].val );
      }
    }
  }
  fprintf( permfile, "/\n        FIXTAB\n" );
  fclose( permfile );
} /* printPermanentSymbolTable()                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  printCrossReference                                            */
/*                                                                            */
/*  Synopsis:  Output a cross reference (concordance) for the file being      */
/*             assembled.                                                     */
/*                                                                            */
/******************************************************************************/
void printCrossReference()
{
  int    ix;
  int    symbol_base;
  int    xc;
  int    xc_index;
  int    xc_refcount;
  int    xc_cols;

  /* Force top of form for first page.                                        */
  page_lineno = LIST_LINES_PER_PAGE;

  list_lineno = 0;
  symbol_base = number_of_fixed_symbols;

  for( ix = symbol_base; ix < symbol_top; ix++ )
  {
    list_lineno++;
    page_lineno++;
    if( page_lineno >= LIST_LINES_PER_PAGE )
    {
      topOfForm( list_title, s_xref );
    }

    fprintf( listfile, "%5d", list_lineno );

    /* Get reference count & index into concordance table for this symbol.    */
    xc_refcount = symtab[ix].xref_count;
    xc_index = symtab[ix].xref_index;
    /* Determine how to label symbol on concordance.                          */
    switch( symtab[ix].type & ( DEFINED | REDEFINED ))
    {
    case UNDEFINED:
      fprintf( listfile, " U         ");
      break;

    case REDEFINED:
      fprintf( listfile, " M  %5d  ", xreftab[xc_index] );
      break;

    default:
      fprintf( listfile, " A  %5d  ", xreftab[xc_index] );
      break;
    }
    fprintf( listfile, "%-6.6s  ", symtab[ix].name );

    /* Output the references, 8 numbers per line after symbol name.           */
    for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ )
    {
      if( xc_cols >= XREF_COLUMNS )
      {
        xc_cols = 0;
        page_lineno++;
        if( page_lineno >= LIST_LINES_PER_PAGE )
        {
          topOfForm( list_title, s_xref);
        }
        list_lineno++;
        fprintf( listfile, "\n%5d%-19s", list_lineno, " " );
      }
      fprintf( listfile, "  %5d", xreftab[xc_index + xc] );
    }
    fprintf( listfile, "\n" );
  }
} /* printCrossReference()                                                    */


/******************************************************************************/
/*                                                                            */
/*  Function:  topOfForm                                                      */
/*                                                                            */
/*  Synopsis:  Prints title and sub-title on top of next page of listing.     */
/*                                                                            */
/******************************************************************************/
void topOfForm( char *title, char *sub_title )
{
  char temp[10];

  list_pageno++;
  strcpy( temp, s_page );
  sprintf( temp, "%s %d", s_page, list_pageno );

  /* Output a top of form if not the first page of the listing.               */
  if( list_pageno > 1 )
  {
    fprintf( listfile, "\f" );
  }
  fprintf( listfile, "\n\n\n      %-63s %10s\n", title, temp );

  /* Reset the current page line counter.                                     */
  page_lineno = 3;
  if( sub_title != NULL )
  {
    fprintf( listfile, "%80s\n", sub_title );
    page_lineno++;
  }
  else
  {
    fprintf( listfile, "\n" );
    page_lineno++;
  }
  fprintf( listfile, "\n" );
  page_lineno++;
} /* topOfForm()                                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  lexemeToName                                                   */
/*                                                                            */
/*  Synopsis:  Convert the current lexeme into a string.                      */
/*                                                                            */
/******************************************************************************/
char *lexemeToName( char *name, int from, int term )
{
  int  to;

  to = 0;

  while( from < term && to < ( SYMLEN - 1 ))
  {
    name[to++] = toupper( line[from++] );
  }

  while( to < SYMLEN )
  {
    name[to++] = '\0';
  }
  return( name );
} /* lexemeToName()                                                           */

/******************************************************************************/
/*                                                                            */
/*  Function:  defineLexeme                                                   */
/*                                                                            */
/*  Synopsis:  Put lexeme into symbol table with a value.                     */
/*                                                                            */
/******************************************************************************/
SYM_T *defineLexeme( int     start,     /* start of lexeme being defined.     */
                     int     term,      /* end+1 of lexeme being defined.     */
                     WORD16  val,       /* value of lexeme being defined.     */
                     SYMTYP  type )     /* how symbol is being defined.       */
{
  char  name[SYMLEN];

  lexemeToName( name, start, term);
  return( defineSymbol( name, val, type, start ));
} /* defineLexeme()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  defineSymbol                                                   */
/*                                                                            */
/*  Synopsis:  Define a symbol in the symbol table, enter symbol name if not  */
/*             not already in table.                                          */
/*                                                                            */
/******************************************************************************/
SYM_T *defineSymbol( char *name, WORD16 val, SYMTYP type, WORD16 start )
{
  SYM_T  *sym;
  int     xref_count;

  if( strlen( name ) < 1 )
  {
    return( &sym_undefined );   /* Protect against non-existent names.        */
  }
  sym = lookup( name );
  /* OS/8 PAL8 seems to allow permanent symbold to be redefined without error */
  if( ( M_FIXED( sym->type ) && pass == 1 && perm_redef_error ) || 
     (M_PERM_REDEFINED( sym->type ) && (sym->val != val)) )
  {
     type |= PERM_REDEFINED;
  }

  xref_count = 0;               /* Set concordance for normal defintion.      */

  if( M_DEFINED( sym->type ))
  {
    if( pass == 2 && ( (sym->val & 07777) != (val & 07777) || M_PERM_REDEFINED(sym->type)) )
    {
      /* Generate diagnostic if redefining a symbol.                          */
      if( M_PERM_REDEFINED( sym->type ) && (M_LABEL(sym->type) || M_LABEL(type)) )
      {
        errorSymbol( &illegal_redefine, sym->name, start );
      } else 
      {
        /* Generate diagnostic if redefining a symbol.                          */
        if( M_REDEFINED( sym->type ) && (M_LABEL(sym->type) || M_LABEL(type)) )
        {
          errorSymbol( &redefined_symbol, sym->name, start );
        }
      }
      type = type | REDEFINED;
      sym->xref_count++;      /* Referenced suymbol, count it.                */
      xref_count = sym->xref_count;
    }
  }

  if( pass == 2 && xref )
  {
    /* Put the definition line number in the concordance table.               */
    /* Defined symbols are not counted as references.                         */
    xreftab[sym->xref_index] = lineno;
    /* Put the line number in the concordance table.                          */
    xreftab[sym->xref_index + xref_count] = lineno;
  }

  /* Now set the value and the type.                                          */
  sym->val = ( M_LABEL(type) ) ? val : val & 07777;
  sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type;
  return( sym );
} /* defineSymbol()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  lookup                                                         */
/*                                                                            */
/*  Synopsis:  Find a symbol in table.  If not in table, enter symbol in      */
/*             table as undefined.  Return address of symbol in table.        */
/*                                                                            */
/******************************************************************************/
SYM_T *lookup( char *name )
{
  int     ix;                   /* Insertion index                            */
  int     lx;                   /* Left index                                 */
  int     rx;                   /* Right index                                */

  /* First search the permanent symbols.                                      */
  lx = 0;
  ix = binarySearch( name, lx, number_of_fixed_symbols );

  /* If symbol not in permanent symbol table.                                 */
  if( ix < 0 )
  {
    /* Now try the user symbol table.                                         */
    ix = binarySearch( name, number_of_fixed_symbols, symbol_top );

    /* If symbol not in user symbol table.                                    */
    if( ix < 0 )
    {
      /* Must put symbol in table if index is negative.                       */
      ix = ~ix;
      if( symbol_top + 1 >= SYMBOL_TABLE_SIZE )
      {
        errorSymbol( &symbol_table_full, name, lexstart );
        exit( 1 );
      }

      for( rx = symbol_top; rx >= ix; rx-- )
      {
        symtab[rx + 1] = symtab[rx];
      }
      symbol_top++;

      /* Enter the symbol as UNDEFINED with a value of zero.                  */
      strcpy( symtab[ix].name, name );
      symtab[ix].type = UNDEFINED;
      symtab[ix].val  = 0;
      symtab[ix].xref_count = 0;
      if( xref && pass == 2 )
      {
        xreftab[symtab[ix].xref_index] = 0;
      }
    }
  }

  return( &symtab[ix] );        /* Return the location of the symbol.         */
} /* lookup()                                                                 */


/******************************************************************************/
/*                                                                            */
/*  Function:  binarySearch                                                   */
/*                                                                            */
/*  Synopsis:  Searches the symbol table within the limits given.  If the     */
/*             symbol is not in the table, it returns the insertion point.    */
/*                                                                            */
/******************************************************************************/
int binarySearch( char *name, int start, int symbol_count )
{
  int     lx;                   /* Left index                                 */
  int     mx;                   /* Middle index                               */
  int     rx;                   /* Right index                                */
  int     compare;              /* Results of comparison                      */

  lx = start;
  rx = symbol_count - 1;
  while( lx <= rx )
  {
    mx = ( lx + rx ) / 2;   /* Find center of search area.                    */

    compare = strcmp( name, symtab[mx].name );

    if( compare < 0 )
    {
      rx = mx - 1;
    }
    else if( compare > 0 )
    {
      lx = mx + 1;
    }
    else
    {
      return( mx );         /* Found a match in symbol table.                 */
    }
  } /* end while                                                              */
  return( ~lx );            /* Return insertion point.                        */
} /* binarySearch()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  compareSymbols                                                 */
/*                                                                            */
/*  Synopsis:  Used to presort the symbol table when starting assembler.      */
/*                                                                            */
/******************************************************************************/
int compareSymbols( const void *a, const void *b )
{
  return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name ));
} /* compareSymbols()                                                         */


/******************************************************************************/
/*                                                                            */
/*  Function:  evalSymbol                                                     */
/*                                                                            */
/*  Synopsis:  Get the pointer for the symbol table entry if exists.          */
/*             If symbol doesn't exist, return a pointer to the undefined sym */
/*                                                                            */
/******************************************************************************/
SYM_T *evalSymbol()
{
  char   name[SYMLEN];
  SYM_T *sym;

  sym = lookup( lexemeToName( name, lexstart, lexterm ));

  /* The symbol goes in the concordance iff it is in a different position in  */
  /* the assembler source file.                                               */
  if( lexstart != last_xref_lexstart ||  lineno != last_xref_lineno )
  {
    sym->xref_count++;          /* Count the number of references to symbol.  */
    last_xref_lexstart = lexstart;
    last_xref_lineno = lineno;

    /* Put the line number in the concordance table.                          */
    if( xref && pass == 2 )
    {
      xreftab[sym->xref_index + sym->xref_count] = lineno;
    }
  }

  return( sym );
} /* evalSymbol()                                                             */


/******************************************************************************/
/*                                                                            */
/*  Function:  moveToEndOfLine                                                */
/*                                                                            */
/*  Synopsis:  Move the parser input to the end of the current input line.    */
/*                                                                            */
/******************************************************************************/
void moveToEndOfLine()
{
  while( !isend( line[cc] )) cc++;
  lexstart = cc;
  lexterm = cc;
  lexstartprev = lexstart;
} /* moveToEndOfLine()                                                        */

/******************************************************************************/
/*                                                                            */
/*  Function:  nextLexeme                                                     */
/*                                                                            */
/*  Synopsis:  Get the next lexical element from input line.                  */
/*                                                                            */
/******************************************************************************/
void nextLexeme()
{
  /* Save start column of previous lexeme for diagnostic messages.            */
  lexstartprev = lexstart;
  lextermprev = lexterm;

  while( is_blank( line[cc] )) { cc++; }
  lexstart = cc;

  if( isalnum( line[cc] ))
  {
    while( isalnum( line[cc] )) { cc++; }
  }
  else if( isend( line[cc] ))
  {
    /* End-of-Line, don't advance cc!                                         */
  }
  else
  {
    switch( line[cc] )
    {
    case '"':     /* Quoted letter                                            */
      if( cc + 2 < maxcc )
      {
        cc++;
        cc++;
      }
      else
      {
        errorMessage( &no_literal_value, lexstart );
        cc++;
      }
      break;

    case '/':     /* Comment, don't advance cc!                               */
      break;

    default:      /* All other punctuation.                                   */
      cc++;
      break;
    }
  }
  lexterm = cc;
} /* nextLexeme()                                                             */


/******************************************************************************/
/*                                                                            */
/*  Function:  nextLexBlank                                                   */
/*                                                                            */
/*  Synopsis:  Used to prevent illegal blanks in expressions.                 */
/*                                                                            */
/******************************************************************************/
void nextLexBlank()
{
  nextLexeme();
  if( is_blank( delimiter ))
  {
    errorMessage( &illegal_blank, lexstart - 1 );
  }
  delimiter = line[lexterm];
} /* nextLexBlank()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  pseudoOperators                                                */
/*                                                                            */
/*  Synopsis:  Process pseudo-ops (directives).                               */
/*                                                                            */
/******************************************************************************/
BOOL pseudoOperators( PSEUDO_T val )
{
  int     count, count2;
  int     delim;
  int     index;
  int     ix;
  int     lexstartsave;
  WORD16  newfield;
  WORD16  oldclc;
  int     pack;
  BOOL    status;
  SYM_T  *sym;
  FILE   *temp;
  int     term;
  WORD16  value;
  char os8_name[8];
  int     reloc_clc;

  status = TRUE;
  switch( (PSEUDO_T) val )
  {
  case ASCII:
    /* added 18-Jan-2003 PNT  -- derived from TEXT */
    delim = line[lexstart];
    index = lexstart + 1;
    while( line[index] != delim && !isend( line[index] ))
    {
      punchOutObject( clc, (line[index] & 127) | 128 );
      incrementClc();
      index++;
    }
    if( isend( line[index] ))
    {
      cc = index;
      lexterm = cc;
      errorMessage( &text_string, cc );
    }
    else
    {
      cc = index + 1;
      lexterm = cc;
    }
    nextLexeme();
    break;
  
  case BANK:
    errorSymbol( &no_pseudo_op, "BANK", lexstartprev );
    /* should select a different 32K out of 128K                              */
    break;

  case BINPUNCH:
    /* If there has been data output and this is a mode switch, set up to     */
    /* output data in BIN mode.                                               */
    if( binary_data_output && rim_mode )
    {
      clearLiteralTable();
      punchLeader( 8 );         /* Generate a short leader/trailer.           */
      checksum = 0;
      binary_data_output = FALSE;
    }
    rim_mode = FALSE;
    break;

  case DECIMAL:
    radix = 10;
    break;

  case DUBL:
    inputDubl();
    break;

  case EJECT:
    page_lineno = LIST_LINES_PER_PAGE;  /* This will force a page break.      */
    status = FALSE;             /* This will force reading of next line       */
    break;

  case ENPUNCH:
    if( pass == 2 )
    {
      objectfile = objectsave;
    }
    break;

  case EXPUNGE:                 /* Erase symbol table                         */
    if( pass == 1 )
    {
      symtab[0] = sym_undefined;
      symbol_top = 0;
      number_of_fixed_symbols = symbol_top;
      fixed_symbols = &symtab[symbol_top - 1];

      /* Enter the pseudo-ops into the symbol table.                          */
      for( ix = 0; ix < DIM( pseudo ); ix++ )
      {
        defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 );
      }
      /* Enter the really permanent symbols into the table.                   */
      /* Also make them part of the permanent symbol table.                   */
      for( ix = 0; ix < DIM( really_permanent_symbols ); ix++ )
      {
        defineSymbol( really_permanent_symbols[ix].name,
                      really_permanent_symbols[ix].val,
                      really_permanent_symbols[ix].type | DEFFIX , 0 );
      }
      number_of_fixed_symbols = symbol_top;
      fixed_symbols = &symtab[symbol_top - 1];

    }
    break;

  case FIELD:
    /* Punch page 0 also */
    punchLiteralPool( cp, 1 );
    newfield = field >> 12;
    lexstartsave = lexstartprev;
    if( isdone( line[lexstart] ))
    {
      newfield += 1;            /* Blank FIELD directive.                     */
    }
    else
    {
      newfield = (getExpr())->val;      /* FIELD with argument.               */
    }

    if( rim_mode )
    {
      errorMessage( &in_rim_mode, lexstartsave ); /* Can't change fields.     */
    }
    else if( newfield > 7 || newfield < 0 )
    {
      errorMessage( &illegal_field_value, lexstartprev );
    }
    else
    {
      value = (( newfield & 0007 ) << 3 ) | 00300;
      punchObject( value );
      if( objectfile != NULL )    /* Only fix checksum if punching */
      {
        checksum -= value;        /* Field punches are not added to checksum.   */
      }
      field = newfield << 12;
    }

    clc = 0200 | field;
    fieldlc = clc & 07777;

    if( !rim_mode )
    {
      punchOrigin( clc );
    }

    clearLiteralTable();

    break;

  case FIXMRI:
    if( line[lexterm] == '=' && isalpha( line[lexstart] ))
    {
      lexstartsave = lexstart;
      term = lexterm;
      nextLexeme();           /* Skip symbol.                                 */
      nextLexeme();           /* Skip trailing =                              */
      defineLexeme( lexstartsave, term, getExprs(), MRI );
    }
    else
    {
      errorLexeme( &symbol_syntax, lexstart );
      nextLexeme();           /* Skip symbol.                                 */
      nextLexeme();           /* Skip trailing =                              */
      (void) getExprs();      /* Skip expression.                             */
    }
    break;

  case FIXTAB:
    if (pass == 1) /* Only fix on first pass, on second all are defined */
    {
      /* Mark all current symbols as permanent symbols.                         */
      for( ix = 0; ix < symbol_top; ix++ )
      {
        symtab[ix].type = symtab[ix].type | FIXED;
      }
      number_of_fixed_symbols = symbol_top;
      fixed_symbols = &symtab[symbol_top - 1];

      /* Re-sort the symbol table                                               */
      qsort( symtab, symbol_top, sizeof(symtab[0]), compareSymbols );
    }
    break;

  case FLTG:
    inputFltg();
    /* errorSymbol( &no_pseudo_op, "FLTG", lexstartprev ); */
    break;

  case IFDEF:
    if( isalpha( line[lexstart] ))
    {
      sym = evalSymbol();
      nextLexeme();
      if( M_DEFINED_CONDITIONALLY( sym->type ))
      {
        conditionTrue();
      }
      else
      {
        conditionFalse();
      }
    }
    else
    {
      errorLexeme( &label_syntax, lexstart );
    }
    break;

  case IFNDEF:
    if( isalpha( line[lexstart] ))
    {
      sym = evalSymbol();
      nextLexeme();
      if( M_DEFINED_CONDITIONALLY( sym->type ))
      {
        conditionFalse();
      }
      else
      {
        conditionTrue();
      }
    }
    else
    {
      errorLexeme( &label_syntax, lexstart );
    }
    break;

  case IFNZERO:
    if( getExprs() == 0 )
    {
      conditionFalse();
    }
    else
    {
      conditionTrue();
    }
    break;

  case IFZERO:
    if( getExprs() == 0 )
    {
      conditionTrue();
    }
    else
    {
      conditionFalse();
    }
    break;

  case NOPUNCH:
    if( pass == 2 )
    {
      objectfile = NULL;
    }
    break;

  case OCTAL:
    radix = 8;
    break;

  case PAGE:
    reloc_clc = clc + reloc;
    punchLiteralPool( cp, 0 );
    oldclc = clc;
    if( isdone( line[lexstart] ))
    {
      clc = (( reloc_clc + 0177 ) & 077600) - reloc;  /* No argumnet.               */
      fieldlc = clc & 07777;
    }
    else
    {
      value = (getExpr())->val;
      clc = field + (( value & 037 ) << 7 ) - reloc;
      fieldlc = clc & 07777;
    }
    testForLiteralCollision( clc + reloc );

    if( !rim_mode && clc != oldclc )
    {
      punchOrigin( clc );
    }
    break;

  case PAUSE:
    break;

  case RELOC:
    if( isdone( line[lexstart] ))
    {
       reloc = 0;               /* Blank RELOC directive.                     */
    }
    else
    {
      value = (getExpr())->val; /* RELOC with argument.                       */
      reloc = (value & 07777) - ( clc  & 07777);
    }
    break;

  case RIMPUNCH:
    /* If the assembler has output any BIN data, output the literal tables    */
    /* and the checksum for what has been assembled and setup for RIM mode.   */
    if( binary_data_output && !rim_mode )
    {
      endOfBinary();
      clearLiteralTable();
      punchChecksum();
      punchLeader( 8 );         /* Generate a short leader/trailer.           */
    }
    rim_mode = TRUE;
    break;

  case SEGMNT:
    punchLiteralPool( cp, 0 );
    if( isdone( line[lexstart] ))
    {                           /* No argument.                               */
      clc = ( clc & 06000 ) + 02000;
      fieldlc = clc & 07777;
    }
    else
    {
      getExpr();
      clc = ( val & 003 ) << 10;
      fieldlc = clc & 07777;
    }
    if( !rim_mode )
    {
      punchOrigin( clc );
    }
    testForLiteralCollision( clc );
    break;

  case TEXT:
    delim = line[lexstart];
    pack = 0;
    count = 0;
    index = lexstart + 1;
    while( line[index] != delim && !isend( line[index] ))
    {
      pack = ( pack << 6 ) | ( line[index] & 077 );
      count++;
      if( count > 1 )
      {
        punchOutObject( clc, pack );
        incrementClc();
        count = 0;
        pack = 0;
      }
      index++;
    }

    if( count != 0 )
    {
      punchOutObject( clc, pack << 6 );
      incrementClc();
    }
    else
    {
      punchOutObject( clc, 0 );
      incrementClc();
    }

    if( isend( line[index] ))
    {
      cc = index;
      lexterm = cc;
      errorMessage( &text_string, cc );
    }
    else
    {
      cc = index + 1;
      lexterm = cc;
    }
    nextLexeme();
    break;

  case FILENAME:
    memset(os8_name, 0, sizeof(os8_name));
    delimiter=line[lexstart];
    if (delimiter != '.')
    {
      for (index = lexstart, count = 0; index < lexterm && count < 6; index++)
      {
        os8_name[count++] = line[index];
      }
      delimiter=line[lexterm];
      if (delimiter == '.')
      {
         nextLexeme();     /* Skip . */
      }
    }
    nextLexeme();
    if (delimiter == '.')
    {
      for (index = lexstart, count = 6; index < lexterm && count < 8; index++)
      {
        os8_name[count++] = line[index];
      }
    }

    pack = 0;
    count = 0;
    for (count2 = 0; count2 < 8; count2++) 
    {
      pack = ( pack << 6 ) | ( os8_name[count2] & 077 );
      count++;
      if( count > 1 )
      {
        punchOutObject( clc, pack );
        incrementClc();
        count = 0;
        pack = 0;
      }
    }
    nextLexeme();
    break;

  case DEVICE:
    memset(os8_name, 0, sizeof(os8_name));
    for (index = lexstart, count = 0; index < lexterm && count < 4; index++)
    {
      os8_name[count++] = line[index];
    }

    pack = 0;
    count = 0;
    for (count2 = 0; count2 < 4; count2++) 
    {
      pack = ( pack << 6 ) | ( os8_name[count2] & 077 );
      count++;
      if( count > 1 )
      {
        punchOutObject( clc, pack );
        incrementClc();
        count = 0;
        pack = 0;
      }
    }

    nextLexeme();
    break;

  case TITLE:
    delim = line[lexstart];
    ix = lexstart + 1;
    /* Find string delimiter.                                                 */
    do
    {
      if( list_title[ix] == delim && list_title[ix + 1] == delim )
      {
        ix++;
      }
      ix++;
    } while( line[ix] != delim && !isend(line[ix]) );

    if( line[ix] == delim )
    {
      count = 0;
      ix = lexstart + 1;
      do
      {
        if( list_title[ix] == delim && list_title[ix + 1] == delim )
        {
          ix++;
        }
        list_title[count] = line[ix];
        count++;
        ix++;
        list_title[count] = '\0';
      } while( line[ix] != delim && !isend(line[ix]) );

      if( strlen( list_title ) > TITLELEN )
      {
        list_title[TITLELEN] = '\0';
      }

      cc = ix + 1;
      lexterm = cc;
      page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles.  */
      list_title_set = TRUE;
    }
    else
    {
      cc = ix;
      lexterm = cc;
      errorMessage( &text_string, cc );
    }

    nextLexeme();
    break;

  case XLIST:
    if( isdone( line[lexstart] ))
    {
      temp = listfile;          /* Blank XLIST directive.                     */
      listfile = listsave;
      listsave = temp;
    }
    else
    {
      if( (getExpr())->val == 0 )
      {
        if( listfile == NULL )
        {
          listfile = listsave;
          listsave = NULL;
        }
      }
      else
      {
        if( listfile != NULL )
        {
          listsave = listfile;
          listfile = NULL;
        }
      }
    }
    break;

  case ZBLOCK:
    value = (getExpr())->val;
    if( value < 0 )
    {
      errorMessage( &zblock_too_small, lexstartprev );
    }
    else if( value + ( clc & 07777 ) - 1 > 07777 )
    {
      errorMessage( &zblock_too_large, lexstartprev );
    }
    else
    {
      for( ; value > 0; value-- )
      {
        punchOutObject( clc, 0 );
        incrementClc();
      }
    }

    break;

  default:
    break;
  } /* end switch for pseudo-ops                                              */
  return( status );
} /* pseudoOperators()                                                        */


/******************************************************************************/
/*                                                                            */
/*  Function:  conditionFalse                                                 */
/*                                                                            */
/*  Synopsis:  Called when a false conditional has been evaluated.            */
/*             Lex should be the opening <; ignore all text until             */
/*             the closing >.                                                 */
/*                                                                            */
/******************************************************************************/
void conditionFalse()
{
  int     level;

  if( line[lexstart] == '<' )
  {
    /* Invariant: line[cc] is the next unexamined character.                  */
    level = 1;
    while( level > 0 )
    {
      if( isend( line[cc] ))
      {
        readLine();
      }
      else
      {
        switch( line[cc] )
        {
        case '>':
          level--;
          cc++;
          break;

        case '<':
          level++;
          cc++;
          break;

        case '$':
          level = 0;
          cc++;
          break;

        default:
          cc++;
          break;
        } /* end switch                                                       */
      } /* end if                                                             */
    } /* end while                                                            */
    nextLexeme();
  }
  else
  {
    errorMessage( &lt_expected, lexstart );
  }
} /* conditionFalse()                                                         */

/******************************************************************************/
/*                                                                            */
/*  Function:  conditionTrue                                                  */
/*                                                                            */
/*  Synopsis:  Called when a true conditional has been evaluated.             */
/*             Lex should be the opening <; skip it and setup for             */
/*             normal assembly.                                               */
/*                                                                            */
/******************************************************************************/
void conditionTrue()
{
  if( line[lexstart] == '<' )
  {
    nextLexeme();               /* Skip the opening '<'                       */
  }
  else
  {
    errorMessage( &lt_expected, lexstart );
  }
} /* conditionTrue()                                                          */


/******************************************************************************/
/*                                                                            */
/*  Function:  errorLexeme                                                    */
/*                                                                            */
/*  Synopsis:  Display an error message using the current lexical element.    */
/*                                                                            */
/******************************************************************************/
void errorLexeme( EMSG_T *mesg, int col )
{
  char   name[SYMLEN];

  errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col );
} /* errorLexeme()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  errorSymbol                                                    */
/*                                                                            */
/*  Synopsis:  Display an error message with a given string.                  */
/*                                                                            */
/******************************************************************************/
void errorSymbol( EMSG_T *mesg, char *name, int col )
{
  char   linecol[12];
  char  *s;

  if( pass == 2 )
  {
    s = ( name == NULL ) ? "" : name ;
    errors++;
    sprintf( linecol, "(%d:%d)", lineno, col + 1 );
    fprintf( errorfile, "%s%-9s : error:  %s \"%s\" at Loc = %5.5o\n",
                                      filename, linecol, mesg->file, s, clc );
    saveError( mesg->list, col );
  }
  error_in_line = TRUE;
} /* errorSymbol()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  errorMessage                                                   */
/*                                                                            */
/*  Synopsis:  Display an error message without a name argument.              */
/*                                                                            */
/******************************************************************************/
void errorMessage( EMSG_T *mesg, int col )
{
  char   linecol[12];

  if( pass == 2 )
  {
    errors++;
    sprintf( linecol, "(%d:%d)", lineno, col + 1 );
    fprintf( errorfile, "%s%-9s : error:  %s at Loc = %5.5o\n",
                                         filename, linecol, mesg->file, clc );
    saveError( mesg->list, col );
  }
  error_in_line = TRUE;
} /* errorMessage()                                                           */

/******************************************************************************/
/*                                                                            */
/*  Function:  saveError                                                      */
/*                                                                            */
/*  Synopsis:  Save the current error in a list so it may displayed after the */
/*             the current line is printed.                                   */
/*                                                                            */
/******************************************************************************/
void saveError( char *mesg, int col )
{
  if( save_error_count < DIM( error_list ))
  {
    error_list[save_error_count].mesg = mesg;
    error_list[save_error_count].col = col;
    save_error_count++;
  }
  error_in_line = TRUE;

  if( listed )
  {
    printErrorMessages();
  }
} /* saveError()                                                              */
/* End-of-File                                                                */
Deleted pics/schofield-brtval.R.
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






























































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
# schofield-brtval.R: Generate SVG graph of the brightness curves
#    produced by Ian Schofield's ILS patch.
#
# The schofield-brtval.svg file checked into Fossil is modified from the
# version output by this program:
#
# 1. The colors are modified to match the scheme on tangentsoft.com/pidp8i
#
# 2. The data line thickness was increased
#
# 3. The data lines were smoothed by Inkscape's "simplify" function
#
# Copyright © 2017 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.
#

min = 0
max = 32
a = min
b = max

rising  = c(min);
falling = c(max);

for (i in 1:400) {
  a = a + (max - a) * 0.01
  b = b + (min - b) * 0.01
  
  rising[i]  = a
  falling[i] = b
  
  if (a > 31 || b < 1) break
}

data = data.frame(Rising = rising, Falling = falling)
dts = ts(data)
svg("schofield-brtval.svg", width=8, height=6)
plot.ts(dts, plot.type='single', ylab='Brightness',
        yaxp=c(min, max, 8))
dev.off()
Deleted pics/schofield-brtval.svg.
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


























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="576pt"
   height="396.73468pt"
   viewBox="0 0 576 396.73468"
   version="1.1"
   id="svg4276"
   inkscape:version="0.91 r13725"
   sodipodi:docname="schofield-brtval.svg">
  <title
     id="title4481">Schofield ILS Brightness Curves</title>
  <metadata
     id="metadata267">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title>Schofield ILS Brightness Curves</dc:title>
        <cc:license
           rdf:resource="https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md" />
        <dc:date>2017-02-02</dc:date>
        <dc:creator>
          <cc:Agent>
            <dc:title>Warren Young</dc:title>
          </cc:Agent>
        </dc:creator>
        <dc:language>English</dc:language>
        <dc:description>Curves showing the brightness levels of an LED driven from 0 to full brightness and from full brightness back to 0 under Ian Schofield's indandescent lamp simulator for the PiDP-8/I.</dc:description>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <sodipodi:namedview
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1"
     objecttolerance="10"
     gridtolerance="10"
     guidetolerance="10"
     inkscape:pageopacity="0"
     inkscape:pageshadow="2"
     inkscape:window-width="2560"
     inkscape:window-height="1391"
     id="namedview4548"
     showgrid="false"
     inkscape:zoom="1.6333333"
     inkscape:cx="360"
     inkscape:cy="253.95917"
     inkscape:window-x="0"
     inkscape:window-y="1"
     inkscape:window-maximized="1"
     inkscape:current-layer="svg4276"
     fit-margin-top="0"
     fit-margin-left="0"
     fit-margin-right="0"
     fit-margin-bottom="0" />
  <defs
     id="defs4278">
    <g
       id="g4280">
      <symbol
         overflow="visible"
         id="glyph0-0"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0.390625,0 0,-8.609375 6.828125,0 0,8.609375 z m 5.75,-1.078125 0,-6.453125 -4.671875,0 0,6.453125 z"
           id="path4283"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-1"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 7.171875,-8.609375 0,1.03125 -2.890625,0 0,7.578125 -1.1875,0 0,-7.578125 -2.90625,0 0,-1.03125 z"
           id="path4286"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-2"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0.78125,-6.25 1.0625,0 0,6.25 -1.0625,0 z m 0,-2.359375 1.0625,0 0,1.203125 -1.0625,0 z"
           id="path4289"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-3"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0.78125,-6.28125 1.03125,0 0,0.890625 C 2.0625,-5.691406 2.289062,-5.914062 2.5,-6.0625 c 0.34375,-0.238281 0.738281,-0.359375 1.1875,-0.359375 0.5,0 0.898438,0.125 1.203125,0.375 0.164063,0.136719 0.320313,0.34375 0.46875,0.625 0.226563,-0.34375 0.5,-0.59375 0.8125,-0.75 0.320313,-0.164063 0.679687,-0.25 1.078125,-0.25 0.84375,0 1.414062,0.308594 1.71875,0.921875 0.164062,0.324219 0.25,0.765625 0.25,1.328125 L 9.21875,0 8.125,0 l 0,-4.359375 C 8.125,-4.773438 8.019531,-5.0625 7.8125,-5.21875 7.601562,-5.375 7.347656,-5.453125 7.046875,-5.453125 c -0.417969,0 -0.777344,0.140625 -1.078125,0.421875 -0.292969,0.273438 -0.4375,0.730469 -0.4375,1.375 l 0,3.65625 -1.078125,0 0,-4.09375 c 0,-0.425781 -0.054687,-0.738281 -0.15625,-0.9375 C 4.140625,-5.320312 3.84375,-5.46875 3.40625,-5.46875 3.007812,-5.46875 2.644531,-5.3125 2.3125,-5 1.988281,-4.695312 1.828125,-4.140625 1.828125,-3.328125 L 1.828125,0 0.78125,0 Z"
           id="path4292"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-4"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 3.390625,-6.421875 c 0.445313,0 0.878906,0.105469 1.296875,0.3125 0.414062,0.210937 0.734375,0.480469 0.953125,0.8125 0.207031,0.324219 0.347656,0.695313 0.421875,1.109375 0.0625,0.292969 0.09375,0.757812 0.09375,1.390625 l -4.609375,0 C 1.566406,-2.160156 1.71875,-1.648438 2,-1.265625 2.28125,-0.878906 2.71875,-0.6875 3.3125,-0.6875 c 0.550781,0 0.988281,-0.179688 1.3125,-0.546875 C 4.8125,-1.441406 4.945312,-1.6875 5.03125,-1.96875 l 1.03125,0 C 6.039062,-1.738281 5.953125,-1.484375 5.796875,-1.203125 5.640625,-0.921875 5.46875,-0.6875 5.28125,-0.5 4.957031,-0.1875 4.554688,0.0195312 4.078125,0.125 3.828125,0.1875 3.539062,0.21875 3.21875,0.21875 2.4375,0.21875 1.773438,-0.0625 1.234375,-0.625 c -0.542969,-0.570312 -0.8125,-1.367188 -0.8125,-2.390625 0,-1.007813 0.269531,-1.828125 0.8125,-2.453125 0.550781,-0.632812 1.269531,-0.953125 2.15625,-0.953125 z M 5.0625,-3.640625 C 5.019531,-4.097656 4.921875,-4.460938 4.765625,-4.734375 4.484375,-5.242188 4.003906,-5.5 3.328125,-5.5 2.835938,-5.5 2.425781,-5.320312 2.09375,-4.96875 1.769531,-4.625 1.597656,-4.179688 1.578125,-3.640625 Z m -1.78125,-2.78125 z"
           id="path4295"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-5"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 3.25,-8.390625 c 1.082031,0 1.867188,0.449219 2.359375,1.34375 0.375,0.6875 0.5625,1.636719 0.5625,2.84375 C 6.171875,-3.066406 6,-2.125 5.65625,-1.375 5.164062,-0.300781 4.359375,0.234375 3.234375,0.234375 c -1,0 -1.75,-0.4375 -2.25,-1.3125 C 0.578125,-1.816406 0.375,-2.800781 0.375,-4.03125 c 0,-0.945312 0.125,-1.765625 0.375,-2.453125 0.457031,-1.269531 1.289062,-1.90625 2.5,-1.90625 z m -0.015625,7.65625 c 0.550781,0 0.988281,-0.238281 1.3125,-0.71875 0.320313,-0.488281 0.484375,-1.394531 0.484375,-2.71875 0,-0.945313 -0.121094,-1.726563 -0.359375,-2.34375 C 4.441406,-7.128906 3.988281,-7.4375 3.3125,-7.4375 c -0.625,0 -1.085938,0.292969 -1.375,0.875 -0.28125,0.585938 -0.421875,1.445312 -0.421875,2.578125 0,0.855469 0.09375,1.542969 0.28125,2.0625 0.28125,0.792969 0.757813,1.1875 1.4375,1.1875 z"
           id="path4298"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-6"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 1.484375,-2.140625 c 0.070313,0.605469 0.351563,1.023437 0.84375,1.25 0.25,0.117187 0.535156,0.171875 0.859375,0.171875 0.625,0 1.085938,-0.195312 1.390625,-0.59375 C 4.878906,-1.707031 5.03125,-2.148438 5.03125,-2.640625 5.03125,-3.222656 4.847656,-3.675781 4.484375,-4 4.128906,-4.320312 3.703125,-4.484375 3.203125,-4.484375 c -0.367187,0 -0.679687,0.074219 -0.9375,0.21875 C 2.003906,-4.128906 1.785156,-3.9375 1.609375,-3.6875 L 0.6875,-3.734375 1.328125,-8.25 l 4.359375,0 0,1.015625 -3.5625,0 -0.359375,2.328125 c 0.195313,-0.144531 0.382813,-0.253906 0.5625,-0.328125 0.3125,-0.125 0.671875,-0.1875 1.078125,-0.1875 0.769531,0 1.421875,0.25 1.953125,0.75 0.539063,0.492187 0.8125,1.117187 0.8125,1.875 0,0.792969 -0.25,1.496094 -0.75,2.109375 -0.492187,0.6054688 -1.273437,0.90625 -2.34375,0.90625 -0.679687,0 -1.28125,-0.1953125 -1.8125,-0.578125 -0.523437,-0.382813 -0.8125,-0.976563 -0.875,-1.78125 z"
           id="path4301"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-7"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 1.15625,-5.9375 0,-0.8125 c 0.757812,-0.070312 1.285156,-0.195312 1.578125,-0.375 0.300781,-0.175781 0.53125,-0.585938 0.6875,-1.234375 l 0.828125,0 L 4.25,0 3.125,0 l 0,-5.9375 z"
           id="path4304"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-8"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="M 0.375,0 C 0.414062,-0.71875 0.566406,-1.34375 0.828125,-1.875 1.085938,-2.414062 1.59375,-2.90625 2.34375,-3.34375 L 3.46875,-4 c 0.5,-0.289062 0.851562,-0.539062 1.0625,-0.75 0.320312,-0.320312 0.484375,-0.691406 0.484375,-1.109375 0,-0.488281 -0.152344,-0.875 -0.453125,-1.15625 -0.292969,-0.289063 -0.679688,-0.4375 -1.15625,-0.4375 -0.730469,0 -1.230469,0.273437 -1.5,0.8125 -0.15625,0.304687 -0.242188,0.710937 -0.25,1.21875 l -1.078125,0 c 0.007813,-0.726563 0.144531,-1.320313 0.40625,-1.78125 0.457031,-0.8125 1.265625,-1.21875 2.421875,-1.21875 0.957031,0 1.65625,0.261719 2.09375,0.78125 0.445312,0.523437 0.671875,1.101563 0.671875,1.734375 0,0.667969 -0.234375,1.242188 -0.703125,1.71875 C 5.195312,-3.90625 4.707031,-3.566406 4,-3.171875 l -0.8125,0.4375 C 2.8125,-2.523438 2.515625,-2.320312 2.296875,-2.125 1.898438,-1.789062 1.648438,-1.414062 1.546875,-1 l 4.59375,0 0,1 z"
           id="path4307"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph0-9"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 3.125,0.234375 c -1,0 -1.726562,-0.2695312 -2.171875,-0.8125 -0.449219,-0.550781 -0.671875,-1.21875 -0.671875,-2 l 1.109375,0 c 0.039063,0.542969 0.140625,0.9375 0.296875,1.1875 0.28125,0.4375 0.773438,0.65625 1.484375,0.65625 0.5625,0 1.007813,-0.144531 1.34375,-0.4375 0.332031,-0.300781 0.5,-0.6875 0.5,-1.15625 0,-0.570313 -0.179687,-0.972656 -0.53125,-1.203125 -0.355469,-0.238281 -0.84375,-0.359375 -1.46875,-0.359375 -0.074219,0 -0.148437,0.00781 -0.21875,0.015625 -0.074219,0 -0.148437,0 -0.21875,0 l 0,-0.9375 c 0.113281,0.023438 0.207031,0.03125 0.28125,0.03125 0.070313,0 0.148437,0 0.234375,0 0.394531,0 0.71875,-0.0625 0.96875,-0.1875 C 4.507812,-5.1875 4.734375,-5.578125 4.734375,-6.140625 4.734375,-6.554688 4.582031,-6.875 4.28125,-7.09375 3.988281,-7.320312 3.644531,-7.4375 3.25,-7.4375 c -0.699219,0 -1.183594,0.234375 -1.453125,0.703125 -0.148437,0.25 -0.230469,0.617187 -0.25,1.09375 l -1.046875,0 c 0,-0.625 0.125,-1.15625 0.375,-1.59375 0.425781,-0.78125 1.179688,-1.171875 2.265625,-1.171875 0.851563,0 1.515625,0.195312 1.984375,0.578125 0.46875,0.375 0.703125,0.929687 0.703125,1.65625 0,0.511719 -0.136719,0.929687 -0.40625,1.25 -0.179687,0.199219 -0.402344,0.355469 -0.671875,0.46875 0.4375,0.125 0.78125,0.359375 1.03125,0.703125 0.25,0.34375 0.375,0.765625 0.375,1.265625 0,0.804687 -0.265625,1.460937 -0.796875,1.96875 -0.523437,0.5 -1.265625,0.75 -2.234375,0.75 z"
           id="path4310"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-0"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0,-0.390625 -8.609375,0 0,-6.828125 8.609375,0 z m -1.078125,-5.75 -6.453125,0 0,4.671875 6.453125,0 z"
           id="path4313"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-1"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -4.96875,-4.15625 c 0,-0.488281 -0.066406,-0.867188 -0.203125,-1.140625 -0.21875,-0.425781 -0.601563,-0.640625 -1.15625,-0.640625 -0.5625,0 -0.941406,0.230469 -1.140625,0.6875 -0.113281,0.25 -0.171875,0.632812 -0.171875,1.140625 l 0,2.078125 2.671875,0 z M -1,-4.546875 c 0,-0.707031 -0.207031,-1.21875 -0.625,-1.53125 -0.257812,-0.1875 -0.570312,-0.28125 -0.9375,-0.28125 -0.625,0 -1.050781,0.28125 -1.28125,0.84375 -0.125,0.292969 -0.1875,0.683594 -0.1875,1.171875 l 0,2.3125 3.03125,0 z m -7.609375,3.65625 0,-3.6875 c 0,-1.007813 0.304687,-1.726563 0.90625,-2.15625 0.355469,-0.25 0.765625,-0.375 1.234375,-0.375 0.542969,0 0.984375,0.15625 1.328125,0.46875 0.1875,0.15625 0.355469,0.386719 0.5,0.6875 0.167969,-0.4375 0.359375,-0.765625 0.578125,-0.984375 0.375,-0.394531 0.890625,-0.59375 1.546875,-0.59375 0.554687,0 1.054687,0.179688 1.5,0.53125 C -0.335938,-6.476562 0,-5.65625 0,-4.53125 l 0,3.640625 z"
           id="path4316"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-2"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.28125,-0.796875 0,-1.015625 1.09375,0 c -0.21875,-0.070312 -0.476562,-0.269531 -0.78125,-0.59375 -0.300781,-0.320312 -0.453125,-0.691406 -0.453125,-1.109375 0,-0.019531 0.00781,-0.050781 0.015625,-0.09375 0,-0.050781 0.00781,-0.132813 0.015625,-0.25 l 1.109375,0 c -0.00781,0.0625 -0.015625,0.121094 -0.015625,0.171875 0,0.054688 0,0.109375 0,0.171875 0,0.53125 0.171875,0.945313 0.515625,1.234375 0.335938,0.28125 0.726562,0.421875 1.171875,0.421875 l 3.609375,0 0,1.0625 z"
           id="path4319"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-3"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.25,-0.78125 0,-1.0625 6.25,0 0,1.0625 z m -2.359375,0 0,-1.0625 1.203125,0 0,1.0625 z"
           id="path4322"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-4"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.390625,-2.984375 c 0,-0.5 0.121094,-0.929687 0.359375,-1.296875 0.148438,-0.195312 0.351562,-0.398438 0.609375,-0.609375 l -0.796875,0 0,-0.96875 5.703125,0 c 0.800781,0 1.429687,0.117187 1.890625,0.34375 0.851562,0.4375 1.28125,1.265625 1.28125,2.484375 0,0.679688 -0.152344,1.246094 -0.453125,1.703125 -0.304687,0.460937 -0.777344,0.71875 -1.421875,0.78125 l 0,-1.078125 c 0.28125,-0.050781 0.5,-0.148438 0.65625,-0.296875 0.226562,-0.238281 0.34375,-0.613281 0.34375,-1.125 0,-0.8125 -0.289062,-1.34375 -0.859375,-1.59375 C 0.585938,-4.785156 -0.0078125,-4.851562 -0.875,-4.84375 c 0.324219,0.210938 0.5625,0.464844 0.71875,0.765625 0.15625,0.292969 0.234375,0.683594 0.234375,1.171875 0,0.679688 -0.238281,1.273438 -0.71875,1.78125 -0.488281,0.511719 -1.289063,0.765625 -2.40625,0.765625 -1.050781,0 -1.867187,-0.253906 -2.453125,-0.765625 -0.59375,-0.519531 -0.890625,-1.140625 -0.890625,-1.859375 z m 3.21875,-1.90625 c -0.769531,0 -1.34375,0.164063 -1.71875,0.484375 -0.375,0.324219 -0.5625,0.730469 -0.5625,1.21875 0,0.75 0.351563,1.261719 1.046875,1.53125 0.367188,0.148438 0.851562,0.21875 1.453125,0.21875 0.710937,0 1.25,-0.140625 1.625,-0.421875 0.367187,-0.289063 0.546875,-0.679687 0.546875,-1.171875 0,-0.757812 -0.34375,-1.289062 -1.03125,-1.59375 -0.382812,-0.175781 -0.835938,-0.265625 -1.359375,-0.265625 z m -3.25,1.78125 z"
           id="path4325"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-5"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -8.640625,-0.78125 0,-1.046875 3.21875,0 C -5.742188,-2.078125 -5.96875,-2.300781 -6.09375,-2.5 -6.3125,-2.84375 -6.421875,-3.269531 -6.421875,-3.78125 c 0,-0.90625 0.320313,-1.519531 0.953125,-1.84375 0.34375,-0.175781 0.824219,-0.265625 1.4375,-0.265625 l 4.03125,0 0,1.078125 -3.953125,0 c -0.457031,0 -0.796875,0.0625 -1.015625,0.1875 -0.34375,0.1875 -0.515625,0.546875 -0.515625,1.078125 0,0.4375 0.152344,0.835937 0.453125,1.1875 0.304688,0.355469 0.871094,0.53125 1.703125,0.53125 l 3.328125,0 0,1.046875 z"
           id="path4328"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-6"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -8.03125,-0.984375 0,-1.0625 1.75,0 0,-1 0.859375,0 0,1 4.109375,0 c 0.21875,0 0.367188,-0.078125 0.4375,-0.234375 0.042969,-0.070312 0.0625,-0.207031 0.0625,-0.40625 0,-0.050781 0,-0.101562 0,-0.15625 -0.007812,-0.0625 -0.015625,-0.128906 -0.015625,-0.203125 l 0.828125,0 c 0.03125,0.117187 0.0507812,0.242187 0.0625,0.375 0.0195312,0.125 0.03125,0.265625 0.03125,0.421875 0,0.492188 -0.125,0.824219 -0.375,1 -0.25,0.179688 -0.578125,0.265625 -0.984375,0.265625 l -4.15625,0 0,0.84375 -0.859375,0 0,-0.84375 z"
           id="path4331"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-7"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.28125,-0.78125 0,-1 0.890625,0 c -0.363281,-0.289062 -0.625,-0.601562 -0.78125,-0.9375 -0.164063,-0.332031 -0.25,-0.703125 -0.25,-1.109375 0,-0.882813 0.3125,-1.484375 0.9375,-1.796875 0.34375,-0.175781 0.828125,-0.265625 1.453125,-0.265625 l 4.03125,0 0,1.078125 -3.953125,0 c -0.382813,0 -0.691406,0.058594 -0.921875,0.171875 -0.394531,0.1875 -0.59375,0.527344 -0.59375,1.015625 0,0.25 0.027344,0.453125 0.078125,0.609375 0.085937,0.292969 0.257813,0.546875 0.515625,0.765625 0.210938,0.179688 0.421875,0.292969 0.640625,0.34375 0.21875,0.054688 0.539063,0.078125 0.953125,0.078125 l 3.28125,0 0,1.046875 z M -6.421875,-3.25 Z"
           id="path4334"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-8"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -6.421875,-3.390625 c 0,-0.445313 0.105469,-0.878906 0.3125,-1.296875 0.210937,-0.414062 0.480469,-0.734375 0.8125,-0.953125 0.324219,-0.207031 0.695313,-0.347656 1.109375,-0.421875 0.292969,-0.0625 0.757812,-0.09375 1.390625,-0.09375 l 0,4.609375 C -2.160156,-1.566406 -1.648438,-1.71875 -1.265625,-2 -0.878906,-2.28125 -0.6875,-2.71875 -0.6875,-3.3125 c 0,-0.550781 -0.179688,-0.988281 -0.546875,-1.3125 C -1.441406,-4.8125 -1.6875,-4.945312 -1.96875,-5.03125 l 0,-1.03125 c 0.230469,0.023438 0.484375,0.109375 0.765625,0.265625 0.28125,0.15625 0.515625,0.328125 0.703125,0.515625 0.3125,0.324219 0.5195312,0.726562 0.625,1.203125 0.0625,0.25 0.09375,0.539063 0.09375,0.859375 0,0.78125 -0.28125,1.445312 -0.84375,1.984375 -0.570312,0.542969 -1.367188,0.8125 -2.390625,0.8125 -1.007813,0 -1.828125,-0.269531 -2.453125,-0.8125 -0.632812,-0.550781 -0.953125,-1.269531 -0.953125,-2.15625 z m 2.78125,-1.671875 c -0.457031,0.042969 -0.820313,0.140625 -1.09375,0.296875 -0.507813,0.28125 -0.765625,0.761719 -0.765625,1.4375 0,0.492187 0.179688,0.902344 0.53125,1.234375 0.34375,0.324219 0.789062,0.496094 1.328125,0.515625 z m -2.78125,1.78125 z"
           id="path4337"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-9"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -1.96875,-1.40625 c 0.355469,-0.03125 0.625,-0.117188 0.8125,-0.265625 0.335938,-0.257813 0.5,-0.71875 0.5,-1.375 0,-0.394531 -0.082031,-0.738281 -0.25,-1.03125 -0.164062,-0.300781 -0.425781,-0.453125 -0.78125,-0.453125 -0.269531,0 -0.476562,0.121094 -0.625,0.359375 -0.082031,0.15625 -0.179688,0.460937 -0.296875,0.90625 L -2.8125,-2.421875 C -2.945312,-1.890625 -3.097656,-1.5 -3.265625,-1.25 c -0.28125,0.460938 -0.675781,0.6875 -1.1875,0.6875 -0.59375,0 -1.070313,-0.210938 -1.4375,-0.640625 C -6.253906,-1.628906 -6.4375,-2.207031 -6.4375,-2.9375 c 0,-0.9375 0.277344,-1.613281 0.828125,-2.03125 0.355469,-0.269531 0.734375,-0.398438 1.140625,-0.390625 l 0,1 c -0.238281,0.023437 -0.457031,0.105469 -0.65625,0.25 -0.269531,0.242187 -0.40625,0.664063 -0.40625,1.265625 0,0.398438 0.078125,0.699219 0.234375,0.90625 0.148437,0.199219 0.34375,0.296875 0.59375,0.296875 0.273437,0 0.492187,-0.132813 0.65625,-0.40625 0.09375,-0.15625 0.179687,-0.382813 0.25,-0.6875 l 0.171875,-0.6875 c 0.1875,-0.757813 0.367188,-1.269531 0.53125,-1.53125 0.273438,-0.40625 0.699219,-0.609375 1.28125,-0.609375 0.554688,0 1.03125,0.214844 1.4375,0.640625 0.40625,0.417969 0.609375,1.058594 0.609375,1.921875 0,0.9375 -0.2109375,1.605469 -0.625,2 -0.425781,0.386719 -0.953125,0.589844 -1.578125,0.609375 z m -4.453125,-1.546875 z"
           id="path4340"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-10"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -8.390625,-3.25 c 0,-1.082031 0.449219,-1.867188 1.34375,-2.359375 0.6875,-0.375 1.636719,-0.5625 2.84375,-0.5625 1.136719,0 2.078125,0.171875 2.828125,0.515625 1.074219,0.492188 1.609375,1.296875 1.609375,2.421875 0,1 -0.4375,1.75 -1.3125,2.25 -0.738281,0.40625 -1.722656,0.609375 -2.953125,0.609375 -0.945312,0 -1.765625,-0.125 -2.453125,-0.375 -1.269531,-0.457031 -1.90625,-1.289062 -1.90625,-2.5 z m 7.65625,0.015625 c 0,-0.550781 -0.238281,-0.988281 -0.71875,-1.3125 -0.488281,-0.320313 -1.394531,-0.484375 -2.71875,-0.484375 -0.945313,0 -1.726563,0.121094 -2.34375,0.359375 -0.613281,0.230469 -0.921875,0.683594 -0.921875,1.359375 0,0.625 0.292969,1.085938 0.875,1.375 0.585938,0.28125 1.445312,0.421875 2.578125,0.421875 0.855469,0 1.542969,-0.09375 2.0625,-0.28125 0.792969,-0.28125 1.1875,-0.757813 1.1875,-1.4375 z"
           id="path4343"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-11"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -2.96875,-3.96875 -3.8125,0 3.8125,2.6875 z M 0,-3.984375 l -2.046875,0 0,3.671875 -1.03125,0 -5.34375,-3.84375 0,-0.890625 5.453125,0 0,-1.234375 0.921875,0 0,1.234375 2.046875,0 z"
           id="path4346"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-12"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -4.875,-3.265625 c 0,-0.46875 -0.128906,-0.832031 -0.390625,-1.09375 C -5.523438,-4.617188 -5.832031,-4.75 -6.1875,-4.75 c -0.3125,0 -0.597656,0.125 -0.859375,0.375 -0.269531,0.25 -0.40625,0.632812 -0.40625,1.140625 0,0.511719 0.136719,0.882813 0.40625,1.109375 0.261719,0.230469 0.5625,0.34375 0.90625,0.34375 0.398437,0 0.710937,-0.144531 0.9375,-0.4375 0.21875,-0.300781 0.328125,-0.648438 0.328125,-1.046875 z m 4.15625,-0.0625 c 0,-0.488281 -0.128906,-0.894531 -0.390625,-1.21875 -0.269531,-0.320313 -0.664063,-0.484375 -1.1875,-0.484375 -0.539063,0 -0.953125,0.167969 -1.234375,0.5 -0.28125,0.335938 -0.421875,0.761719 -0.421875,1.28125 0,0.5 0.148437,0.914062 0.4375,1.234375 0.28125,0.3125 0.679687,0.46875 1.1875,0.46875 0.4375,0 0.820313,-0.144531 1.140625,-0.4375 0.3125,-0.289063 0.46875,-0.738281 0.46875,-1.34375 z m -3.75,1.5 c -0.125,0.292969 -0.269531,0.523437 -0.4375,0.6875 -0.3125,0.304687 -0.71875,0.453125 -1.21875,0.453125 -0.625,0 -1.160156,-0.222656 -1.609375,-0.671875 -0.457031,-0.457031 -0.6875,-1.097656 -0.6875,-1.921875 0,-0.8125 0.214844,-1.441406 0.640625,-1.890625 0.429688,-0.457031 0.921875,-0.6875 1.484375,-0.6875 0.523437,0 0.949219,0.132813 1.28125,0.390625 0.179687,0.148438 0.355469,0.375 0.53125,0.6875 0.15625,-0.34375 0.339844,-0.613281 0.546875,-0.8125 0.398438,-0.375 0.90625,-0.5625 1.53125,-0.5625 0.742188,0 1.367188,0.25 1.875,0.75 0.5117188,0.492188 0.765625,1.1875 0.765625,2.09375 0,0.824219 -0.21875,1.515625 -0.65625,2.078125 -0.445313,0.5625 -1.09375,0.84375 -1.9375,0.84375 -0.488281,0 -0.914063,-0.117187 -1.28125,-0.359375 -0.363281,-0.238281 -0.640625,-0.597656 -0.828125,-1.078125 z"
           id="path4349"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-13"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -5.9375,-1.15625 -0.8125,0 c -0.070312,-0.757812 -0.195312,-1.285156 -0.375,-1.578125 -0.175781,-0.300781 -0.585938,-0.53125 -1.234375,-0.6875 l 0,-0.828125 8.359375,0 0,1.125 -5.9375,0 z"
           id="path4352"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-14"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="M 0,-0.375 C -0.71875,-0.414062 -1.34375,-0.566406 -1.875,-0.828125 -2.414062,-1.085938 -2.90625,-1.59375 -3.34375,-2.34375 L -4,-3.46875 c -0.289062,-0.5 -0.539062,-0.851562 -0.75,-1.0625 -0.320312,-0.320312 -0.691406,-0.484375 -1.109375,-0.484375 -0.488281,0 -0.875,0.152344 -1.15625,0.453125 -0.289063,0.292969 -0.4375,0.679688 -0.4375,1.15625 0,0.730469 0.273437,1.230469 0.8125,1.5 0.304687,0.15625 0.710937,0.242188 1.21875,0.25 l 0,1.078125 c -0.726563,-0.007813 -1.320313,-0.144531 -1.78125,-0.40625 -0.8125,-0.457031 -1.21875,-1.265625 -1.21875,-2.421875 0,-0.957031 0.261719,-1.65625 0.78125,-2.09375 0.523437,-0.445312 1.101563,-0.671875 1.734375,-0.671875 0.667969,0 1.242188,0.234375 1.71875,0.703125 0.28125,0.273438 0.621094,0.761719 1.015625,1.46875 l 0.4375,0.8125 c 0.210937,0.375 0.414063,0.671875 0.609375,0.890625 0.335938,0.398437 0.710938,0.648437 1.125,0.75 l 0,-4.59375 1,0 z"
           id="path4355"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-15"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m -8.421875,-3.515625 c 0,-0.9375 0.246094,-1.585937 0.734375,-1.953125 0.480469,-0.375 0.980469,-0.5625 1.5,-0.5625 l 0,1.046875 c -0.332031,0.0625 -0.59375,0.164063 -0.78125,0.296875 C -7.320312,-4.425781 -7.5,-4.039062 -7.5,-3.53125 c 0,0.59375 0.277344,1.070312 0.828125,1.421875 0.542969,0.34375 1.320313,0.53125 2.328125,0.5625 -0.351562,-0.238281 -0.617188,-0.539063 -0.796875,-0.90625 -0.15625,-0.332031 -0.234375,-0.707031 -0.234375,-1.125 0,-0.707031 0.226562,-1.320313 0.671875,-1.84375 0.449219,-0.519531 1.121094,-0.78125 2.015625,-0.78125 0.761719,0 1.4375,0.25 2.03125,0.75 0.5859375,0.492187 0.875,1.195313 0.875,2.109375 0,0.792969 -0.296875,1.476562 -0.890625,2.046875 -0.601563,0.5625 -1.609375,0.84375 -3.015625,0.84375 -1.039062,0 -1.925781,-0.125 -2.65625,-0.375 -1.382812,-0.488281 -2.078125,-1.382813 -2.078125,-2.6875 z M -0.71875,-3.4375 c 0,-0.550781 -0.1875,-0.960938 -0.5625,-1.234375 -0.375,-0.28125 -0.816406,-0.421875 -1.328125,-0.421875 -0.425781,0 -0.832031,0.125 -1.21875,0.375 -0.382813,0.242188 -0.578125,0.6875 -0.578125,1.34375 0,0.449219 0.152344,0.84375 0.453125,1.1875 0.292969,0.34375 0.742187,0.515625 1.34375,0.515625 0.53125,0 0.980469,-0.15625 1.34375,-0.46875 0.367187,-0.3125 0.546875,-0.742187 0.546875,-1.296875 z"
           id="path4358"
           inkscape:connector-curvature="0" />
      </symbol>
      <symbol
         overflow="visible"
         id="glyph1-16"
         style="overflow:visible">
        <path
           style="stroke:none"
           d="m 0.234375,-3.125 c 0,1 -0.2695312,1.726562 -0.8125,2.171875 -0.550781,0.449219 -1.21875,0.671875 -2,0.671875 l 0,-1.109375 c 0.542969,-0.039063 0.9375,-0.140625 1.1875,-0.296875 0.4375,-0.28125 0.65625,-0.773438 0.65625,-1.484375 0,-0.5625 -0.144531,-1.007813 -0.4375,-1.34375 -0.300781,-0.332031 -0.6875,-0.5 -1.15625,-0.5 -0.570313,0 -0.972656,0.179687 -1.203125,0.53125 -0.238281,0.355469 -0.359375,0.84375 -0.359375,1.46875 0,0.074219 0.00781,0.148437 0.015625,0.21875 0,0.074219 0,0.148437 0,0.21875 l -0.9375,0 c 0.023438,-0.113281 0.03125,-0.207031 0.03125,-0.28125 0,-0.070313 0,-0.148437 0,-0.234375 0,-0.394531 -0.0625,-0.71875 -0.1875,-0.96875 -0.21875,-0.445312 -0.609375,-0.671875 -1.171875,-0.671875 -0.414063,0 -0.734375,0.152344 -0.953125,0.453125 -0.226562,0.292969 -0.34375,0.636719 -0.34375,1.03125 0,0.699219 0.234375,1.183594 0.703125,1.453125 0.25,0.148437 0.617187,0.230469 1.09375,0.25 l 0,1.046875 c -0.625,0 -1.15625,-0.125 -1.59375,-0.375 -0.78125,-0.425781 -1.171875,-1.179688 -1.171875,-2.265625 0,-0.851563 0.195312,-1.515625 0.578125,-1.984375 0.375,-0.46875 0.929687,-0.703125 1.65625,-0.703125 0.511719,0 0.929687,0.136719 1.25,0.40625 0.199219,0.179687 0.355469,0.402344 0.46875,0.671875 0.125,-0.4375 0.359375,-0.78125 0.703125,-1.03125 0.34375,-0.25 0.765625,-0.375 1.265625,-0.375 0.804687,0 1.460937,0.265625 1.96875,0.796875 0.5,0.523437 0.75,1.265625 0.75,2.234375 z"
           id="path4361"
           inkscape:connector-curvature="0" />
      </symbol>
    </g>
  </defs>
  <rect
     id="rect4364"
     style="fill:#fff3df;fill-opacity:1;stroke:none"
     height="396.73468"
     width="576"
     y="0"
     x="0" />
  <path
     style="fill:none;stroke:#45873b;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
     d="M 88.266406,310.63218 C 122.02676,237.79712 168.07359,167.7088 234.99687,121.36656 298.00502,77.074005 374.52101,57.963738 449.84844,47.956407 c 29.57913,-3.956704 59.3163,-6.634684 89.08593,-8.656254"
     id="path4366"
     inkscape:connector-curvature="0" />
  <path
     style="fill:none;stroke:#95473f;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
     d="M 88.266406,33.300157 C 122.02506,106.13557 168.07177,176.22644 234.99687,222.56578 c 63.74354,44.80848 141.26877,63.83721 217.47266,73.75781 28.71422,3.77216 57.57485,6.34594 86.46484,8.31216"
     id="path4368"
     inkscape:connector-curvature="0" />
  <g
     id="g4370"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-41.632655)">
    <use
       height="100%"
       width="100%"
       id="use4372"
       y="413.28125"
       x="289.06641"
       xlink:href="#glyph0-1"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4374"
       y="413.28125"
       x="296.39648"
       xlink:href="#glyph0-2"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4376"
       y="413.28125"
       x="299.0625"
       xlink:href="#glyph0-3"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4378"
       y="413.28125"
       x="309.05859"
       xlink:href="#glyph0-4"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4380"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4382"
       y="237.14844"
       x="12.960938"
       xlink:href="#glyph1-1"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4384"
       y="229.14453"
       x="12.960938"
       xlink:href="#glyph1-2"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4386"
       y="225.14844"
       x="12.960938"
       xlink:href="#glyph1-3"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4388"
       y="222.48242"
       x="12.960938"
       xlink:href="#glyph1-4"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4390"
       y="215.80859"
       x="12.960938"
       xlink:href="#glyph1-5"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4392"
       y="209.13477"
       x="12.960938"
       xlink:href="#glyph1-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4394"
       y="205.80078"
       x="12.960938"
       xlink:href="#glyph1-7"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4396"
       y="199.12695"
       x="12.960938"
       xlink:href="#glyph1-8"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4398"
       y="192.45312"
       x="12.960938"
       xlink:href="#glyph1-9"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4400"
       y="186.45312"
       x="12.960938"
       xlink:href="#glyph1-9"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <path
     inkscape:connector-curvature="0"
     id="path4402"
     d="m 86.957812,321.72593 458.527348,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4404"
     d="m 86.957812,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4406"
     d="m 152.46172,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4408"
     d="m 217.96562,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4410"
     d="m 283.46953,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4412"
     d="m 348.97344,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4414"
     d="m 414.47734,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4416"
     d="m 479.98125,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4418"
     d="m 545.48516,321.72593 0,7.20313"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <g
     id="g4420"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4422"
       y="384.48047"
       x="72.421875"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4424"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4426"
       y="384.48047"
       x="134.58984"
       xlink:href="#glyph0-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4428"
       y="384.48047"
       x="141.26367"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4430"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4432"
       y="384.48047"
       x="196.75391"
       xlink:href="#glyph0-7"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4434"
       y="384.48047"
       x="203.42773"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4436"
       y="384.48047"
       x="210.10156"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4438"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4440"
       y="384.48047"
       x="262.25781"
       xlink:href="#glyph0-7"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4442"
       y="384.48047"
       x="268.93164"
       xlink:href="#glyph0-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4444"
       y="384.48047"
       x="275.60547"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4446"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4448"
       y="384.48047"
       x="327.76172"
       xlink:href="#glyph0-8"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4450"
       y="384.48047"
       x="334.43555"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4452"
       y="384.48047"
       x="341.10938"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4454"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4456"
       y="384.48047"
       x="393.26562"
       xlink:href="#glyph0-8"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4458"
       y="384.48047"
       x="399.93945"
       xlink:href="#glyph0-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4460"
       y="384.48047"
       x="406.61328"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4462"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4464"
       y="384.48047"
       x="458.76953"
       xlink:href="#glyph0-9"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4466"
       y="384.48047"
       x="465.44336"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4468"
       y="384.48047"
       x="472.11719"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4470"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4472"
       y="384.48047"
       x="524.27344"
       xlink:href="#glyph0-9"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4474"
       y="384.48047"
       x="530.94727"
       xlink:href="#glyph0-6"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4476"
       y="384.48047"
       x="537.62109"
       xlink:href="#glyph0-5"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <path
     inkscape:connector-curvature="0"
     id="path4478"
     d="m 70.239062,313.46421 0,-282.992177"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4480"
     d="m 70.239062,313.46421 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4482"
     d="m 70.239062,278.08921 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4484"
     d="m 70.239062,242.71421 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4486"
     d="m 70.239062,207.34312 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4488"
     d="m 70.239062,171.96812 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4490"
     d="m 70.239062,136.59312 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4492"
     d="m 70.239062,101.21812 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4494"
     d="m 70.239062,65.843125 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <path
     inkscape:connector-curvature="0"
     id="path4496"
     d="m 70.239062,30.472033 -7.199218,0"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
  <g
     id="g4498"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4500"
       y="353.63281"
       x="41.761719"
       xlink:href="#glyph1-10"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4502"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4504"
       y="318.25781"
       x="41.761719"
       xlink:href="#glyph1-11"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4506"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4508"
       y="282.88281"
       x="41.761719"
       xlink:href="#glyph1-12"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4510"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4512"
       y="250.84766"
       x="41.761719"
       xlink:href="#glyph1-13"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4514"
       y="244.17383"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4516"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4518"
       y="215.47266"
       x="41.761719"
       xlink:href="#glyph1-13"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4520"
       y="208.79883"
       x="41.761719"
       xlink:href="#glyph1-15"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4522"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4524"
       y="180.09766"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4526"
       y="173.42383"
       x="41.761719"
       xlink:href="#glyph1-10"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4528"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4530"
       y="144.72266"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4532"
       y="138.04883"
       x="41.761719"
       xlink:href="#glyph1-11"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4534"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4536"
       y="109.34766"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4538"
       y="102.67383"
       x="41.761719"
       xlink:href="#glyph1-12"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <g
     id="g4540"
     style="fill:#5d4734;fill-opacity:1"
     transform="translate(11.2,-36.832655)">
    <use
       height="100%"
       width="100%"
       id="use4542"
       y="73.976562"
       x="41.761719"
       xlink:href="#glyph1-16"
       style="fill:#5d4734;fill-opacity:1" />
    <use
       height="100%"
       width="100%"
       id="use4544"
       y="67.302734"
       x="41.761719"
       xlink:href="#glyph1-14"
       style="fill:#5d4734;fill-opacity:1" />
  </g>
  <path
     inkscape:connector-curvature="0"
     id="path4546"
     d="m 70.239062,321.72593 486.722658,0 0,-299.519523 -486.722658,0 0,299.519523"
     style="fill:none;stroke:#5d4734;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" />
</svg>
Deleted pics/wy/back.jpg.

cannot compute difference between binary files

Deleted pics/wy/front.jpg.

cannot compute difference between binary files

Deleted pics/wy/power-switch.png.

cannot compute difference between binary files

Deleted pics/wy/serial-db9.jpg.

cannot compute difference between binary files

Deleted pics/wy/serial-kk.jpg.

cannot compute difference between binary files

Deleted pics/wy/system.jpg.

cannot compute difference between binary files

Changes to src/PDP8/pdp8_cpu.c.
50
51
52
53
54
55
56

57
58
59
60
61
62
63
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64







+







   dealings in this Software without prior written authorization from those
   authors.

   ----------------------------------------------------------------------------

   cpu          central processor

   07-Sep-17    RMS     Fixed sim_eval declaration in history routine (COVERITY)
   09-Mar-17    RMS     Fixed PCQ_ENTRY for interrupts (COVERITY)
   13-Feb-17    RMS     RESET clear L'AC, per schematics
   28-Jan-17    RMS     Renamed switch register variable to SR, per request
   18-Sep-16    RMS     Added alternate dispatch table for non-contiguous devices
   17-Sep-13    RMS     Fixed boot in wrong field problem (Dave Gesswein)
   28-Apr-07    RMS     Removed clock initialization
   30-Oct-06    RMS     Added idle and infinite loop detection
217
218
219
220
221
222
223

224


225
226
227
228
229
230
231
218
219
220
221
222
223
224
225

226
227
228
229
230
231
232
233
234







+
-
+
+








   3. Adding I/O devices.  These modules must be modified:

        pdp8_defs.h     add device number and interrupt definitions
        pdp8_sys.c      add sim_devices table entry
*/

#include "pdp8_defs.h"
/* ---PiDP change------------------------------------------------------------------------------------------- */

/* ---PiDP add---------------------------------------------------------------------------------------------- */
#include "gpio-common.h"
#include "pidp8i.h"
/* ---PiDP end---------------------------------------------------------------------------------------------- */

#define PCQ_SIZE        64                              /* must be 2**n */
#define PCQ_MASK        (PCQ_SIZE - 1)
#define PCQ_ENTRY(x)    pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = x
408
409
410
411
412
413
414
415

416
417
418
419
420
421
422
411
412
413
414
415
416
417

418
419
420
421
422
423
424
425







-
+







        if ((reason = sim_process_event ())) {
/* ---PiDP add--------------------------------------------------------------------------------------------- */
            // We're about to leave the loop, so repaint one last time
            // in case this is a Ctrl-E and we later get a "cont"
            // command.  Set a flag that will let us auto-resume.
            extern int resumeFromInstructionLoopExit, swStop, swSingInst;
            resumeFromInstructionLoopExit = swStop = swSingInst = 1;
            set_pidp8i_leds (PC, MA, MB, IR, LAC, MQ, IF, DF, SC,
            set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC,
                    int_req, Pause);

            // Also copy SR hardware value to software register in case
            // the user tries poking at it from the sim> prompt.
            SR = get_switch_register();
/* ---PiDP end---------------------------------------------------------------------------------------------- */
            break;
434
435
436
437
438
439
440
441

442
443
444
445
446
447
448
437
438
439
440
441
442
443

444
445
446
447
448
449
450
451







-
+







            sim_interval = sim_interval - 1;

            // Have to keep display updated while stopped.  This does
            // mean if the software starts with the STOP switch held
            // down, we'll put garbage onto the display for MA, MB, and
            // IR, but that's what the real hardware does, too.  See
            // https://github.com/simh/simh/issues/386
            set_pidp8i_leds (PC, MA, MB, IR, LAC, MQ, IF, DF, SC,
            set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC,
                    int_req, Pause);

            // Go no further in STOP mode.  In particular, fetch no more
            // instructions, and do not touch PC!
            continue;

        case pft_halt:
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
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







-
+
-













+








    if (++skip_count >= (max_skips - dither)) {
        // Save skips to inst counter and reset
        inst_count += skip_count;
        skip_count = 0;

        // We need to update the LED data again
        set_pidp8i_leds (PC, MA, MB, IR, LAC, MQ, IF, DF, SC, int_req, Pause);
        set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, int_req, Pause);
        Pause = 0;

        // Has it been ~1s since we updated our max_skips value?
        time_t now;
        if (time(&now) > last_update) {
            // Yep; simulator IPS may have changed, so freshen it.
            last_update = now;
            max_skips = inst_count / pidp8i_updates_per_sec;
            //printf("Inst./repaint: %zu - %zu; %.2f MIPS\r\n",
            //        max_skips, dither, inst_count / 1e6);
            inst_count = 0;
            }
        dither = max_skips > 32 ? lrand48() % (max_skips >> 3) : 0; // 12.5%
        }
    Pause = 0;      // it's set outside the "if", so it must be *reset* outside
/* ---PiDP end---------------------------------------------------------------------------------------------- */
    }                                                   /* end while */

/* Simulation halted */

saved_PC = IF | (PC & 07777);                           /* save copies */
saved_DF = DF & 070000;
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1763
1764
1765
1766
1767
1768
1769

1770
1771
1772
1773
1774
1775
1776







-







/* Show history */

t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 l, k, di, lnt;
const char *cptr = (const char *) desc;
t_stat r;
t_value sim_eval;
InstHistory *h;

if (hst_lnt == 0)                                       /* enabled? */
    return SCPE_NOFNC;
if (cptr) {
    lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);
    if ((r != SCPE_OK) || (lnt == 0))
1783
1784
1785
1786
1787
1788
1789
1790
1791


1792
1793
1794
1795
1796
1797
1798
1799
1785
1786
1787
1788
1789
1790
1791


1792
1793
1794
1795
1796
1797
1798
1799
1800
1801







-
-
+
+








    h = &hst[(++di) % hst_lnt];                         /* entry pointer */
    if (h->pc & HIST_PC) {                              /* instruction? */
        l = (h->lac >> 12) & 1;                         /* link */
        fprintf (st, "%05o  %o %04o  %04o  ", h->pc & ADDRMASK, l, h->lac & 07777, h->mq);
        if (h->ir < 06000)
            fprintf (st, "%05o  ", h->ea);
        else fprintf (st, "       ");
        sim_eval = h->ir;
        if ((fprint_sym (st, h->pc & ADDRMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0)
        sim_eval[0] = h->ir;
        if ((fprint_sym (st, h->pc & ADDRMASK, sim_eval, &cpu_unit, SWMASK ('M'))) > 0)
            fprintf (st, "(undefined) %04o", h->ir);
        if (h->ir < 04000)
            fprintf (st, "  [%04o]", h->opnd);
        fputc ('\n', st);                               /* end line */
        }                                               /* end else instruction */
    }                                                   /* end for */
return SCPE_OK;
}
Changes to src/PDP8/pidp8i.c.in.
27
28
29
30
31
32
33


34
35


36
37
38
39
40
41
42
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46







+
+


+
+







   authors.
*/

#include "pidp8i.h"

#include "../gpio-common.h"

#include "pdp8_defs.h"

#include <assert.h>
#include <dirent.h> // for USB stick searching
#include <errno.h>
#include <string.h>


//// MODULE GLOBALS ////////////////////////////////////////////////////

// handle_sing_step() sets this to nonzero and returns a value breaking
// us out of the PDP-8 simulator's sim_instr() loop, which causes SCP to
// call our build_pidp8i_scp_cmd(), which gives SCP a command to run:
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
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







+









+
+
+
-
+
+

+

















-
-
-
+
+
+











-
+
+
+
+
+
+


-
+


+







// slowly rise or they can overshoot and then take a while to recover
// with the IPS.

size_t get_pidp8i_initial_max_skips (size_t updates_per_sec)
{
    DEVICE *pthrot = find_dev ("INT-THROTTLE");
    if (pthrot) {
        extern int suppressILS;
        REG *ptyper = find_reg ("THROT_TYPE", NULL, pthrot);
        REG *pvalr  = find_reg ("THROT_VAL", NULL, pthrot);
        if (ptyper && pvalr) {
            uint32 *ptype = ptyper->loc;
            uint32 *pval  =  pvalr->loc;
            size_t ips = 0;
            switch (*ptype) {
                case SIM_THROT_MCYC: ips = *pval * 1e6; break;
                case SIM_THROT_KCYC: ips = *pval * 1e3; break;
                case SIM_THROT_SPC: {
                    suppressILS = 1;
                    break;
            }
                }
            }
            if (ips) {
                suppressILS = 0;
                printf("PiDP-8/I initial throttle = %zu IPS\r\n", ips);
                return ips / updates_per_sec;
            }
        }
    }

    // No better idea, so give a plausible value for an unthrottled Pi 1
    return 200;
}


//// set_pidp8i_leds ///////////////////////////////////////////////////
// Given all of the PDP-8's internal registers that affect the front
// panel display, modify the GPIO thread's LED state values accordingly.
//
// Also update the LED brightness values based on those new states.

void set_pidp8i_leds (uint32 sPC, uint32 sMA, uint16 sMB,
    uint16 sIR, int32 sLAC, int32 sMQ, int32 sIF, int32 sDF,
    int32 sSC, int32 int_req, int Pause)
void set_pidp8i_leds (uint32 sPC, uint32 sMA, uint16 sIR, int32 sLAC,
    int32 sMQ, int32 sIF, int32 sDF, int32 sSC, int32 int_req,
    int Pause)
{
    // Bump the instruction count.  This should always be equal to the
    // Fetch LED's value, but integers are too cheap to get cute here.
    //
    // Note that we only update pdis_update directly once in this whole
    // process.  This is in case the display swap happens while we're
    // working: we want to finish work on the same display even though
    // it's now called the paint-from display, so it's consistent.
    display* pd = pdis_update;
    ++pd->inst_count;

    // Rows 0-4, easy cases: single-register LED strings
    // Rows 0-4, easy cases: single-register LED strings.
    //
    // The only non-obvious one is that we use the PDP-8 simulator's IR
    // register value for the MB lights due to a quirk in the way the
    // register states are set at the time this function is called; MB
    // is not passed to us because its value at the call time is bogus.
    set_pidp8i_row_leds (pd, 0, sPC);
    set_pidp8i_row_leds (pd, 1, sMA);
    set_pidp8i_row_leds (pd, 2, sMB);
    set_pidp8i_row_leds (pd, 2, sIR);
    set_pidp8i_row_leds (pd, 3, sLAC & 07777);
    set_pidp8i_row_leds (pd, 4, sMQ);


#if 0   // debugging
    static time_t last = 0, now;
    if (time(&now) != last) {
        uint16* pcurr = pd->curr;
        printf("\r\nSET: [PC:%04o] [MA:%04o] [MB:%04o] [AC:%04o] [MQ:%04o]",
                pcurr[0], pcurr[1], pcurr[2], pcurr[3], pcurr[4]);
291
292
293
294
295
296
297
298
299
300
301






302




303
304

305
306



307
308
309
310
311
312
313
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







-
-
-
-
+
+
+
+
+
+
-
+
+
+
+


+
-
-
+
+
+








    // Row 5b: set the Defer LED if...
    if ((inst_type <= 05000) &&  // it's a memory reference instruction
            (sIR & 00400)) {     // and indirect addressing flag is set
        set_pidp8i_led (pd, 5, 1);
    }

    // Row 5c: The Fetch LED is bumped once per CPU instruction, as is
    // Execute while we're not in STOP state.  They're set at different
    // times, but they're twiddled so rapidly that they both just become
    // a 50% blur in normal operation, so we don't make the CPU core set
    // Row 5c: The Fetch & Execute LEDs are pulsed once per instruction.
    // On real hardware, the pulses don't happen at exactly the same
    // time, but we can't simulate that because SIMH handles each CPU
    // instruction "whole."  When running real code, all we care about
    // is that both LEDs are twiddled so rapidly that they both just
    // become a 50% blur, mimicking the hardware closely enough.
    // these "on-time."  It just doesn't matter.
    //
    // The exception is that when the CPU is stopped, both LEDs are off,
    // because the pulses happen "outside" the STOP state: Fetch before
    // and Execute after resuming from STOP.
    extern int swStop, swSingInst;
    int running = !swStop && !swSingInst;
    if (running) {
    if (running) set_pidp8i_led (pd, 5, 2);    // Execute
    set_pidp8i_led (pd, 5, 3);                 // Fetch
        set_pidp8i_led (pd, 5, 2);    // Execute
        set_pidp8i_led (pd, 5, 3);    // Fetch
    }

    // Row 6a: Remaining LEDs in upper right block
    pd->curr[6] = 0;
    if (running)           set_pidp8i_led (pd, 6, 7); // bump Run LED
    if (Pause)             set_pidp8i_led (pd, 6, 8); // bump Pause LED
    if (int_req & INT_ION) set_pidp8i_led (pd, 6, 9); // bump ION LED

326
327
328
329
330
331
332
333
334


335
336
337
338
339

340
341
342
343
344
345
346
349
350
351
352
353
354
355


356
357
358
359
360
361

362
363
364
365
366
367
368
369







-
-
+
+




-
+







    if (!running || resumeFromInstructionLoopExit) {
        memcpy(pdis_paint, pdis_update, sizeof(struct display));
    }
}


//// mount_usb_stick_file //////////////////////////////////////////////
// Search for a PDP-8 media image in one of the Pi's USB auto-mount
// directories and attempt to ATTACH it to the simulator.
// Search for a PDP-8 media image on a USB device mounted under /media
// and attempt to ATTACH it to the simulator.

static void mount_usb_stick_file (int devNo, char *devCode)
{
    char    sFoundFile[CBUFSIZE] = { '\0' };
    char    sUSBPath[CBUFSIZE];     // will be "/media/usb0" etc
    char    sDirName[CBUFSIZE];     // will be "/media/DIRNAME" etc
    char    fileExtension[4];       // will be ".RX" etc
    int     i, j;

    // Build expected file name extension from the first two characters of
    // the passed-in device code.
    fileExtension[0] = '.';                     // extension starts with a .
    strncpy (fileExtension + 1, devCode, 2);    // extension is PT, RX, RL etc
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
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







-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+







    // we keep track is so we don't have the same media image file attached
    // to both devices of a given type we support.  That is, you can't have
    // a given floppy image file attached to both RX01 drives, but you *can*
    // repeatedly re-ATTACH the same floppy image to the first RX01 drive.
    static char mountedFiles[8][CBUFSIZE];
    mountedFiles[devNo][0] = '\0';

    for (i = 0; i < 8 && sFoundFile[0] == '\0'; ++i) {
        // search all 8 USB mount points, numbered 0-7
        snprintf (sUSBPath, sizeof (sUSBPath), "/media/usb%d", i);
        DIR *pDir = opendir (sUSBPath);
        if (pDir) {
            struct dirent* pDirent;
            while ((pDirent = readdir (pDir)) != 0) { // search all files in directory
                if (pDirent->d_name[0] == '.') continue;    // dotfiles clutter debug output
	// Search all directories under /media
	DIR *pDir1 = opendir ("/media");
	if (pDir1) {
		struct dirent* pDE1;
		while ((pDE1 = readdir (pDir1)) != 0) {
			if (pDE1->d_type != DT_DIR) continue;

			// Found a directory under /media.  Search it for plausibly
			// named files given devCode.
			snprintf (sDirName, sizeof(sDirName), "/media/%s", pDE1->d_name);
			DIR *pDir2 = opendir (sDirName);
			if (pDir2) {
				struct dirent* pDE2;
				while ((pDE2 = readdir (pDir2)) != 0) {      // search all files in directory
					if (pDE2->d_name[0] == '.') continue;    // dotfiles clutter debug output

                char* pext = strstr (pDirent->d_name, fileExtension);
                if (pext && (pext == (pDirent->d_name + strlen (pDirent->d_name) - 3))) {
                    snprintf (sFoundFile, sizeof (sFoundFile), "%s/%s",
                            sUSBPath, pDirent->d_name);
#if 0   // debugging
                    printf("\r\nFound candidate file %s for dev %s, ext *%s...",
                            sFoundFile, devCode, fileExtension);
#endif
                    for (j = 0; j < 7; ++j) {
                        if (strncmp (mountedFiles[j], sFoundFile, CBUFSIZE) == 0) {
#if 0   // debugging
                            printf("\r\nAlready have %s mounted, slot %d; will not remount.",
                                    sFoundFile, j);
#endif
                            sFoundFile[0] = '\0';   // don't leave outer loop; keep looking
                            break;
                        }
                    }
                    if (j == 7) {
                        // Media image file is not already mounted, so leave while
                        // loop with path set to mount it
                        break;
                    }
                }
#if 0   // debugging
                else {
                    printf("\r\nFile %s on %s doesn't match *%s...",
                            pDirent->d_name, sUSBPath, fileExtension);
                }
#endif
            }
					char* pext = strstr (pDE2->d_name, fileExtension);
					if (pext && (pext == (pDE2->d_name + strlen (pDE2->d_name) - 3))) {
						snprintf (sFoundFile, sizeof (sFoundFile), "%s/%s",
								sDirName, pDE2->d_name);
	#if 0   // debugging
						printf("\r\nFound candidate file %s for dev %s, ext *%s...",
								sFoundFile, devCode, fileExtension);
	#endif
						for (j = 0; j < 7; ++j) {
							if (strncmp (mountedFiles[j], sFoundFile, CBUFSIZE) == 0) {
	#if 0   // debugging
								printf("\r\nAlready have %s mounted, slot %d; will not remount.",
										sFoundFile, j);
	#endif
								sFoundFile[0] = '\0';   // don't leave outer loop; keep looking
								break;
							}
						}
						if (j == 7) {
							// Media image file is not already mounted, so leave while
							// loop with path set to mount it
							break;
						}
					}
	#if 0   // debugging
					else {
						printf("\r\nFile %s on %s doesn't match *%s...",
								pDE2->d_name, sDirName, fileExtension);
					}
	#endif
				} // end while (pDE2...)

            closedir (pDir);
        }
        else {
            // Not a Pi or the USB auto-mounting software isn't installed
            printf ("\r\nCannot open %s: %s\r\n", sUSBPath, strerror (errno));
            return;
        }
    }
				closedir (pDir2);
			} // end if (pDir2)
			else {
				// USB auto-mounting either doesn't work here or uses
				// something other than the /media/DIR/FILE.EXT scheme
				// we expect.
				printf ("\r\nCannot open %s: %s\r\n", sDirName, strerror (errno));
				return;
			}
		} // end while (pDE1...)

		closedir(pDir1);
	} // end if (pDir1)

    if (sFoundFile[0]) {            // no file found, exit
        if (access (sFoundFile, R_OK) == 0) {
            char sAttachCmd[CBUFSIZE] = { '\0' };
            snprintf (sAttachCmd, sizeof(sAttachCmd), "%s %s",
                    devCode, sFoundFile);
            t_stat scpCode = attach_cmd ((int32) 0, sAttachCmd);
Changes to src/PDP8/pidp8i.h.
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
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







-
+
+









-
+


-
-
-
+
+
+

-
-
-
+
+
+


   dealings in this Software without prior written authorization from those
   authors.
*/

#if !defined(PIDP8I_H)
#define PIDP8I_H

#include "pdp8_defs.h"
#include <stdint.h>
#include <stdlib.h>

typedef enum {
    pft_normal,
    pft_halt,
    pft_stop,
} pidp8i_flow_t;

extern char *build_pidp8i_scp_cmd (char* cbuf, size_t cbufsize); 

extern int32 get_switch_register (void);
extern int32_t get_switch_register (void);
extern size_t get_pidp8i_initial_max_skips (size_t updates_per_sec);

extern pidp8i_flow_t handle_flow_control_switches (uint16* pM,
        uint32 *pPC, uint32 *pMA, int32 *pMB, int32 *pLAC, int32 *pIF,
        int32 *pDF, int32* pint_req);
extern pidp8i_flow_t handle_flow_control_switches (uint16_t* pM,
        uint32_t *pPC, uint32_t *pMA, int32_t *pMB, int32_t *pLAC, int32_t *pIF,
        int32_t *pDF, int32_t* pint_req);

extern void set_pidp8i_leds (uint32 sPC, uint32 sMA, uint16 sMB,
        uint16 sIR, int32 sLAC, int32 sMQ, int32 sIF, int32 sDF,
        int32 sSC, int32 int_req, int Pause);
extern void set_pidp8i_leds (uint32_t sPC, uint32_t sMA, uint16_t sIR,
        int32_t sLAC, int32_t sMQ, int32_t sIF, int32_t sDF, int32_t sSC,
        int32_t int_req, int Pause);

#endif // !defined(PIDP8I_H)
Added src/asm/asr33-rim-loader.pal.

















































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ asr33-rim-loader.pal
/
/ Load paper tapes in RIM format from a Teletype Model 33 ASR's paper
/ tape reader.  By contrast with the loader in hs-rim-loader.pal, this
/ is the low-speed paper tape RIM loader.
/
/ This is the RIM loader printed on the front panel of the PDP-8/I.  The
/ RIM loader in hs-rim-loader.pal (which is translated to boot/1.script
/ at build time) is similar to this one, but it works on the DEC high-
/ speed paper tape reader peripheral, which is where the PiDP-8/I's
/ automatic paper tape mounting feature attaches any tape images it
/ finds via USB.
/
/ Therefore, you cannot use this RIM loader if you want to use the
/ PiDP-8/I's automatic media attachment mechanism.  It is included
/ here mainly for documentation at the moment, but also in case
/ someone works out a way to build the simulator so that it can
/ conditionally load tapes from an emulated ASR-33.
/
/ Raw disassembly done from the octal values by Bernhard Baehr's PDP-8/E
/ Simulator.  Comments and labels by Warren Young.  Original copyright
/ by Digital Equipment Corporation: this program appeared in DEC manuals
/ and was printed on the front panel of every PDP-8/I.
/
/ SIMH: echo Installing the RIM loader for the ASR 33 paper tape reader...
/ SIMH: set df disabled

	*7755
	HLT		/ nonstandard: auto-halt on SIMH startup

	/ normal RIM loader entry point, 7756
NEXT,	KCC		/ clear PTR flag
RBYTE1,	KSF		/ loop until PTR is ready
	JMP RBYTE1
	KRB		/ read first byte in
	CLL RTL		/ shift it left by 2, clearing link as well
	RTL		/ and 2 more again
	SPA		/ if top bit of AC is set...
	JMP RBYTE1	/ ...AC contains the addr's value
	RTL		/ ...else rotate it another 2 positions
RBYTE2,	KSF		/ wait for next character
	JMP RBYTE2
	KRS		/ read second byte in
	SNL		/ if link is set...
	DCA I BYTE	/ ...it's the value's address
GOTVAL,	DCA BYTE	/ ...else it's the value at that addr
	JMP NEXT	/ go round again, getting next value from PTR
BYTE,	0
$
Added src/asm/hs-rim-loader.pal.















































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ hs-rim-loader.pal
/
/ Load paper tapes in RIM format from the DEC high-speed PTR.
/
/ This routine differs from that printed in DEC's manuals in that it
/ starts with a HLT instruction so that when you [re]start SIMH with
/ IF=1, running this program, the processor auto-halts, giving the user
/ the opportunity to attach a paper tape via DF or ATTACH, then start
/ the processor at 7756 as normal.
/
/ The RIM loader code printed on the front panel of the PDP-8/I differs
/ because it is for the the paper tape reader built into a Teletype
/ Model 33 ASR.  See asr33-rim-loader.pal for that other implementation,
/ including more information about it.
/
/ Raw disassembly done from the octal values by Bernhard Baehr's PDP-8/E
/ Simulator.  Comments and labels by Warren Young.  Original copyright
/ by Digital Equipment Corporation: this program appeared in many DEC
/ manuals printed throughout the PDP-8 era variants of which were made
/ (and thus documented) from 1965 to 1979.
/
/ SIMH: echo Installing the RIM loader for the DEC high-speed tape reader...
/ SIMH: set df disabled
/ SIMH: set cpu noidle

	*7755
	HLT		/ nonstandard: auto-halt on SIMH startup

	/ normal RIM loader entry point, 7756
	RFC		/ clear PTR flag
RBYTE1,	RSF		/ loop until PTR is ready
	JMP RBYTE1
	RCC		/ read first byte in
	CLL RTL		/ shift it left by 2, clearing link as well
	RTL		/ and 2 more again
	SPA		/ if top bit of AC is set...
	JMP GOTVAL	/ ...AC contains the addr's value
	RTL		/ ...else rotate it another 2 positions
RBYTE2,	RSF		/ wait for next character
	JMP RBYTE2
	RCC		/ read second byte in
	SNL		/ if link is set...
	DCA I BYTE	/ ...it's the value's address
GOTVAL,	DCA BYTE	/ ...else it's the value at that addr
	JMP RBYTE1	/ go round again, getting next value from PTR
BYTE,	0
$
Added src/cc8/GPL3.txt.


































































































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
Added src/cc8/Makefile.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
########################################################################
# Makefile.in - Processed by autosetup's configure script to generate
#    an intermediate GNU make(1) file for building the PiDP-8/I software
#    from within its src/cc8/ subdirectory.
#
# The resulting Makefile will redirect simple "make" calls to the top
# level as well as the major top-level targets (e.g. "make clean") but
# purposefully will not redirect anything like an installation or "run
# the system" type target.  Its only purpose is to help out those who
# are working on CC8 from within this directory.  If you need to work
# on the wider system, do it from the project's top level.
#
# If you are seeing this at the top of a file called Makefile and you
# intend to make edits, do that in Makefile.in.  Saying "make" will then
# re-build Makefile from that modified Makefile.in before proceeding to
# do the "make" operation.
#
# Copyright © 2017 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 clean ctags distclean tags reconfig:
	cd @builddir@; make $@
Added src/cc8/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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# A Minimal Implementation of C for the DEC PDP-8 Processor

## Introduction

The C language and its derivatives are now the industry standard for the
development of operating systems and utilities. The language has evolved
significantly since its initial specification in 1972. At this time, the
PDP-7 was used for the initial implementation and the compiler ported to
a number of other systems including the PDP-11. Also, the first
glimmerings of Unix appeared following a re-write of the assembly
language version in C and the rest is of course history. The PDP-8 was
introduced by DEC in 1965 at the same time as the PDP-7 with the
intention of being a small and cheap processor that could be used in a
variety of environments. From this simple machine, the modern desktop
device has evolved which I am using to type this document. Nonetheless,
far from fading into obscurity, there is a very active group of
enthusiasts who have looked to implementing the PDP-8 on modern hardware
and the thanks to Oscar Vermuelen and others, we can all have a PDP8/I
to play with. With this in mind, I thought it was time to have a modern
language compiler running on the PDP-8 which as far as I can tell, the
last native compiler developed for the PDP-8 was Pascal in 1979 by Heinz
Stegbauer. In more recent times, one cross-compiler has been developed
by Vince Slyngstad and updated by Paolo Maffei based on Ron Cain’s
Small-C using a VM approach. [This code][sms] is most certainly worth
examining, and I am delighted to acknowledge this work as I have used
some of the C library code in this project.

[sms]: http://so-much-stuff.com/pdp8/C/C.php

Finally, I would refer the reader to Fabrice Bellard’s OTCC. It is this
bit of remarkable software that suggested that there may be a chance to
implement a native PDP-8 compiler.

Developing a native compiler for the PDP-8 is not an easy task as this
processor has a very limited address space and no hardware stack. And,
although the option exists to write the whole thing in assembly language
as has been the case for Pascal and Algol, this project has explored the
option of writing the compiler itself in C. To this end, 2 compilers
have been written. Firstly, a cross-compiler based again on Ron Cain’s
Small-C which is used to compile the native OS/8 compiler and library.
As yet, the native compiler has rather limited functionality and will
not compile itself. The cross-compiler will compile itself but produces
an enormous (28K) assembler file which cannot be run on the PDP-8.


<a id="cross" name="posix"></a>
# The Cross-Compiler

The code for this is in the `cross` subdirectory, and is built along
with the top-level PiDP-8/I software. When installed, it is in your
`PATH` as `cc8`.

The CC8 cross-compiler is based upon Ron Cain’s famous Small-C compiler.
The reader is directed to the extensive documentation available on the
web.

The key file is the PDP-8 code generator in `code8.c` which emits SABR —
Symbolic Assembler for Binary Relocatable programmes — assembly code.
SABR is normally used as the second pass of the OS/8 FORTRAN II system.

When you use the cross-compiler on a POSIX type system such as the
Raspbian PiDP-8/I environment, the resulting `*.sb` files will have
LF-only line endings, but OS/8 expects CR+LF line endings. The `txt2ptp`
utility program included with the PiDP-8/I distribution will
automatically do that conversion for you when making a SIMH paper tape
image file, which you can then read into the OS/8 environment.

The cross-compiler has some non-standard features to enable the
interface between the main programme and the C library. This constitutes
a compile time linkage system to allow for standard and vararg functions
to be called in the library.

Several of the C programs in this distribution `#include <init.h>` which
inserts an assembly language initialization routine into the program at
that point using the `#asm` inline assembly feature. This file is
symlinked into each directory that has a `*.c` file needing it since CC8
doesn't have an include path feature, and it must be in the current
directory in any case when using the OS/8 version of CC8.

The `init.h` initialization routine defines some low-level subroutines,
initializes the environment for the programs, and calls into the LIBC
initialization code. This file was copied to the OS/8 boot disk as
`DSK:INIT.H` unless you gave `--disable-os8-cc8` when configuring the
PiDP-8/I software.

The file `include/libc.h` is likewise copied to `DSK:LIBC.H`. It defines
the mappings between the familiar C library routine names and their
underlying implementation names.

The linking loader determines the core layout for the built programs.
Most commonly, it uses this scheme:

**Field 0:** FOTRAN library utility functions and OS/8 I/O system

**Field 1:** The programme’s runtime stack/globals/literals

**Field 2:** The programme's executable code

**Field 3:** The LIBC library code

Since this memory layout applies to the phases of the CC8 compiler as
well, this means that each phase uses approximately 16 kWords of core.


<a id="native" name="os8"></a>
## The Native Compiler

This compiler is supplied in both source and binary forms as part of the
PiDP-8/I software distribution.

We ship pre-built binaries to avoid a chicken-and-egg problem: the
binaries require a working OS/8 environment to be built, but when the
PiDP-8/I build system goes to build the bootable OS/8 media, it expects
to have the OS/8 CC8 binaries at hand so it can copy them to the RK05
disk it is building. It's trivial to deal with that on our development
systems, since we normally have a working `os8v3d-*.rk05` disk set from
the previous build sitting around to bootstrap the process, so we break
the cycle at that point rather than do a two-stage RK05 bootstrap build
on end-user systems.

These pre-built binaries are saved as `media/os8/subsys/cc8.tu56` by the
`tools/cc8-tu56-update` script. Basically, that script uses the
cross-compiler to produce SABR assembly files for each stage of the OS/8
CC8 compiler, which it then copies into the OS/8 environment, then it
assembles, links, and saves the result as `CC*.SV`:

2.  `c8.c` &rarr; `c8.sb` &rarr; `CC.SV`: The compiler driver: accepts
    the input file name from the user, and calls the first proper
    compiler stage, `CC1`. Should we add a preprocessor feature, this
    driver will call it before calling `CC1`.

2.  `n8.c` &rarr; `n8.sb` &rarr; `CC1.SV`: The parser/tokeniser section
    of the compiler.

3.  `p8.c` &rarr; `p8.sb` &rarr; `CC2.SV`: The token to SABR code
    converter section of the compiler.

4.  `libc.c` &rarr; `libc.sb` &rarr; `LIBC.RL`: The C library linked to
    any program built with CC8, including the stages above, but also to
    your own programs.

If you are not changing the OS/8 CC8 source code, you needn't run the
`cc8-tu56-update` script or build the OS/8 version of CC8 by hand.

The PiDP-8/I build system's OS/8 RK05 media build script copies those
files and the other files required for building C programs under OS/8 to
the appropriate OS/8 volumes: `CC*.SV` on `SYS:`, and everything else on
`DSK:`. 

Input programs should go on `DSK:`. Compiler outputs are also placed on
`DSK:`.

To try it out:

Boot OS/8 within the PiDP-8/I environment as you normally would. If
you're at the Linux command prompt within the PiDP-8/I source tree, you
can start it most easily with a `make run` command.

With the OS/8 environment running, you can enter a C programme in lower
case via the editor, but before doing that, try building a copy of one
of the example programs:

    .R CC            ⇠ compiler front end
    >ps.c            ⇠ takes name of C program; creates CC.SB
    .COMP CC         ⇠ compile SABR output of CC8 to CC.RL

Link and run it with:

    .R LOADER
    *CC,LIBC/G       ⇠ CC.RL + pre-built LIBC.RL = runnable program; /G = "go"

These steps are wrapped up into the `CC.BI` BATCH file:

    .EXE CC.BI       ⇠ must specify .BI to avoid running CC.SV instead
    >ps.c            ⇠ builds, links, and runs it

That demo is particularly interesting. It generates Pascal’s triangle
without using factorials, which are a bit out of range for 12 bits!


<a id="warning"></a>
## GOVERNMENT HEALTH WARNING

**You are hereby warned**: The native OS/8 compiler does not contain any
error checking whatsoever. If the source files contain an error or you
mistype a build command, you may get:

*   A runtime crash in the compiler
*   SABR assembly output that won't assemble
*   Output that assembles but won't run correctly

Rarely will any of these failure modes give any kind of sensible hint as
to the cause. OS/8 CC8 cannot afford the hundreds of kilobytes of error
checking and text reporting that you get in a modern compiler like GCC
or Clang. That would have required a roomful of core memory to achieve
on a real PDP-8. Since we're working within the constraints of the old
PDP-8 architecture, we only have about 3 kWords to construct the parse
result, for example.

In addition, the native OS/8 compiler is severely limited in code space,
so it does not understand the full C language. It is less functional
than K&R C 1978; we do not have a good benchmark for what it compares to
in terms of other early C dialects, but we can sum it up in a single
word: "primitive."

Nonetheless, our highly limited C dialect is Turing complete. It might
be better to think of it as a high-level assembly language that
resembles C rather than as "C" proper.


<a id="cross-fl"></a>
### Features and Limitations of the Cross-Compiler

The features of the cross-compiler are basically that of Small-C itself,
the primary difference being in the PDP-8 SABR code generator, which
doesn't affect its C language support.

A good approximation is K&R C (1978) minus:

*   `struct` and `union`

*   function pointers

*   `float` and `long`


<a id="features"></a>
### Features of the OS/8 CC8 Compiler

The OS/8 version of CC8 is missing many features relative to the
cross-compiler, and much more compared to modern C. Before we list
those limitations, here is what is known to work:

1.  **Local and global variables**

1.  **Pointers,** within limitations given in the following section.

1.  **Functions:** Parameter lists must be declared in K&R form:

        int foo (a, b)
        int a, b;
        {
            ...
        }

1.  **Recursion:** See [`FIB.CC`][fib] for an example of this.

[fib]: https://tangentsoft.com/pidp8i/doc/src/cc8/examples/fib.c

1.  **Simple arithmetic operators:** `+`, `-`, `*`, `/`, etc.

1.  **Bitwise operators:** `&`, &brvbar;, `~` and `!`

1.  **Simple comparison operators:** False expressions evaluate as 0 and
    true as -1 in twos complement form, meaning all 1's in binary form.
    See the list of limitations below for the operators excluded by our
    "simple" qualifier.

1.  **A few 2-character operators:** `++`, `--` (postfix only) and `==`.

1.  **Limited library:** See `libc.h` for allowed libc functions, of
    which there are currently 31, including:

    1.  **A subset of stdio:**

        *   `fopen` is implemented as
        
                void fopen(char *filename, char *mode)
                
            The filename must be upper case. Mode is either "w" or "r".

        *   Only 1 input file and 1 output may be open at any one time

        *   `fclose()` only closes the output file.

        *   Call `fopen` to open a new input file. The current file does
            not need to be closed.

        *   `fprintf`, `fputc`, and `fputs` are as expected.

        *   `fgets` is implemented. It will read and retain CR/LF. It
            returns a null string on EOF.

        *   `fscanf` is not implemented. Read a line with `fgets()` and
            then call `sscanf` on it.

        *   `feof` is not implemented; `fgetc` and `fgets` will return a
            null on EOF.

    1.  **printf:**  See `libc.c` for the allowed format specifiers:
        `%d`, `%s` etc.  Length and width.precision formatting is supported.

    There are many limitations in this library relative to Standard C or
    even K&R C, which are documented below.

1.  **Limited structuring constructs:** `if`, `while`, `for`, etc. are
    supported, but they may not work as expected when deeply nested or
    in long `if/else if/...` chains.


<a id="limitations"></a>
### Known Limitations of the OS/8 CC8 Compiler

The OS/8 compiler has these known limitations relative to [those of the
cross-compiler](#cross-fl):

1.  The language is typeless in that everything is a 12 bit integer and
    any variable/array can interpreted as `int`, `char` or pointer.  All
    variables and arrays must be declared as `int`.  The return type may
    be left off of a function's definition; it is implicitly `int` in
    all cases, since `void` is not supported.

2.  There must be an `int main()` which must be the last function in the
    single input C file.

3.  We do not yet support separate compilation of multiple C modules
    that get linked together.  You can produce relocatable libraries in
    OS/8 `*.RL` format and link them with the OS/8 LOADER, but because
    of the previous limitation, only one of these can be written in C.

4.  Unlike the CC8 cross-compiler, the OS/8 compiler ignores all C
    preprocessor directives: `#define`, `#ifdef`, `#include`, etc. This
    even includes inline assembly via `#asm`!
    
    One day, we may add a preprocessor called by the `CC.SV` driver
    program, but not today.
    
    This means you cannot use `#include` directives to string multiple C
    modules into a single program.

    If that then makes you wonder how the OS/8 compiler looks up the
    stock library functions defined in `libc.h` — note that I've
    resisted using the word "standard" here, for they are anything but
    that in the Standard C sense — it is that the entry point mappings
    declared in `libc.h` are hard-coded into the `CC2` compiler stage,
    implemented in `p8.c`.

    Similarly, the program initialization code defined in `init.h` is
    inserted into the program directly by the compiler rather than being
    pulled in via the preprocessor.

    Both of these header files must be included when building with the
    cross-compiler. The examples have these `#include` statements
    stripped out as they are copied to the OS/8 RK05 disk during the
    build process. This is done by `bin/cc8-to-os8`, a tool you may find
    use for yourself if you use both compilers on a single source
    program.

    If you have a program that is compiled using both the cross-compiler
    and the OS/8 compiler, you may wish to use `#include` statements,
    since the cross-compiler does process them.

5.  Variables are implicitly `static`, even when local.

6.  Arrays may only be single indexed. See `PS.CC` for an example.

7.  The compiler does not yet understand how to assign a variable's
    initial value as part of its declaration. This:

        int i = 5;

    must instead be:

        int i;
        i = 5;

8.  There is no `&&` nor &brvbar;&brvbar;.  Neither is there support for
    complex relational operators like `>=` nor even `!=`.  Abandon all
    hope for complex assignment operators like `+=`.

    Most of this can be worked around through clever coding. For
    example, this:

        if (i != 0 || j == 5)

    could be rewritten to avoid both missing operators as:

        if (!(i == 0) | (j == 5))

    because a true result in each subexpression yields -1 per the
    previous point, which when bitwise OR'd together means you get -1 if
    either subexpression is true, which means the whole expression
    evaluates to true if either subexpression is true.

    If the code you were going to write was instead:

        if (i != 0 || j != 5)

    then the rewrite is even simpler owing to the rules of [Boolean
    algebra](https://en.wikipedia.org/wiki/Boolean_algebra):

        if (!(i == 0 & j == 5))

    These rules mean that if we negate the entire expression, we get the
    same truth table if we flip the operators around and swap the
    logical test from OR to AND, which in this case converts the
    expression to a form that is now legal in our limited C dialect. All
    of this comes from the Laws section of the linked Wikipedia article;
    if you learn nothing else about Boolean algebra, you would be well
    served to memorize those rules.

9.  `atoi` is non-standard: `int atoi(char *, int *)`, returning
     the length of the numeric string.

10. `scanf` is not implemented; use `gets` then `sscanf`

11. Dereferencing parenthesized expressions does not work: `*(<expr>)`

12. The stack, which includes all globals and literals, is only 4 kwords.
    Stack overflow is not detected.  Literals are inlcuded in this due
    to a limitation in the way `COMMN` is implemented in SABR.

13. There is no argument list checking, not even for standard library
    functions.

14. `do/while` loops are parsed, but the code is not properly generated.
    Regular `while` loops work fine, however.

15. `switch` doesn't work.


<a id="bugs"></a>
### Known Bugs in the OS/8 CC8 Compiler

1.  Binary file I/O is not always reliable.  You are strongly encouraged
    to limit I/O to text files.

2.  Don’t forget to handle form feed.  See `c8.c`.

3.  For some obscure reason, always open the input file first, then the
    output file. I suspect a fault in `libc.c`, which you are welcome to
    fix, keeping in mind that we're using every trick in the book to fit
    as much functionality in as we currently do.  It may not be possible
    to make this as reliable as modern C programmers expect.


## Conclusion

This is a somewhat limited manual which attempts to give an outline of a
very simple compiler for which I apologise as the source code is obscure
and badly commented. However, the native OS/8 compiler/tokeniser
(`n8.c`) is only 600 lines which is a nothing in the scale of things
these days.  However, I hope this project gives some insight into
compiler design and code generation strategies to target a most
remarkable computer. I would also like to give credit to the builders of
OS/8 and in particular the FORTRAN II system which was never designed to
survive the onslaught of this kind of modern software.

Don’t expect too much! This compiler will not build this week’s bleeding
edge kernel. But, it may be used to build any number of useful utility
programs for OS/8.


## License

This document is under the [GNU GPLv3 License][gpl], copyright © May,
June, and November 2017 by [Ian Schofield][ian], with assorted updates
by [Warren Young][wy] in 2017.

[gpl]: https://www.gnu.org/licenses/gpl.html
[ian]: mailto:Isysxp@gmail.com
[wy]:  https://tangentsoft.com/
Added src/cc8/cross/README.md.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This directory contains the sources for the CC8 cross-compiler, which is
based on Ron Cain's Small-C system.

It is built by the top-level build system as `bin/cc8` and is installed
to `$prefix/bin/cc8`.

Call it as:

    cc8 myfile.c

The compiler does not have any consequential command line options.

The output file is `myfile.s` which is in SABR assembly code, intended
to be assembled within the PiDP-8/I OS/8 environment. See the `test`
subdirectory and [the top-level README][/doc/trunk/cc8/README.md] for
further details.
Added src/cc8/cross/code8.c.






































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * This file is part of the CC8 cross-compiler.
 *
 * The CC8 cross-compiler is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The CC8 cross-compiler is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the CC8 cross-compiler as ../GPL3.txt.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#define unix

#include <stdio.h>
#include "defs.h"
#include "data.h"
#include <string.h>

/*	Define ASNM and LDNM to the names of the assembler and linker
	respectively */

/*
 *	Some predefinitions:
 *
 *	INTSIZE is the size of an integer in the target machine
 *	BYTEOFF is the offset of an byte within an integer on the
 *		target machine. (ie: 8080,pdp11 = 0, 6809 = 1,
 *		360 = 3)
 *	This compiler assumes that an integer is the SAME length as
 *	a pointer - in fact, the compiler uses INTSIZE for both.
 */
#define	INTSIZE	1
#define	BYTEOFF	0

/*
 *	print all assembler info before any code is generated
 *
 */
header ()
{
	outstr ("/	Small C PDP8 Coder (1.0:27/1/99)");
	nl();
	FEvers();
	nl ();
	ol ("OPDEF ANDI 0400");
	ol ("OPDEF TADI 1400");
	ol ("OPDEF ISZI 2400");
	ol ("OPDEF DCAI 3400");
	ol ("OPDEF JMSI 4400");
	ol ("OPDEF JMPI 5400");
	ol ("OPDEF MQL 7421");
	ol ("OPDEF MQA 7701");
	ol ("OPDEF MQO 7501");
	ol ("OPDEF SWP 7521");
	ol ("OPDEF CDF1 6211");
	ol ("OPDEF CDF0 6201");
	ol ("OPDEF RIF 6224");
	ol ("OPDEF CAF0 6203");
	ol ("OPDEF BSW 7002");
	ol ("OPDEF CAM 7621");
	ol ("/");
    return 0;
}

nl ()
{
	outbyte (EOL);
/*	outbyte (10); */
    return 0;
}
initmac()
{
	defmac("cpm\t1");
	defmac("I8080\t1");
	defmac("RMAC\t1");
	defmac("smallc\t1");
    return 0;
}

galign(t)
int	t;
{
	return(t);
}

/*
 *	return size of an integer
 */
intsize() {
	return(INTSIZE);
}

/*
 *	return offset of ls byte within word
 *	(ie: 8080 & pdp11 is 0, 6809 is 1, 360 is 3)
 */
byteoff() {
	return(BYTEOFF);
}

/*
 *	Output internal generated label prefix
 */
olprfix() {
	ot("CC");
    return 0;
}

/*
 *	Output a label definition terminator
 */
col ()
{
	outbyte (',');
    return 0;
}

/*
 *	begin a comment line for the assembler
 *
 */
comment ()
{
	outbyte ('/');
    return 0;
}

/*
 *	Emit user label prefix
 */
prefix ()
{
    return 0;
}

/* Stkbase output stack base->literals =stkp+2 ... ie 202(8) =130(10) + sizeof(globals) */
stkbase()
{
	ot("GBL");
    return 0;
}

/*
 *	print any assembler stuff needed after all code
 *
 */
trailer ()
{
//	ot("\tENTRY ");
//	outbyte('M');
//	printlabel (litlab);
//	nl();
	outbyte('M');
	printlabel (litlab);
	col();
	ot("\t0");
	nl();
	ol("\tCDF1");
	ot("\tTAD L");
	printlabel (litlab);
	nl();
	ol ("\tSNA CLA    / Any literals to push?");
	ot ("\tJMP I M");
	printlabel (litlab);
	nl();
	ot("\tTAD X");
	printlabel (litlab);
	nl();
	ol ("\tDCA JLC");
	outbyte('D');
	printlabel (litlab);
	col();
	ol("CDF0");
	ot("\tTADI JLC");
	nl();
	ol ("\tJMSI PSH");
	ol ("\tCLA");
	ol ("\tISZ JLC");
	ot("\tISZ L");
	printlabel (litlab);
	nl();
	ot("\tJMP D");
	printlabel (litlab);
	nl();
	ot ("\tJMP I M");
	printlabel (litlab);
	nl();
	ol("CCEND,\t0");
	ol ("END");
    return 0;
}


/*
 *	function prologue
 */
prologue (sym)
char *sym;
{
    return 0;
}

/*
 *	text (code) segment
 */
gtext ()
{
/*	ol ("cseg"); */
    return 0;
}

/*
 *	data segment
 */
gdata ()
{
/*	ol ("dseg"); */
    return 0;
}

/*
 *  Output the variable symbol at scptr as an extrn or a public
 */
ppubext(scptr) char *scptr; {
	if (scptr[STORAGE] == STATIC) return 0;
//	ot (scptr[STORAGE] == EXTERN ? "extrn\t" : "public\t");
//	prefix ();
//	outstr (scptr);
//	nl();
    return 0;
}

/*
 * Output the function symbol at scptr as an extrn or a public
 */
fpubext(scptr) char *scptr; {
/*	if (scptr[STORAGE] == STATIC) return;
//	ot (scptr[OFFSET] == FUNCTION ? "public\t" : "extrn\t");
//	prefix ();
//	outstr (scptr);
//	nl (); */
    return 0;
}

/*
 *  Output a decimal number to the assembler file
 */
onum(num) int num; {
	outdec(num);	/* pdp11 needs a "." here */
    return 0;
}


/*
 *	fetch a static memory cell into the primary register
getmem (sym)
char	*sym;
{
	int adr;
		ol ("\tCLA");
		immd3 ();
		adr=glint(sym)+128;
		onum(glint(sym)+128);
		nl();
		ol("\tDCA JLC");
		ol("\tTADI JLC");
}*/

getmem (sym)
char	*sym;
{
	int adr;
	ol ("\tCLA");
	immd4 ();
	adr=glint(sym)+128;
	onum(glint(sym)+128);
    nl();
    return 0;
}
/*
 *	fetch a static memory cell into the primary register (pre-increment*/

getincmem (sym)
char	*sym;
{
	int adr;
	ol ("\tCLA");
	adr=glint(sym)+128;
	ot ("\tISZI (");
	onum(adr);
	nl();
	immd4 ();
	onum(adr);
	nl();
    return 0;
}


/*
 *	fetch the address of the specified symbol into the primary register
 *
 */
getloc (sym)
char	*sym;
{
	ol("\tCLA");
	ol("\tTAD STKP");
	if (sym[STORAGE] == LSTATIC) {
		immd3 ();
		printlabel(-1-glint(sym));
		nl();
	} else {
		if (stkp-glint(sym)==0) outstr("/");
		immd3 ();
		outdec (stkp-glint(sym));
		nl ();
	}
    return 0;
}

/*
 *	store the primary register into the specified static memory cell
 *

putmem (sym)
char	*sym;
{
		ol("\tMQL");
		immd3 ();
		onum(glint(sym)+128);
		nl();
		ol("\tDCA JLC");
		ol("\tMQA");
		ol("\tDCAI JLC");
		ol("\tTADI JLC");
}
*/

putmem (sym)
char	*sym;
{
	ot("\tDCAI (");
	onum(glint(sym)+128);
	nl();
	immd4 ();
	onum(glint(sym)+128);
	nl();
    return 0;
}

/*
 *	store the specified object type in the primary register
 *	at the address on the top of the stack
 *
 */
putstk (typeobj)
char	typeobj;
{
	ol("\tJMSI PTSK");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	fetch the specified object type indirect through the primary
 *	register into the primary register
 *
 */
indirect (typeobj)
char	typeobj;
{
	ol("\tDCA JLC");
/*	ol("\tCDF1"); */
	ol("\tTADI JLC");
    return 0;
}

/*
 *	fetch the specified object type indirect through the primary
 *	register into the primary register (pre-increment)
 *
 */
incdirect (typeobj)
char	typeobj;
{
	ol("\tDCA JLC");
	ol("\tISZI JLC");
	ol("\tTADI JLC");
    return 0;
}


/*
 *	swap the primary and secondary registers
 *
 */
swap ()
{
	ol ("\tSWP");
    return 0;
}
/*
*	Clear primary reg
*/
cpri()
{
	ol("\tCLA");
    return 0;
}
/*
 *	print partial instruction to get an immediate value into
 *	the primary register
 *
 */
immed ()
{
	ol ("\tCLA");
	ot ("\tTAD (");
    return 0;
}
immd2 ()
{
	ol ("\tCLA");
	ot ("\tTAD ");
    return 0;
}
immd3 ()
{
	ot ("\tTAD (");
    return 0;
}

immd4 ()
{
	ot("\tTADI (");
    return 0;
}
/*
 *	push the primary register onto the stack
 *
 */
gpush ()
{
	ol ("\tJMSI PSH");
	stkp = stkp - INTSIZE;
    return 0;
}

/*
 *	pop the top of the stack into the secondary register
 *
 */
gpop ()
{
	ol ("\tJMSI POP");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	swap the primary register and the top of the stack
 *
 */
swapstk ()
{
	ol ("\tMQL");
	gpop();
	ol ("\tSWP");
	gpush();
	ol ("\tSWP");
    return 0;
}

/*
 *	call the specified subroutine name
 *	varag is allowed for libc functions using a v prefix. In this case, the arg count+1 is pushed onto the stack as well.
 *  For the actual routine, the declaration should be a single arg eg printf(int args) in this case, the value of args is the count and &args-args point to the first arg in the caller's list.
 */
gcall (sname,nargs)
char	*sname;
int		*nargs;
{
	char tm[10];

	if (strstr(sname,"vlibc")) {
	immed();
	sname++;
	outdec(*nargs);
	outstr("\t/ PUSH ARG COUNT");
	nl();
	ol("\tJMSI PSH");
	stkp = stkp - INTSIZE;
	(*nargs)++;
	}
	if (strstr(sname,"libc"))
	{
		strcpy(tm,sname);
		immed();
		outstr(tm+4);
		nl();
		ol("\tMQL");
		ol("\tCALL 1,LIBC");
		ol("\tARG STKP");
		ol("\tCDF1");		/* Make sure DF is correct */
		return 0;
	}
	ol("\tCPAGE 2");
	ol("\tJMSI PCAL");
	ot ("\t");
	outstr (sname);
	nl ();
    return 0;
}

stri()
{
	ol("\tDCAI 10");
    return 0;
}
iinit()
{
	ol("\tCIA;CMA");
	ol("\tDCA 10");
    return 0;
}

/*
 *	return from subroutine
 *
 */
gret (sym)
char *sym;
{
	ol ("\tJMPI POPR");
    return 0;
}

/*
 *	perform subroutine call to value on top of stack
 *
 */
callstk ()
{
	immed ();
	outstr ("$+5");
	nl ();
	swapstk ();
	ol ("pchl");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	jump to specified internal label number
 *
 */
jump (label)
int	label;
{
	ot ("\tJMP\t");
	printlabel (label);
	nl ();
    return 0;
}

/*
 *	test the primary register and jump if false to label
 *
 */
testjump (label, ft)
int	label,
	ft;
{
	if (ft)
		ol ("\tSZA");
	else
		ol ("\tSNA");
	jump (label);
    return 0;
}

casejump()
{
	ol("\tTAD TMP");
	ol("\tSNA CLA");
    return 0;
}
/*
 *	print pseudo-op  to define a byte
 *
 */
defbyte ()
{
	ot ("\t");
    return 0;
}

/*
 *	print pseudo-op to define storage
 *
 */
defstorage ()
{
	ot ("COMMN\t");
    return 0;
}

/*
 *	print pseudo-op to define a word
 *
 */
defword ()
{
	ot ("\t");
    return 0;
}

/*
 *	modify the stack pointer to the new value indicated
 *
 */
modstk (newstkp)
int	newstkp;
{
	int	k;

	k = galign(stkp-newstkp);
	if (k == 0)
		return (newstkp);
	if (k>0 && k<5) {
		while (k--) ol ("\tISZ STKP");
		return (newstkp);
	}
	ol ("\tMQL");
	immd3 ();
	outdec (k);
	nl ();
	ol ("\tTAD STKP");
	ol ("\tDCA STKP");
	swap ();
	return (newstkp);
}

/*
 *	multiply the primary register by INTSIZE
 */
gaslint ()
{
    return 0;
}

/*
 *	divide the primary register by INTSIZE
 */
gasrint()
{
    return 0;
}

/*
 *	Case jump instruction
 */
gjcase() {
	ol ("\tCIA");
	ol ("\tDCA TMP");
    return 0;
}

/*
 *	add the primary and secondary registers
 *	if lval2 is int pointer and lval is not, scale lval
 */
gadd (lval,lval2) int *lval,*lval2;
{
/*	if (lval==0) ol("\tCIA");*/
	ol("\tDCA JLC");
	ol("\tJMSI POP");
	ol("\tMQA");
	ol("\tTAD JLC");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	subtract the primary register from the secondary
 *
 */
gsub ()
{
	ol("\tCIA");
	ol("\tDCA JLC");
	ol("\tJMSI POP");
	ol("\tMQA");
	ol("\tTAD JLC");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	multiply the primary and secondary registers
 *	(result in primary)
 *
 */
gmult ()
{
	ol("\tDCA JLC");
	ol("\tJMSI POP");
	ol("\tMQA");
	ol("\tCALL 1,MPY");
	ol("\tARG JLC");
	ol("\tCDF1");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	divide the secondary register by the primary
 *	(quotient in primary, remainder in secondary)
 *
 */
gdiv ()
{
	ol("\tDCA JLC");
	ol("\tJMSI POP");
	ol("\tMQA");
	ol("\tCALL 1,DIV");
	ol("\tARG JLC");
	ol("\tCDF1");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	compute the remainder (mod) of the secondary register
 *	divided by the primary register
 *	(remainder in primary, quotient in secondary)
 *
 */
gmod ()
{
	ol("\tDCA JLC");
	ol("\tJMSI POP");
	ol("\tMQA");
	ol("\tCALL 1,DIV");
	ol("\tARG JLC");
	ol("\tCALL 1,IREM");
	ol("\tARG 0");
	ol("\tCDF1");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	inclusive 'or' the primary and secondary registers
 *
 */
gor ()
{
	ol("\tJMSI POP");
	ol("\tMQA");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	exclusive 'or' the primary and secondary registers
 *
 */
gxor ()
{
	gpop();
	gcall ("?xor");
    return 0;
}

/*
 *	'and' the primary and secondary registers
 *
 */
gand ()
{
	ol("\tDCA JLC");
	ol("\tJMSI POP");
	ol("\tMQA");
	ol("\tAND JLC");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	arithmetic shift right the secondary register the number of
 *	times in the primary register
 *	(results in primary register)
 *
 */
gasr ()
{
	int lbl;

	lbl=getlabel();
	ol("\tCIA");
	ol("\tJMSI POP");
	gnlabel(lbl);
	ol("\tSWP");
	ol("\tCLL RAR");
	ol("\tSWP");
	ol("\tIAC");
	ol("\tSZA");
	jump(lbl);
	ol("\tSWP");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	arithmetic shift left the secondary register the number of
 *	times in the primary register
 *	(results in primary register)
 *
 */
gasl ()
{
	int lbl;

	lbl=getlabel();
	ol("\tCIA");
	ol("\tJMSI POP");
	gnlabel(lbl);
	ol("\tSWP");
	ol("\tCLL RAL");
	ol("\tSWP");
	ol("\tIAC");
	ol("\tSZA");
	jump(lbl);
	ol("\tSWP");
	stkp = stkp + INTSIZE;
    return 0;
}

/*
 *	two's complement of primary register
 *
 */
gneg ()
{
	ol("\tCIA");
    return 0;
}

/*
 *	logical complement of primary register
 *
 */
glneg ()
{
	ol("\tSNA CLA");
	ol("\tCMA");
    return 0;
}

/*
 *	one's complement of primary register
 *
 */
gcom ()
{
	ol("\tCMA");
    return 0;
}

/*
 *	Convert primary value into logical value (0 if 0, 1 otherwise)
 *
 */
gbool ()
{
	ol("\tSZA CLA");
	ol("\tIAC");
    return 0;
}

/*
 *	increment the primary register by 1 if char, INTSIZE if
 *      int
 */
ginc (lval) int lval[];
{
	ol ("\tIAC");
/*	if (lval[2] == CINT)
//		ol ("inx\th"); */
    return 0;
}
/*
 * Shortened INC
*/

gisz (lval)
int *lval;
{
	int adr;
	char *sym=lval[0];

	if (lval[1]) {
		ol ("\tISZI JLC");
		return 0;
	}

	ot ("\tISZI (");
	adr=stkp-glint(sym);
//	if (lval[STORAGE] == PUBLIC)
		adr=glint(sym)+128;
	onum(adr);
	nl();
    return 0;
}
/*
 *	decrement the primary register by one if char, INTSIZE if
 *	int
 */
gdec (lval) int lval[];
{
	ol ("\tTAD (-1");
/*	if (lval[2] == CINT)
//		ol("dcx\th"); */
    return 0;
}

/*
 *	following are the conditional operators.
 *	they compare the secondary register against the primary register
 *	and put a literl 1 in the primary if the condition is true,
 *	otherwise they clear the primary register
 *
 */

/*
 *	equal
 *
 */
geq ()
{
	ol("\tCIA");
	ol("\tTADI STKP");
	gpop();
	ol("\tSNA CLA");
	ol("\tCMA");
    return 0;
}

/*
 *	not equal
 *
 */
gne ()
{
	gpop();
	ol("\tCIA");
	ol("\tDCA JLC");
	ol("\tMQA");
	ol("\tTAD JLC");
    return 0;
}

/*
 *	less than (signed)
 *
 */
glt ()
{
	gpop();
	ol("\tCIA");
	ol("\tDCA JLC");
	ol("\tMQA");
	ol("\tTAD JLC");
	ol("\tAND (2048");
    return 0;
}

/*
 *	less than or equal (signed)
 *
 */
gle ()
{
	gpop();
	ol("\tCIA");
	ol("\tDCA JLC");
	ol("\tMQA");
	ol("\tTAD JLC");
	ol("\tSNA");
	ol("\tCLA CMA");
	ol("\tAND (2048");
    return 0;
}

/*
 *	greater than (signed)
 *
 */
ggt ()
{
	gpop();
	ol("\tSWP");
	ol("\tCIA");
	ol("\tDCA JLC");
	ol("\tMQA");
	ol("\tTAD JLC");
	ol("\tAND (2048");
    return 0;
}

/*
 *	greater than or equal (signed)
 *
 */
gge ()
{
	gpop();
	ol("\tSWP");
	ol("\tCIA");
	ol("\tDCA JLC");
	ol("\tMQA");
	ol("\tTAD JLC");
	ol("\tSNA");
	ol("\tCLA CMA");
	ol("\tAND (2048");
    return 0;
}

/*
 *	less than (unsigned)
 *
 */
gult ()
{
	gpop();
	ol("\tCLL CIA");
	ol("\tDCA JLC");
	ol("\tMQA");
	ol("\tTAD JLC");
	ol("\tSNL CLA");
	ol("\tIAC");
    return 0;
}

/*
 *	less than or equal (unsigned)
 *
 */
gule ()
{
	gpop();
	ol("\tCLL CIA");
	ol("\tDCA JLC");
	ol("\tMQA");
	ol("\tTAD JLC");
	ol("\tSNL CLA");
	ol("\tIAC");
    return 0;
}

/*
 *	greater than (unsigned)
 *
 */
gugt ()
{
	gpop();
	ol("\tCLL CIA");
	ol("\tDCA JLC");
	ol("\tMQA");
	ol("\tTAD JLC");
	ol("\tSNA SZL CLA");
	ol("\tIAC");
    return 0;
}

/*
 *	greater than or equal (unsigned)
 *
 */
guge ()
{
	gpop();
	ol("\tSWP");
	ol("\tCLL CIA");
	ol("\tDCA JLC");
	ol("\tMQA");
	ol("\tTAD JLC");
	ol("\tSNL CLA");
	ol("\tIAC");
    return 0;
}

/*	Squirrel away argument count in a register that modstk
	doesn't touch.
*/

gnargs(d)
int	d; {
/*	ot ("mvi\ta,");
//	onum(d);
//	nl (); */
    return 0;
}

assemble(s)
char	*s; {
#ifdef	ASNM
	char buf[100];
	strcpy(buf, ASNM);
	strcat(buf, " ");
	strcat(buf, s);
	buf[strlen(buf)-1] = 's';
	return(system(buf));
#else
	return(0);
#endif
}
Added src/cc8/cross/ctype.h.

1
+
/*	Nothing needed in this file */
Added src/cc8/cross/data.c.























































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File data.c: 2.2 (84/11/27,16:26:13) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"

/* storage words */

char	symtab[SYMTBSZ];
char	*glbptr, *rglbptr, *locptr;
int	ws[WSTABSZ];
int	*wsptr;
int	swstcase[SWSTSZ];
int	swstlab[SWSTSZ];
int	swstp;
char	litq[LITABSZ];
int	litptr;
char	macq[MACQSIZE];
int	macptr;
char	line[LINESIZE];
char	mline[LINESIZE];
int	lptr, mptr, gsize;

/* miscellaneous storage */

int	nxtlab,
	litlab,
	stkp,
	argstk,
	ncmp,
	errcnt,
	glbflag,
	ctext,
	cmode,
	lastst,
	inbreak;

FILE	*input, *input2, *output;
FILE	*inclstk[INCLSIZ];
int	inclsp;
char	fname[NAMESIZE];
FILE *bfile;

char	quote[2];
unsigned char	*cptr;
int	*iptr;
int	fexitlab;
int	iflevel, skiplevel;
int	errfile;
int	sflag;
int	cflag;
int	errs;
int	aflag;
Added src/cc8/cross/data.h.
















































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File data.h: 2.2 (84/11/27,16:26:11) */

/* storage words */

extern	char	symtab[];
extern	char	*glbptr, *rglbptr, *locptr;
extern	int	ws[];
extern	int	*wsptr;
extern	int	swstcase[];
extern	int	swstlab[];
extern	int	swstp;
extern	char	litq[];
extern	int	litptr;
extern	char	macq[];
extern	int	macptr;
extern	char	line[];
extern	char	mline[];
extern	int	lptr, mptr, gsize;

/* miscellaneous storage */

extern	int	nxtlab,
		litlab,
		stkp,
		argstk,
		ncmp,
		errcnt,
		glbflag,
		ctext,
		cmode,
		lastst,
		inbreak;

extern	FILE	*input, *input2, *output, *bfile;
extern	FILE	*inclstk[];
extern	int	inclsp;
extern	char	fname[];

extern	char	quote[];
extern	char	*cptr;
extern	int	*iptr;
extern	int	fexitlab;
extern	int	iflevel, skiplevel;
extern	int	errfile;
extern	int	sflag;
extern	int	cflag;
extern	int	errs;
extern	int	aflag;
Added src/cc8/cross/defs.h.




























































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File defs.h: 2.1 (83/03/21,02:07:20) */


#define	FOREVER	for(;;)
#define	FALSE	0
#define	TRUE	1
#define	NO	0
#define	YES	1

/* miscellaneous */

#define	EOS	0
#define	EOL	10
#define	BKSP	8
#define	CR	13
#define	FFEED	12
#define TAB	9

/* symbol table parameters */

#define	SYMSIZ	14
#define	SYMTBSZ	2800
#define	NUMGLBS	150
#define	STARTGLB	symtab
#define	ENDGLB	(STARTGLB+NUMGLBS*SYMSIZ)
#define	STARTLOC	(ENDGLB+SYMSIZ)
#define	ENDLOC	(symtab+SYMTBSZ-SYMSIZ)

/* symbol table entry format */

#define	NAME	0
#define	IDENT	9
#define	TYPE	10
#define	STORAGE	11
#define	OFFSET	12

/* system-wide name size (for symbols) */

#define	NAMESIZE	20
#define	NAMEMAX	20

/* possible entries for "ident" */

#define	VARIABLE	1
#define	ARRAY	2
#define	POINTER	3
#define	FUNCTION	4

/* possible entries for "type" */

#define	CCHAR	1
#define	CINT	2

/* possible entries for storage */

#define	PUBLIC	1
#define	AUTO	2
#define	EXTERN	3

#define	STATIC	4
#define	LSTATIC	5
#define	DEFAUTO	6
/* "do"/"for"/"while"/"switch" statement stack */

#define	WSTABSZ	100
#define	WSSIZ	7
#define	WSMAX	ws+WSTABSZ-WSSIZ

/* entry offsets in "do"/"for"/"while"/"switch" stack */

#define	WSSYM	0
#define	WSSP	1
#define	WSTYP	2
#define	WSCASEP	3
#define	WSTEST	3
#define	WSINCR	4
#define	WSDEF	4
#define	WSBODY	5
#define	WSTAB	5
#define	WSEXIT	6

/* possible entries for "wstyp" */

#define	WSWHILE	0
#define	WSFOR	1
#define	WSDO	2
#define	WSSWITCH	3

/* "switch" label stack */

#define	SWSTSZ	100

/* literal pool */

#define	LITABSZ	2000
#define	LITMAX	LITABSZ-1

/* input line */

#define	LINESIZE	200
#define	LINEMAX	(LINESIZE-1)
#define	MPMAX	LINEMAX

/* macro (define) pool */

#define	MACQSIZE	1000
#define	MACMAX	(MACQSIZE-1)

/* "include" stack */

#define	INCLSIZ	3

/* statement types (tokens) */

#define	STIF	1
#define	STWHILE	2
#define	STRETURN	3
#define	STBREAK	4
#define	STCONT	5
#define	STASM	6
#define	STEXP	7
#define	STDO	8
#define	STFOR	9
#define	STSWITCH	10
Added src/cc8/cross/error.c.
















































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File error.c: 2.1 (83/03/20,16:02:00) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

error (ptr)
char	ptr[];
{
	FILE *tempfile;

	tempfile = output;
	output = stdout;
	doerror(ptr);
	output = tempfile;
	doerror(ptr);
	errcnt++;
    return 0;
}

doerror(ptr)
char *ptr;
{
	int k;
	comment ();
	outstr (line);
	nl ();
	comment ();
	k = 0;
	while (k < lptr) {
		if (line[k] == 9)
			tab ();
		else
			outbyte (' ');
		k++;
	}
	outbyte ('^');
	nl ();
	comment ();
	outstr ("******  ");
	outstr (ptr);
	outstr ("  ******");
	nl ();
    return 0;
}
Added src/cc8/cross/expr.c.








































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File expr.c: 2.2 (83/06/21,11:24:26) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

/*
 *	lval[0] - symbol table address, else 0 for constant
 *	lval[1] - type indirect object to fetch, else 0 for static object
 *	lval[2] - type pointer or array, else 0
 */

expression (comma)
int	comma;
{
	int	lval[3];

	do {
		if (heir1 (lval))
			rvalue (lval);
		if (!comma)
			return 0;
	} while (match (","));
    return 0;
}

heir1 (lval)
int	lval[];
{
	int	k, lval2[3];
	char	fc;

	k = heir1a (lval);
	if (match ("=")) {
		if (k == 0) {
			needlval ();
			return (0);
		}
		if (lval[1])
			gpush ();
		if (heir1 (lval2))
			rvalue (lval2);
		store (lval);
		return (0);
	} else
	{	
		fc = ch();
		if  (match ("-=") ||
		    match ("+=") ||
		    match ("*=") ||
		    match ("/=") ||
		    match ("%=") ||
		    match (">>=") ||
		    match ("<<=") ||
		    match ("&=") ||
		    match ("^=") ||
		    match ("|=")) {
			if (k == 0) {
				needlval ();
				return (0);
			}
			if (lval[1])
				gpush ();
			rvalue (lval);
			gpush ();
			if (heir1 (lval2))
				rvalue (lval2);
			switch (fc) {
				case '-':	{
					if (dbltest(lval,lval2))
						gaslint();
					gsub();
					result (lval, lval2);
					break;
				}
				case '+':	{
					if (dbltest(lval,lval2))
						gaslint();
					gadd (lval,lval2);
					result(lval,lval2);
					break;
				}
				case '*':	gmult (); break;
				case '/':	gdiv (); break;
				case '%':	gmod (); break;
				case '>':	gasr (); break;
				case '<':	gasl (); break;
				case '&':	gand (); break;
				case '^':	gxor (); break;
				case '|':	gor (); break;
			}
			store (lval);
			return (0);
		} else
			return (k);
	}
}

heir1a (lval)
int	lval[];
{
	int	k, lval2[3], lab1, lab2;

	k = heir1b (lval);
	blanks ();
	if (ch () != '?')
		return (k);
	if (k)
		rvalue (lval);
	FOREVER
		if (match ("?")) {
			testjump (lab1 = getlabel (), FALSE);
			if (heir1b (lval2))
				rvalue (lval2);
			jump (lab2 = getlabel ());
			printlabel (lab1);
			col ();
			nl ();
			blanks ();
			if (!match (":")) {
				error ("missing colon");
				return (0);
			}
			if (heir1b (lval2))
				rvalue (lval2);
			printlabel (lab2);
			col ();
			nl ();
		} else
			return (0);
}

heir1b (lval)
int	lval[];
{
	int	k, lval2[3], lab;

	k = heir1c (lval);
	blanks ();
	if (!sstreq ("||"))
		return (k);
	if (k)
		rvalue (lval);
	FOREVER
		if (match ("||")) {
			testjump (lab = getlabel (), TRUE);
			if (heir1c (lval2))
				rvalue (lval2);
			printlabel (lab);
			col ();
			nl ();
			gbool();
		} else
			return (0);
}

heir1c (lval)
int	lval[];
{
	int	k, lval2[3], lab;

	k = heir2 (lval);
	blanks ();
	if (!sstreq ("&&"))
		return (k);
	if (k)
		rvalue (lval);
	FOREVER
		if (match ("&&")) {
			testjump (lab = getlabel (), FALSE);
			if (heir2 (lval2))
				rvalue (lval2);
			printlabel (lab);
			col ();
			nl ();
			gbool();
		} else
			return (0);
}

heir2 (lval)
int	lval[];
{
	int	k, lval2[3];

	k = heir3 (lval);
	blanks ();
	if ((ch() != '|') | (nch() == '|') | (nch() == '='))
		return (k);
	if (k)
		rvalue (lval);
	FOREVER {
		if ((ch() == '|') & (nch() != '|') & (nch() != '=')) {
			inbyte ();
			gpush ();
			if (heir3 (lval2))
				rvalue (lval2);
			gor ();
			blanks();
		} else
			return (0);
	}
}

heir3 (lval)
int	lval[];
{
	int	k, lval2[3];

	k = heir4 (lval);
	blanks ();
	if ((ch () != '^') | (nch() == '='))
		return (k);
	if (k)
		rvalue (lval);
	FOREVER {
		if ((ch() == '^') & (nch() != '=')){
			inbyte ();
			gpush ();
			if (heir4 (lval2))
				rvalue (lval2);
			gxor ();
			blanks();
		} else
			return (0);
	}
}

heir4 (lval)
int	lval[];
{
	int	k, lval2[3];

	k = heir5 (lval);
	blanks ();
	if ((ch() != '&') | (nch() == '|') | (nch() == '='))
		return (k);
	if (k)
		rvalue (lval);
	FOREVER {
		if ((ch() == '&') & (nch() != '&') & (nch() != '=')) {
			inbyte ();
			gpush ();
			if (heir5 (lval2))
				rvalue (lval2);
			gand ();
			blanks();
		} else
			return (0);
	}
}

heir5 (lval)
int	lval[];
{
	int	k, lval2[3];

	k = heir6 (lval);
	blanks ();
	if (!sstreq ("==") &
	    !sstreq ("!="))
		return (k);
	if (k)
		rvalue (lval);
	FOREVER {
		if (match ("==")) {
			gpush ();
			if (heir6 (lval2))
				rvalue (lval2);
			geq ();
		} else if (match ("!=")) {
			gpush ();
			if (heir6 (lval2))
				rvalue (lval2);
			gne ();
		} else
			return (0);
	}
}

heir6 (lval)
int	lval[];
{
	int	k, lval2[3];

	k = heir7 (lval);
	blanks ();
	if (!sstreq ("<") &&
	    !sstreq ("<=") &&
	    !sstreq (">=") &&
	    !sstreq (">"))
		return (k);
	if (sstreq ("<<") || sstreq (">>"))
		return (k);
	if (k)
		rvalue (lval);
	FOREVER {
		if (match ("<=")) {
			gpush ();
			if (heir7 (lval2))
				rvalue (lval2);
			if (lval[2] || lval2[2]) {
				gule ();
				continue;
			}
			gle ();
		} else if (match (">=")) {
			gpush ();
			if (heir7 (lval2))
				rvalue (lval2);
			if (lval[2] || lval2[2]) {
				guge ();
				continue;
			}
			gge ();
		} else if ((sstreq ("<")) &&
			   !sstreq ("<<")) {
			inbyte ();
			gpush ();
			if (heir7 (lval2))
				rvalue (lval2);
			if (lval[2] || lval2[2]) {
				gult ();
				continue;
			}
			glt ();
		} else if ((sstreq (">")) &&
			   !sstreq (">>")) {
			inbyte ();
			gpush ();
			if (heir7 (lval2))
				rvalue (lval2);
			if (lval[2] || lval2[2]) {
				gugt ();
				continue;
			}
			ggt ();
		} else
			return (0);
		blanks ();
	}
}

heir7 (lval)
int	lval[];
{
	int	k, lval2[3];

	k = heir8 (lval);
	blanks ();
	if (!sstreq (">>") &&
	    !sstreq ("<<") || sstreq(">>=") || sstreq("<<="))
		return (k);
	if (k)
		rvalue (lval);
	FOREVER {
		if (sstreq(">>") && ! sstreq(">>=")) {
			inbyte(); inbyte();
			gpush ();
			if (heir8 (lval2))
				rvalue (lval2);
			gasr ();
		} else if (sstreq("<<") && ! sstreq("<<=")) {
			inbyte(); inbyte();
			gpush ();
			if (heir8 (lval2))
				rvalue (lval2);
			gasl ();
		} else
			return (0);
		blanks();
	}
}

heir8 (lval)
int	lval[];
{
	int	k, lval2[3];

	k = heir9 (lval);
	blanks ();
	if ((ch () != '+') & (ch () != '-') | nch() == '=')
		return (k);
	if (k)
		rvalue (lval);
	FOREVER {
		if (match ("+")) {
			gpush ();
			if (heir9 (lval2))
				rvalue (lval2);
			/* if left is pointer and right is int, scale right */
			if (dbltest (lval, lval2))
				gaslint ();
			/* will scale left if right int pointer and left int */
			gadd (lval,lval2);
			result (lval, lval2);
		} else if (match ("-")) {
			gpush ();
			if (heir9 (lval2))
				rvalue (lval2);
			/* if dbl, can only be: pointer - int, or
						pointer - pointer, thus,
				in first case, int is scaled up,
				in second, result is scaled down. */
			if (dbltest (lval, lval2))
				gaslint ();
			gsub ();
			/* if both pointers, scale result */
			if ((lval[2] == CINT) && (lval2[2] == CINT)) {
				gasrint(); /* divide by intsize */
			}
			result (lval, lval2);
		} else
			return (0);
	}
}

heir9 (lval)
int	lval[];
{
	int	k, lval2[3];

	k = heir10 (lval);
	blanks ();
	if (((ch () != '*') && (ch () != '/') &&
		(ch () != '%')) || (nch() == '='))
		return (k);
	if (k)
		rvalue (lval);
	FOREVER {
		if (match ("*")) {
			gpush ();
			if (heir10 (lval2))
				rvalue (lval2);
			gmult ();
		} else if (match ("/")) {
			gpush ();
			if (heir10 (lval2))
				rvalue (lval2);
			gdiv ();
		} else if (match ("%")) {
			gpush ();
			if (heir10 (lval2))
				rvalue (lval2);
			gmod ();
		} else
			return (0);
	}
}

heir10 (lval)
int	lval[];
{
	int	k;
	unsigned char	*ptr;

	if (match ("++")) {
		if ((k = heir10 (lval)) == 0) {
			needlval ();
			return (0);
		}
//		if (lval[1])
//		gpush ();
		rivalue (lval);
//		ginc (lval);
//		store (lval);
		return (0);
	} else if (match ("--")) {
		if ((k = heir10 (lval)) == 0) {
			needlval ();
			return (0);
		}
		if (lval[1])
			gpush ();
		rvalue (lval);
		gdec (lval);
		store (lval);
		return (0);
	} else if (match ("-")) {
		k = heir10 (lval);
		if (k)
			rvalue (lval);
		gneg ();
		return (0);
	} else if (match ("~")) {
		k = heir10 (lval);
		if (k)
			rvalue (lval);
		gcom ();
		return (0);
	} else if (match ("!")) {
		k = heir10 (lval);
		if (k)
			rvalue (lval);
		glneg ();
		return (0);
	} else if (ch()=='*' && nch() != '=') {
		inbyte();
		k = heir10 (lval);
		if (k)
			rvalue (lval);
		if (ptr = lval[0])
			lval[1] = ptr[TYPE];
		else
			lval[1] = CINT;
		lval[2] = 0;  /* flag as not pointer or array */
		return (1);
	} else if (ch()=='&' && nch()!='&' && nch()!='=') {
		inbyte();
		k = heir10 (lval);
		if (k == 0) {
			error ("illegal address");
			return (0);
		}
		ptr = lval[0];
		lval[2] = ptr[TYPE];
		if (lval[1])
			return (0);
		/* global and non-array */
		immed ();
		k=128+ptr[OFFSET]+ptr[OFFSET+1]*256;
		onum(k);
		ot("\t/Offset from stackbase at 128 (200(8))");
		nl ();
		lval[1] = ptr[TYPE];
		return (0);
	} else {
		k = heir11 (lval);
		if (match ("++")) {
			if (k == 0) {
				needlval ();
				return (0);
			}
//			if (lval[1])
//				gpush ();
			rvalue (lval);
			gisz (lval);
//			ginc (lval);
//			store (lval);
//			gdec (lval);
			return (0);
		} else if (match ("--")) {
			if (k == 0) {
				needlval ();
				return (0);
			}
			if (lval[1])
				gpush ();
			rvalue (lval);
			gdec (lval);
			store (lval);
			ginc (lval);
			return (0);
		} else
			return (k);
	}
}

heir11 (lval)
int	*lval;
{
	int	k;
	char	*ptr;

	k = primary (lval);
	ptr = lval[0];
	blanks ();
	if ((ch () == '[') | (ch () == '('))
		FOREVER {
			if (match ("[")) {
				if (ptr == 0) {
					error ("can't subscript");
					junk ();
					needbrack ("]");
					return (0);
				} else if (ptr[IDENT] == POINTER)
					rvalue (lval);
				else if (ptr[IDENT] != ARRAY) {
					error ("can't subscript");
					k = 0;
				}
				gpush ();
				expression (YES);
				needbrack ("]");
				if (ptr[TYPE] == CINT)
					gaslint ();
				gadd (NULL,NULL);
				lval[0] = 0;
				lval[1] = ptr[TYPE];
				k = 1;
			} else if (match ("(")) {
				if (ptr == 0)
					callfunction (0);
				else if (ptr[IDENT] != FUNCTION) {
					rvalue (lval);
					callfunction (0);
				} else
					callfunction (ptr);
				k = lval[0] = 0;
			} else
				return (k);
		}
	if (ptr == 0)
		return (k);
	if (ptr[IDENT] == FUNCTION) {
		immed ();
		prefix ();
		outstr (ptr);
		nl ();
		return (0);
	}
	return (k);
}
Added src/cc8/cross/function.c.





































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File function.c: 2.1 (83/03/20,16:02:04) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

/*
 *	begin a function
 *
 *	called from "parse", this routine tries to make a function out
 *	of what follows
 *	modified version.  p.l. woods
 *
 */
int argtop;
newfunc ()
{
	char	n[NAMESIZE], *ptr, rtn[NAMESIZE];
	fexitlab = getlabel();

	if (!symname (n) ) {
		error ("illegal function or declaration");
		kill_line ();
		return 0;
	}
	if (ptr = findglb (n)) {
		if (ptr[IDENT] != FUNCTION)
			multidef (n);
		else if (ptr[OFFSET] == FUNCTION)
			multidef (n);
		else
			ptr[OFFSET] = FUNCTION;
	} else
		addglb (n, FUNCTION, CINT, 0, PUBLIC);  // Do not allocate any storage to global functions
	if (!match ("("))
		error ("missing open paren");
	prefix ();
	if (astreq(n,"main",4)) {
		if (inbreak) {
		ol("\tEND");
		output=bfile;
		}
		outstr("xmain");
	}
	else outstr (n);
	strcpy(rtn,n);
	col ();
//	outstr("\t0");
	nl ();
	if (inbreak) {
		ol("\tCLA CLL");
		ol("\tCALL 2,PGINIT");
		ol("\tARG STKP");
		ol("\tARG GBL");
	}
	prologue (rtn);
	locptr = STARTLOC;
	argstk = 0;
	while (!match (")")) {
		if (symname (n)) {
			if (findloc (n))
				multidef (n);
			else {
				addloc (n, 0, 0, argstk, AUTO);
				argstk = argstk + intsize();
			}
		} else {
			error ("illegal argument name");
			junk ();
		}
		blanks ();
		if (!streq (line + lptr, ")")) {
			if (!match (","))
				error ("expected comma");
		}
		if (endst ())
			break;
	}
	stkp = 0;
	argtop = argstk;
	while (argstk) {
		if (amatch ("register", 8)) {
			if (amatch("char", 4)) 
				getarg(CCHAR);
			else if (amatch ("int", 3))
				getarg(CINT);
			else
				getarg(CINT);
			ns();
		} else if (amatch ("char", 4)) {
			getarg (CCHAR);
			ns ();
		} else if (amatch ("int", 3)) {
			getarg (CINT);
			ns ();
		} else {
			error ("wrong number args");
			break;
		}
	}
	statement(YES);
	printlabel(fexitlab);
	col();
	nl();
	if (astreq(n,"main",4))	/* On exit from main pop literal table as well */
		modstk(0);
	else 
		modstk (0); 
	gret (rtn);
	stkp = 0;
	locptr = STARTLOC;
    return 0;
}

/*
 *	declare argument types
 *
 *	called from "newfunc", this routine add an entry in the local
 *	symbol table for each named argument
 *	completely rewritten version.  p.l. woods
 *
 */
getarg (t)
int	t;
{
    int	j, legalname, address;
    char	n[NAMESIZE], c, *argptr;

	FOREVER {
		if (argstk == 0)
			return 0;
		if (match ("*"))
			j = POINTER;
		else
			j = VARIABLE;
		if (!(legalname = symname (n)))
			illname ();
		if (match ("[")) {
			while (inbyte () != ']')
				if (endst ())
					break;
			j = POINTER;
		}
		if (legalname) {
			if (argptr = findloc (n)) {
				argptr[IDENT] = j;
				argptr[TYPE] = t;
				address = argtop - glint(argptr);
				if (t == CCHAR && j == VARIABLE)
					address = address + byteoff();
				argptr[OFFSET] = (address) & 0xff;
				argptr[OFFSET + 1] = (address >> 8) & 0xff;
			} else
				error ("expecting argument name");
		}
		argstk = argstk - intsize();
		if (endst ())
			return 0;
		if (!match (","))
			error ("expected comma");
	}
}
Added src/cc8/cross/gen.c.











































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File gen.c: 2.1 (83/03/20,16:02:06) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

/* ToUpper routine */

	ucase(ch)
	int ch;
	{
		if ('a'>ch || ch>'z') return(ch);
		return(ch-32);
	}
/*
 *	return next available internal label number
 *
 */
getlabel ()
{
	return (nxtlab++);
}

/*
 *	print specified number as label
 */
printlabel (label)
int	label;
{
	olprfix ();
	outdec (label);
    return 0;
}

/*
 *	glabel - generate label
 */
glabel (lab)
char	*lab;
{
	prefix ();
	outstr (lab);
	col ();
	nl ();
    return 0;
}

/*
 *	gnlabel - generate numeric label
 */
gnlabel (nlab)
int	nlab;
{
	printlabel (nlab);
	col ();
	nl ();
    return 0;
}

outbyte (c)
char	c;
{
	if (c == 0)
		return (0);
	fputc (c, output);
	return (c);
}

outstr (ptr)
char	ptr[];
{
	int	k;

	k = 0;
	while (outbyte (ucase(ptr[k++])));
    return 0;
}


tab ()
{
	outbyte (9);
    return 0;
}

ol (ptr)
char	ptr[];
{
	ot (ptr);
	nl ();
    return 0;
}

ot (ptr)
char	ptr[];
{
	outstr (ptr);
    return 0;
}

outdec (number)
int	number;
{
	int	k, zs;
	char	c;

	if (number == -32768) {
		outstr ("-32768");
		return 0;
	}
	zs = 0;
	k = 10000;
	if (number < 0) {
		number = (-number);
		outbyte ('-');
	}
	while (k >= 1) {
		c = number / k + '0';
		if ((c != '0' | (k == 1) | zs)) {
			zs = 1;
			outbyte (c);
		}
		number = number % k;
		k = k / 10;
	}
    return 0;
}
		
store (lval)
int	*lval;
{
	if (lval[1] == 0)
		putmem (lval[0]);
	else
		putstk (lval[1]);
    return 0;
}

rvalue (lval)
int	*lval;
{
	if ((lval[0] != 0) & (lval[1] == 0))
		getmem (lval[0]);
	else
		indirect (lval[1]);
    return 0;
}

rivalue (lval)
int	*lval;
{
	if ((lval[0] != 0) & (lval[1] == 0))
		getincmem (lval[0]);
	else
		incdirect (lval[1]);
    return 0;
}

test (label, ft)
int	label,
	ft;
{
	needbrack ("(");
	expression (YES);
	needbrack (")");
	testjump (label, ft);
    return 0;
}
Added src/cc8/cross/io.c.































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*  File io.c: 2.1 (83/03/20,16:02:07) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

/*
 *  open input file
 */
openin (p) char *p;
{
    strcpy(fname, p);
    fixname (fname);
    if (!checkname (fname))
        return (NO);
    if ((input = fopen (fname, "r")) == NULL) {
        pl ("Open failure\n");
        return (NO);
    }
    kill_line ();
    return (YES);
}

/*
 *  open output file
 */
openout ()
{
    outfname (fname);
    if ((output = fopen (fname, "w")) == NULL) {
        pl ("Open failure");
        return (NO);
    }
    kill_line ();
    return (YES);
}

/*
 *  Change input filename to output filename.  We already checked that
 *  the input file name matches *.c, so we replace the 'c' with an 's'
 *  and then, space permitting, append a 'b'.
 *
 */
outfname (s)
char    *s;
{
    char *os = s;
    while (*s)
        s++;
    *--s = 's';

    if ((s - os) < (NAMESIZE - 2)) {
        *++s = 'b';
        *++s = '\0';
    }

    return 0;

}

/*
 *  remove NL from filenames
 *
 */
fixname (s)
char    *s;
{
    while (*s && *s++ != EOL);
    if (!*s) return 0;
    *(--s) = 0;
    return 0;
}

/*
 *  check that filename is "*.c"
 */
checkname (s)
char    *s;
{
    while (*s)
        s++;
    if (*--s != 'c')
        return (NO);
    if (*--s != '.')
        return (NO);
    return (YES);
}

kill_line ()
{
    lptr = 0;
    line[lptr] = 0;
    return 0;
}

inln ()
{
    int k;
    FILE    *unit;

    FOREVER {
        if (feof (input))
            return 0;
        if ((unit = input2) == NULL)
            unit = input;
        kill_line ();
        while ((k = fgetc (unit)) != EOF) {
            if ((k == EOL) | (lptr >= LINEMAX))
                break;
            if (k != 13) line[lptr++] = k;
        }
        line[lptr] = 0;
        if (output && cmode) {
            outstr("/\t");
            ol(line);
        }
        if (k <= 0)
            if (input2 != NULL) {
                input2 = inclstk[--inclsp];
                fclose (unit);
            }
        if (lptr) {
            if ((ctext) & (cmode)) {
                comment ();
                outstr (line);
                nl ();
            }
            lptr = 0;
            return 0;
        }
    }
}

inbyte ()
{
    while (ch () == 0) {
        if (feof (input))
            return (0);
        preprocess ();
    }
    return (gch ());
}

inchar ()
{
    if (ch () == 0)
        inln ();
    if (feof (input))
        return (0);
    return (gch ());
}

gch ()
{
    if (ch () == 0)
        return (0);
    else
        return (line[lptr++] & 127);
}

nch ()
{
    if (ch () == 0)
        return (0);
    else
        return (line[lptr + 1] & 127);
}

ch ()
{
    return (line[lptr] & 127);
}

/*
 *  print a carriage return and a string only to console
 *
 */
pl (str)
char    *str;
{
    int k;

    k = 0;
    putchar (EOL);
    while (str[k])
        putchar (str[k++]);
    return 0;
}
Added src/cc8/cross/lex.c.















































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File lex.c: 2.1 (83/03/20,16:02:09) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

/*
 *	semicolon enforcer
 *
 *	called whenever syntax requires a semicolon
 *
 */
ns ()
{
	if (!match (";"))
		error ("missing semicolon");
    return 0;
}

junk ()
{
	if (an (inbyte ()))
		while (an (ch ()))
			gch ();
	else
		while (an (ch ())) {
			if (ch () == 0)
				break;
			gch ();
		}
	blanks ();
    return 0;
}

endst ()
{
	blanks ();
	return ((streq (line + lptr, ";") | (ch () == 0)));
}

needbrack (str)
char	*str;
{
	if (!match (str)) {
		error ("missing bracket");
		comment ();
		outstr (str);
		nl ();
	}
    return 0;
}

/*
 *	test if given character is alpha
 *
 */
alpha (c)
char	c;
{
	c = c & 127;
	return (((c >= 'a') & (c <= 'z')) |
		((c >= 'A') & (c <= 'Z')) |
		(c == '_'));
}

/*
 *	test if given character is numeric
 *
 */
numeric (c)
char	c;
{
	c = c & 127;
	return ((c >= '0') & (c <= '9'));
}

/*
 *	test if given character is alphanumeric
 *
 */
an (c)
char	c;
{
	return ((alpha (c)) | (numeric (c)));
}

sstreq (str1) char *str1; {
	return (streq(line + lptr, str1));
}

streq (str1, str2)
char	str1[], str2[];
{
	int	k;

	k = 0;
	while (str2[k]) {
		if ((str1[k] != str2[k]))
			return (0);
		k++;
	}
	return (k);
}

astreq (str1, str2, len)
char	str1[], str2[];
int	len;
{
	int	k;

	k = 0;
	while (k < len) {
		if ((str1[k] != str2[k]))
			break;
		if (str1[k] == 0)
			break;
		if (str2[k] == 0)
			break;
		k++;
	}
	if (an (str1[k]))
		return (0);
	if (an (str2[k]))
		return (0);
	return (k);
}

match (lit)
char	*lit;
{
	int	k;

	blanks ();
	if (k = streq (line + lptr, lit)) {
		lptr = lptr + k;
		return (1);
	}
	return (0);
}

amatch (lit, len)
char	*lit;
int	len;
{
	int	k;

	blanks ();
	if (k = astreq (line + lptr, lit, len)) {
		lptr = lptr + k;
		while (an (ch ()))
			inbyte ();
		return (1);
	}
	return (0);
}

blanks ()
{
	FOREVER {
		while (ch () == 0) {
			preprocess ();
			if (feof (input))
				break;
		}
		if (ch () == ' ')
			gch ();
		else if (ch () == 9)
			gch ();
		else
			return 0;
	}
}
Added src/cc8/cross/main.c.













































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File main.c: 2.7 (84/11/28,10:14:56) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

main (argc, argv)
int	argc;
char** argv;        /* OS/8 CC8 can't cope, but bootstrapping CC8 doesn't work, either */
{
	char	*p,*bp;
	int smacptr;
	macptr = 0;
	ctext = 0;
	argc--; argv++;
	errs = 0;
	aflag = 1;
	while (p = *argv++)
		if (*p == '-') while (*++p)
			switch(*p) {
				case 't': case 'T':
					ctext = 1;
					break;
				case 's': case 'S':
					sflag = 1;
					break;
				case 'c': case 'C':
					cflag = 1;
					break;
				case 'a': case 'A':
					aflag = 0;
					break;
				case 'd': case 'D':
					bp = ++p;
					if (!*p) usage();
					while (*p && *p != '=') p++;
					if (*p == '=') *p = '\t';
					while (*p) p++;
					p--;
					defmac(bp);
					break;
				default:
					usage();
			}
			else break;

	smacptr = macptr;
	if (!p)
		usage();
	while (p) {
		errfile = 0;
		if (typof(p) == 'c' || typof(p) =='C') {
			glbptr = STARTGLB;
			locptr = STARTLOC;
			wsptr = ws;
			inclsp =
			iflevel =
			skiplevel =
			swstp =
			litptr =
			stkp =
			errcnt =
			ncmp =
			lastst =
			quote[1] =
			gsize =
			inbreak =
			0;
			macptr = smacptr;
			input2 = NULL;
			quote[0] = '"';
			cmode = 1;
			glbflag = 1;
			nxtlab = 0;
			litlab = getlabel ();
			defmac("end\tmemory");
			rglbptr = glbptr;
			defmac("short\tint");
			initmac();
			/*
			 *	compiler body
			 */
			if (!openin (p))
				return 0;
			if (!openout ())
				return 0;
			header ();
			gtext ();
			parse ();
			fclose (input);
			gdata ();
			dumplits ();
			dumpglbs ();
			errorsummary ();
			trailer ();
			fclose (output);
			pl ("");
			errs = errs || errfile;
#ifndef	NOASLD
		}
		if (!errfile && !sflag)
			errs = errs || assemble(p);
#else
		} else {
			fputs("Don't understand file ", stderr);
			fputs(p, stderr);
			errs = 1;
		}
#endif
		p = *argv++;
	}
	exit(errs != 0);
    return 0;
}

FEvers()
{
	outstr("/\tFront End (1.0:27/1/99)");
    return 0;
}

usage()
{
	fputs("usage: sccXXXX [-tcsa] [-dSYM[=VALUE]] files\n", stderr);
	exit(1);
}

/*
 *	process all input text
 *
 *	at this level, only static declarations, defines, includes,
 *	and function definitions are legal.
 *
 */
parse ()
{
	while (!feof (input)) {
		if (amatch ("extern", 6))
			dodcls(EXTERN);
		else if (amatch ("static",6))
			dodcls(STATIC);
		else if (dodcls(PUBLIC)) ;
		else if (match ("#asm"))
			doasm ();
		else if (match ("#include"))
			doinclude ();
		else if (match ("#define"))
			dodefine();
		else if (match ("#undef"))
			doundef();
		else
			newfunc ();
		blanks ();
	}
    return 0;
}

/*
 *		parse top level declarations
	*/

dodcls(stclass)
int stclass; {
	blanks();
	if (amatch("char", 4))
		declglb(CCHAR, stclass);
	else if (amatch("int", 3))
		declglb(CINT, stclass);
	else if (stclass == PUBLIC)
		return(0);
	else
		declglb(CINT, stclass);
	ns ();
	return(1);
}


/*
 *	dump the literal pool
 */
dumplits ()
{
	int	j, k;

/*	A loc containing the size */
	ol("\tLAP");
	ot ("\tCPAGE ");
	onum (2+litptr);
	nl();
	outbyte('L');
	printlabel (litlab);
	col();
	ot("\t");
	onum (-litptr);
	nl();
	if (litptr == 0)
		return 0;
/*	Generate a loc containing the address of the literals */
	outbyte('X');
	printlabel (litlab);
	col();
	ot("\t");
	printlabel (litlab);
	nl();
	printlabel (litlab);
	col ();
	k = 0;
	while (k < litptr) {
		defbyte ();
		j = 8;
		while (j--) {
			onum (litq[k++] & 127);
			if ((j == 0) | (k >= litptr)) {
				nl ();
				break;
			}
			outbyte (';');
		}
	}
	ol("\tEAP");
    return 0;
}

/*
 *	dump all static variables
 */
dumpglbs ()
{
	int	j;

	if (!glbflag) {
		ot("GBLS,\t0");
		nl();
		return 0;
	}
	cptr = rglbptr;
	while (cptr < glbptr) {
		if (cptr[IDENT] != FUNCTION) {
			ppubext(cptr);
			if (cptr[STORAGE] != EXTERN) {
				//prefix ();
				//outstr (cptr);
				//col ();
				//defstorage ();
				j = glint(cptr);
				if ((cptr[TYPE] == CINT) ||
				    (cptr[IDENT] == POINTER))
					j = j * intsize();
				//onum (j);
				//nl ();
			}
		} else {
			fpubext(cptr);
		}
		cptr = cptr + SYMSIZ;
	}
  ot("GBLS,\t");
  onum(gsize+128);				// Beginning of stack after globals
  nl();
  return 0;
}

/*
 *	report errors
 */
errorsummary ()
{
	if (ncmp)
		error ("missing closing bracket");
	nl ();
	comment ();
	outdec (errcnt);
	if (errcnt) errfile = YES;
	outstr (" error(s) in compilation");
	nl ();
	comment();
	ot("literal pool:");
	outdec(litptr);
	nl();
	comment();
	ot("global pool:");
	outdec(glbptr-rglbptr);
	nl();
	comment();
	ot("Macro pool:");
	outdec(macptr);
	nl();
	pl (errcnt ? "Error(s)" : "No errors");
    return 0;
}

typof(s)
char	*s; {
	s += strlen(s) - 2;
	if (*s == '.')
		return(*(s+1));
	return(' ');
}
Added src/cc8/cross/preproc.c.





























































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File preproc.c: 2.3 (84/11/27,11:47:40) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

/*
 *	open an include file
 */
doinclude ()
{
	char	*p;
	FILE	*inp2;

	blanks ();
	if (inp2 = fixiname ())
		if (inclsp < INCLSIZ) {
			inclstk[inclsp++] = input2;
			input2 = inp2;
		} else {
			fclose (inp2);
			error ("too many nested includes");
		}
	else {
		error ("Could not open include file");
	}
	kill_line ();
    return 0;
}

/*
 *	fixiname - remove "brackets" around include file name
 */
fixiname ()
{
	char	c1, c2, *p, *ibp;
	char buf[20];

	ibp = &buf[0];

	if ((c1 = gch ()) != '"' && c1 != '<')
		return (NULL);
	for (p = line + lptr; *p ;)
		*ibp++ = *p++;
	c2 = *(--p);
	if (c1 == '"' ? (c2 != '"') : (c2 != '>')) {
		error ("incorrect delimiter");
		return (NULL);
	}
	*(--ibp) = 0;

    return c1 == '<' ? fopen(buf, "r") : NULL;
}

/*
 *	"asm" pseudo-statement
 *
 *	enters mode where assembly language statements are passed
 *	intact through parser
 *
 */
doasm ()
{
	cmode = 0;
	FOREVER {
		inln ();
		if (match ("#endasm")) {
			ol("/\t#ENDASM");
			break;
		}
		if (feof (input))
			break;
		outstr (line);
		nl ();
	}
	kill_line ();
	cmode = 1;
    return 0;
}

dodefine ()
{
	addmac();
    return 0;
}

doundef ()
{
	int	mp;
	char	sname[NAMESIZE];

	if (!symname(sname)) {
		illname();
		kill_line ();
		return 0;
	}

	if (mp = findmac(sname))
		delmac(mp);
	kill_line ();
    return 0;
}

preprocess ()
{
	if (ifline()) return 0;
	while (cpp());
    return 0;
}

doifdef (ifdef)
int ifdef;
{
	char sname[NAMESIZE];
	int k;

	blanks();
	++iflevel;
	if (skiplevel) return 0;
	k = symname(sname) && findmac(sname);
	if (k != ifdef) skiplevel = iflevel;
    return 0;
}

ifline()
{
	FOREVER {
		inln();
		if (feof(input)) return(1);
		if (match("#ifdef")) {
			doifdef(YES);
			continue;
		} else if (match("#ifndef")) {
			doifdef(NO);
			continue;
		} else if (match("#else")) {
			if (iflevel) {
				if (skiplevel == iflevel) skiplevel = 0;
				else if (skiplevel == 0) skiplevel = iflevel;
			} else noiferr();
			continue;
		} else if (match("#endif")) {
			if (iflevel) {
				if (skiplevel == iflevel) skiplevel = 0;
				--iflevel;
			} else noiferr();
			continue;
		}
		if (!skiplevel) return(0);
	}
}

noiferr()
{
	error("no matching #if...");
    return 0;
}


cpp ()
{
	int	k;
	char	c, sname[NAMESIZE];
	int	tog;
	int	cpped;		/* non-zero if something expanded */

	cpped = 0;
	/* don't expand lines with preprocessor commands in them */
	if (!cmode || line[0] == '#') return(0);

	mptr = lptr = 0;
	while (ch ()) {
		if ((ch () == '/') & (nch () == '/')) {
			inln();
		}
		if ((ch () == ' ') | (ch () == 9)) {
			keepch (' ');
			while ((ch () == ' ') | (ch () == 9))
				gch ();
		} else if (ch () == '"') {
			keepch (ch ());
			gch ();
			while (ch () != '"') {
				if (ch () == 0) {
					error ("missing quote");
					break;
				}
				if (ch() == '\\') keepch(gch());
				keepch (gch ());
			}
			gch ();
			keepch ('"');
		} else if (ch () == 39) {
			keepch (39);
			gch ();
			while (ch () != 39) {
				if (ch () == 0) {
					error ("missing apostrophe");
					break;
				}
				if (ch() == '\\') keepch(gch());
				keepch (gch ());
			}
			gch ();
			keepch (39);
		} else if ((ch () == '/') & (nch () == '*')) {
			inchar ();
			inchar ();
			while ((((c = ch ()) == '*') & (nch () == '/')) == 0)
				if (c == '$') {
					inchar ();
					tog = TRUE;
					if (ch () == '-') {
						tog = FALSE;
						inchar ();
					}
					if (alpha (c = ch ())) {
						inchar ();
						toggle (c, tog);
					}
				} else {
					if (ch () == 0)
						inln ();
					else
						inchar ();
					if (feof (input))
						break;
				}
			inchar ();
			inchar ();
		} else if (an (ch ())) {
			k = 0;
			while (an (ch ())) {
				if (k < NAMEMAX)
					sname[k++] = ch ();
				gch ();
			}
			sname[k] = 0;
			if (k = findmac (sname)) {
				cpped = 1;
				while (c = macq[k++])
					keepch (c);
			} else {
				k = 0;
				while (c = sname[k++])
					keepch (c);
			}
		} else
			keepch (gch ());
	}
	keepch (0);
	if (mptr >= MPMAX)
		error ("line too long");
	lptr = mptr = 0;
	while (line[lptr++] = mline[mptr++]);
	lptr = 0;
	return(cpped);
}

keepch (c)
char	c;
{
	mline[mptr] = c;
	if (mptr < MPMAX)
		mptr++;
	return (c);
}

defmac(s)
char *s;
{
	kill_line();
	strcpy(line, s);
	addmac();
    return 0;
}

addmac ()
{
	char	sname[NAMESIZE];
	int	k;
	int	mp;

	if (!symname (sname)) {
		illname ();
		kill_line ();
		return 0;
	}
	if (mp = findmac(sname)) {
		error("Duplicate define");
		delmac(mp);
	}
	k = 0;
	while (putmac (sname[k++]));
	while (ch () == ' ' | ch () == 9)
		gch ();
	while (putmac (gch ()));
	if (macptr >= MACMAX)
		error ("macro table full");
    return 0;
}

delmac(mp) int mp; {
	--mp; --mp;	/* step over previous null */
	while (mp >= 0 && macq[mp]) macq[mp--] = '%';
    return 0;
}
	

putmac (c)
char	c;
{
	macq[macptr] = c;
	if (macptr < MACMAX)
		macptr++;
	return (c);
}

findmac (sname)
char	*sname;
{
	int	k;

	k = 0;
	while (k < macptr) {
		if (astreq (sname, macq + k, NAMEMAX)) {
			while (macq[k++]);
			return (k);
		}
		while (macq[k++]);
		while (macq[k++]);
	}
	return (0);
}

toggle (name, onoff)
char	name;
int	onoff;
{
	switch (name) {
	case 'C':
		ctext = onoff;
		break;
	}
    return 0;
}
Added src/cc8/cross/primary.c.
































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File primary.c: 2.4 (84/11/27,16:26:07) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

primary (lval)
int	*lval;
{
	unsigned char	*ptr, sname[NAMESIZE];
	int	num[1];
	int	k;

	lval[2] = 0;  /* clear pointer/array type */
	if (match ("(")) {
		k = heir1 (lval);
		needbrack (")");
		return (k);
	}
	if (amatch("sizeof", 6)) {
		needbrack("(");
		immed();
		if (amatch("int", 3)) onum(intsize());
		else if (amatch("char", 4)) onum(1);
		else if (symname(sname)) {
			if ((ptr = findloc(sname)) ||
				(ptr = findglb(sname))) {
				if (ptr[STORAGE] == LSTATIC)
					error("sizeof local static");
				k = glint(ptr);
				if ((ptr[TYPE] == CINT) ||
					(ptr[IDENT] == POINTER))
					k *= intsize();
				onum(k);
			} else {
				error("sizeof undeclared variable");
				onum(0);
			}
		} else {
			error("sizeof only on type or variable");
		}
		needbrack(")");
		nl();
		return(lval[0] = lval[1] = 0);
	}
	if (symname (sname)) {
		if (ptr = findloc (sname)) {
			getloc (ptr);
			lval[0] = ptr;
			lval[1] =  ptr[TYPE];
			if (ptr[IDENT] == POINTER) {
				lval[1] = CINT;
				lval[2] = ptr[TYPE];
			}
			if (ptr[IDENT] == ARRAY) {
				lval[2] = ptr[TYPE];
				lval[2] = 0;
				return (0);
			}
			else
				return (1);
		}
		if (ptr = findglb (sname))
			if (ptr[IDENT] != FUNCTION) {
				lval[0] = ptr;
				lval[1] = 0;
				if (ptr[IDENT] != ARRAY) {
					if (ptr[IDENT] == POINTER)
						lval[2] = ptr[TYPE];
					return (1);
				}
				immed ();
				onum(128+ptr[OFFSET]+ptr[OFFSET+1]*256);
				ot("\t/Offset from stackbase at 128 (200(8))");
				nl ();
				lval[1] = lval[2] = ptr[TYPE];
				lval[2] = 0;
				return (0);
			}
		blanks ();
		if (ch() != '(')
			error("undeclared variable");
		ptr = addglb (sname, FUNCTION, CINT, 0, PUBLIC);
		lval[0] = ptr;
		lval[1] = 0;
		return (0);
	}
	if (constant (num))
		return (lval[0] = lval[1] = 0);
	else {
		error ("invalid expression");
		immed ();
		onum (0);
		nl ();
		junk ();
		return (0);
	}
}

/*
 *	true if val1 -> int pointer or int array and val2 not pointer or array
 */
dbltest (val1, val2)
int	val1[], val2[];
{
	if (val1 == NULL)
		return (FALSE);
	if (val1[2] != CINT)
		return (FALSE);
	if (val2[2])
		return (FALSE);
	return (TRUE);
}

/*
 *	determine type of binary operation
 */
result (lval, lval2)
int	lval[],
	lval2[];
{
	if (lval[2] && lval2[2])
		lval[2] = 0;
	else if (lval2[2]) {
		lval[0] = lval2[0];
		lval[1] = lval2[1];
		lval[2] = lval2[2];
	}
    return 0;
}
		
constant (val)
int	val[];
{
	if (number (val))
	{ 
		if (val[0]==0) {
			ol("\t/ (0)");
			cpri();
			return (1);
		}
		immed ();
	}
	else if (pstr (val))
		immed ();
	else if (qstr (val)) {
		immd2 ();
		stkbase();
		nl();
/*		outbyte ('+');  */
		immd3 ();
	} else
		return (0);
	onum (val[0]);
	nl ();
	return (1);
}

number (val)
int	val[];
{
	int	k, minus, base;
	char	c;

	k = minus = 1;
	while (k) {
		k = 0;
		if (match ("+"))
			k = 1;
		if (match ("-")) {
			minus = (-minus);
			k = 1;
		}
	}
	if (!numeric (c = ch ()))
		return (0);
	if (match ("0x") || match ("0X"))
		while (numeric (c = ch ()) ||
		       (c >= 'a' && c <= 'f') ||
		       (c >= 'A' && c <= 'F')) {
			inbyte ();
			k = k * 16 +
			    (numeric (c) ? (c - '0') : ((c & 07) + 9));
		}
	else {
		base = (c == '0') ? 8 : 10;
		while (numeric (ch ())) {
			c = inbyte ();
			k = k * base + (c - '0');
		}
	}
	if (minus < 0)
		k = (-k);
	val[0] = k;
	return (1);
}

pstr (val)
int	val[];
{
	int	k;
	char	c;

	k = 0;
	if (!match ("'"))
		return (0);
	while ((c = gch ()) != 39) {
		c = (c == '\\') ? spechar(): c;
		k = (k & 255) * 256 + (c & 255);
	}
	val[0] = k;
	return (1);
}

qstr (val)
int	val[];
{
	char	c;

	if (!match (quote))
		return (0);
	val[0] = litptr;
	while (ch () != '"') {
		if (ch () == 0)
			break;
		if (litptr >= LITMAX) {
			error ("string space exhausted");
			while (!match (quote))
				if (gch () == 0)
					break;
			return (1);
		}
		c = gch();
		litq[litptr++] = (c == '\\') ? spechar(): c;
	}
	gch ();
	litq[litptr++] = 0;
	return (1);
}

/*
 *	decode special characters (preceeded by back slashes)
 */
spechar() {
	char c;
	c = ch();

	if	(c == 'n') c = EOL;
	else if	(c == 't') c = TAB;
	else if (c == 'r') c = CR;
	else if (c == 'f') c = FFEED;
	else if (c == 'b') c = BKSP;
	else if (c == '0') c = EOS;
	else if (c == EOS) return 0;

	gch();
	return (c);
}

/*
 *	perform a function call
 *
 *	called from "heir11", this routine will either call the named
 *	function, or if the supplied ptr is zero, will call the contents
 *	of HL
 *  NB Added section to load Acc with nargs for vararg calls
 *  NB have addded pseudo functions here as well
 */
callfunction (ptr)
char	*ptr;
{
	int	nargs;

	if (strcmp(ptr,"stri")==0) {
		expression(NO);
		stri();
		needbrack(")");
		return 0;
	}
	if (strcmp(ptr,"iinit")==0) {
		expression(NO);
		iinit();
		needbrack(")");
		return 0;
	}

	nargs = 0;
	blanks ();
	if (ptr == 0)
		gpush ();
	while (!streq (line + lptr, ")")) {
		if (endst ())
			break;
		expression (NO);
		if (ptr == 0)
			swapstk ();
		gpush ();
		nargs = nargs + intsize();
		if (!match (","))
			break;
	}
	needbrack (")");
	if (aflag)
		gnargs(nargs / intsize());
	if (ptr)
		gcall (ptr,&nargs);
	else
		callstk ();
	stkp = modstk (stkp + nargs);
    return 0;
}

needlval ()
{
	error ("must be lvalue");
    return 0;
}
Added src/cc8/cross/stmt.c.






















































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File stmt.c: 2.1 (83/03/20,16:02:17) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

/*
 *	statement parser
 *
 *	called whenever syntax requires a statement.  this routine
 *	performs that statement and returns a number telling which one
 *
 *	'func' is true if we require a "function_statement", which
 *	must be compound, and must contain "statement_list" (even if
 *	"declaration_list" is omitted)
 */

statement (func)
int	func;
{
	if ((ch () == 0) & feof (input))
		return (0);
	lastst = 0;
	if (func)
		if (match ("{")) {
			compound (YES);
			return (lastst);
		} else
			error ("function requires compound statement");
	if (match ("{"))
		compound (NO);
	else
		stst ();
	return (lastst);
}

/*
 *	declaration
 */
stdecl ()
{
	if (amatch("register", 8))
		doldcls(DEFAUTO);
	else if (amatch("auto", 4))
		doldcls(DEFAUTO);
	else if (amatch("static", 6))
		doldcls(LSTATIC);
	else if (doldcls(AUTO)) ;
	else
		return (NO);
	return (YES);
}

doldcls(stclass)
int	stclass;
{
	blanks();
	if (amatch("char", 4))
		declloc(CCHAR, stclass);
	else if (amatch("int", 3))
		declloc(CINT, stclass);
	else if (stclass == LSTATIC || stclass == DEFAUTO)
		declloc(CINT, stclass);
	else
		return(0);
	ns();
	return(1);
}


/*
 *	non-declaration statement
 */
stst ()
{
	if (amatch ("if", 2)) {
		doif ();
		lastst = STIF;
	} else if (amatch ("while", 5)) {
		dowhile ();
		lastst = STWHILE;
	} else if (amatch ("switch", 6)) {
		doswitch ();
		lastst = STSWITCH;
	} else if (amatch ("do", 2)) {
		dodo ();
		ns ();
		lastst = STDO;
	} else if (amatch ("for", 3)) {
		dofor ();
		lastst = STFOR;
	} else if (amatch ("return", 6)) {
		doreturn ();
		ns ();
		lastst = STRETURN;
	} else if (amatch ("break", 5)) {
		dobreak ();
		ns ();
		lastst = STBREAK;
	} else if (amatch ("continue", 8)) {
		docont ();
		ns ();
		lastst = STCONT;
	} else if (match (";"))
		;
	else if (amatch ("case", 4)) {
		docase ();
		lastst = statement (NO);
	} else if (amatch ("default", 7)) {
		dodefault ();
		lastst = statement (NO);
	} else if (match ("#asm")) {
		doasm ();
		lastst = STASM;
	} else if (match ("{"))
		compound (NO);
	else {
		expression (YES);
/*		if (match (":")) {
			dolabel ();
			lastst = statement (NO);
		} else {
*/			ns ();
			lastst = STEXP;
/*		}
*/	}
    return 0;
}

/*
 *	compound statement
 *
 *	allow any number of statements to fall between "{" and "}"
 *
 *	'func' is true if we are in a "function_statement", which
 *	must contain "statement_list"
 */
compound (func)
int	func;
{
	int	decls;

	decls = YES;
	ncmp++;
	while (!match ("}")) {
		if (feof (input))
			return 0;
		if (decls) {
			if (!stdecl ())
				decls = NO;
		} else
			stst ();
	}
	ncmp--;
    return 0;
}

/*
 *	"if" statement
 */
doif ()
{
	int	fstkp, flab1, flab2;
	char	*flev;

	flev = locptr;
	fstkp = stkp;
	flab1 = getlabel ();
	test (flab1, FALSE);
	statement (NO);
	stkp = modstk (fstkp);
	locptr = flev;
	if (!amatch ("else", 4)) {
		gnlabel (flab1);
		return 0;
	}
	jump (flab2 = getlabel ());
	gnlabel (flab1);
	statement (NO);
	stkp = modstk (fstkp);
	locptr = flev;
	gnlabel (flab2);
    return 0;
}

/*
 *	"while" statement
 */
dowhile ()
{
	int	ws[7];

	ws[WSSYM] = locptr;
	ws[WSSP] = stkp;
	ws[WSTYP] = WSWHILE;
	ws[WSTEST] = getlabel ();
	ws[WSEXIT] = getlabel ();
	addwhile (ws);
	gnlabel (ws[WSTEST]);
	test (ws[WSEXIT], FALSE);
	statement (NO);
	jump (ws[WSTEST]);
	gnlabel (ws[WSEXIT]);
	locptr = ws[WSSYM];
	stkp = modstk (ws[WSSP]);
	delwhile ();
    return 0;
}

/*
 *	"do" statement
 */
dodo ()
{
	int	ws[7];

	ws[WSSYM] = locptr;
	ws[WSSP] = stkp;
	ws[WSTYP] = WSDO;
	ws[WSBODY] = getlabel ();
	ws[WSTEST] = getlabel ();
	ws[WSEXIT] = getlabel ();
	addwhile (ws);
	gnlabel (ws[WSBODY]);
	statement (NO);
	if (!match ("while")) {
		error ("missing while");
		return 0;
	}
	gnlabel (ws[WSTEST]);
	test (ws[WSBODY], TRUE);
	gnlabel (ws[WSEXIT]);
	locptr = ws[WSSYM];
	stkp = modstk (ws[WSSP]);
	delwhile ();
    return 0;
}

/*
 *	"for" statement
 */
dofor ()
{
	int	ws[7],
		*pws;

	ws[WSSYM] = locptr;
	ws[WSSP] = stkp;
	ws[WSTYP] = WSFOR;
	ws[WSTEST] = getlabel ();
	ws[WSINCR] = getlabel ();
	ws[WSBODY] = getlabel ();
	ws[WSEXIT] = getlabel ();
	addwhile (ws);
	pws = readwhile ();
	needbrack ("(");
	if (!match (";")) {
		expression (YES);
		ns ();
	}
	gnlabel (pws[WSTEST]);
	if (!match (";")) {
		expression (YES);
		testjump (pws[WSBODY], TRUE);
		jump (pws[WSEXIT]);
		ns ();
	} else
		pws[WSTEST] = pws[WSBODY];
	gnlabel (pws[WSINCR]);
	if (!match (")")) {
		expression (YES);
		needbrack (")");
		jump (pws[WSTEST]);
	} else
		pws[WSINCR] = pws[WSTEST];
	gnlabel (pws[WSBODY]);
	statement (NO);
	jump (pws[WSINCR]);
	gnlabel (pws[WSEXIT]);
	locptr = pws[WSSYM];
	stkp = modstk (pws[WSSP]);
	delwhile ();
    return 0;
}

/*
 *	"switch" statement
 */
doswitch ()
{
	int	ws[7];
	int	*ptr;

	ws[WSSYM] = locptr;
	ws[WSSP] = stkp;
	ws[WSTYP] = WSSWITCH;
	ws[WSCASEP] = swstp;
	ws[WSTAB] = getlabel ();
	ws[WSDEF] = ws[WSEXIT] = getlabel ();
	addwhile (ws);
//	immed ();
//	printlabel (ws[WSTAB]);
//	nl ();
//	gpush ();
	needbrack ("(");
	expression (YES);
	needbrack (")");
//	stkp = stkp + intsize();  /* '?case' will adjust the stack */
	gjcase ();
	jump (ws[WSTAB]);
	statement (NO);
	ptr = readswitch ();
//	if (ptr[WSDEF]!=ptr[WSEXIT]) jump (ptr[WSDEF]);
	jump (ptr[WSEXIT]);
	dumpsw (ptr);
	gnlabel (ptr[WSEXIT]);
	locptr = ptr[WSSYM];
//	stkp = modstk (ptr[WSSP]);
	swstp = ptr[WSCASEP];
	delwhile ();
    return 0;
}

/*
 *	"case" label
 */
docase ()
{
	int	val;

	val = 0;
	if (readswitch ()) {
		if (!number (&val))
			if (!pstr (&val))
				error ("bad case label");
		addcase (val);
		if (!match (":"))
			error ("missing colon");
	} else
		error ("no active switch");
    return 0;
}

/*
 *	"default" label
 */
dodefault ()
{
	int	*ptr,
		lab;

	if (ptr = readswitch ()) {
		ptr[WSDEF] = lab = getlabel ();
		gnlabel (lab);
		if (!match (":"))
			error ("missing colon");
	} else
		error ("no active switch");
    return 0;
}

/*
 *	"return" statement
 */
doreturn ()
{
	if (endst () == 0)
		expression (YES);
	jump(fexitlab);
    return 0;
}

/*
 *	"break" statement
 */
dobreak ()
{
	int	*ptr;

	if ((ptr = readwhile ()) == 0)
		return 0;
	modstk (ptr[WSSP]);
	jump (ptr[WSEXIT]);
    return 0;
}

/*
 *	"continue" statement
 */
docont ()
{
	int	*ptr;

	if ((ptr = findwhile ()) == 0)
		return 0;
/*	modstk (ptr[WSSP]); */
	if (ptr[WSTYP] == WSFOR)
		jump (ptr[WSINCR]);
	else
		jump (ptr[WSTEST]);
    return 0;
}

/*
 *	dump switch table
 */
dumpsw (ws)
int	ws[];
{
	int	i,j;

	gdata ();
	gnlabel (ws[WSTAB]);
	if (ws[WSCASEP] != swstp) {
		j = ws[WSCASEP];
		while (j < swstp) {
			i = 4;
			while (i--) {
				immd3();
				onum (swstcase[j]);
				nl();
				casejump();
				jump (swstlab[j++]);
				if ((i == 0) | (j >= swstp)) {
					nl ();
					break;
				}
				nl();
			}
		}
		jump(ws[WSDEF]);
	}
	gtext ();
    return 0;
}
Added src/cc8/cross/sym.c.



























































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File sym.c: 2.1 (83/03/20,16:02:19) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

/*
 *	declare a static variable
 */
declglb (typ, stor)
int	typ,
	stor;
{
	int	k, j;
	char	sname[NAMESIZE];

	FOREVER {
		FOREVER {
			if (endst ())
				return 0;
			k = 1;
			if (match ("*"))
				j = POINTER;
			else
				j = VARIABLE;
			if (!symname (sname))
				illname ();
			if (findglb (sname))
				multidef (sname);
			if (match ("[")) {
				k = needsub ();
				if (k || stor == EXTERN)
					j = ARRAY;
				else
					j = POINTER;
			}
			addglb (sname, j, typ, k, stor);
			break;
		}
		if (!match (","))
			return 0;
	}
}

/*
 *	declare local variables
 *
 *	works just like "declglb", but modifies machine stack and adds
 *	symbol table entry with appropriate stack offset to find it again
 */
declloc (typ, stclass)
int	typ, stclass;
{
	int	k, j;
	char	sname[NAMESIZE];

	FOREVER {
		FOREVER {
			if (endst ())
				return 0;
			if (match ("*"))
				j = POINTER;
			else
				j = VARIABLE;
			if (!symname (sname))
				illname ();
			if (findloc (sname))
				multidef (sname);
			if (match ("[")) {
				k = needsub ();
				if (k) {
					j = ARRAY;
					if (typ == CINT)
						k = k * intsize();
				} else {
					j = POINTER;
					k = intsize();
				}
			} else
				if ((typ == CCHAR) & (j != POINTER))
					k = 1;
				else
					k = intsize();
			if (stclass != LSTATIC) {
				k = galign(k);
				stkp = modstk (stkp - k);
				addloc (sname, j, typ, stkp+k-1, AUTO);		/* Reversed stack for PDP8 */
			} else
				addloc( sname, j, typ, k, LSTATIC);
			break;
		}
		if (!match (","))
			return 0;
	}
}

/*
 *	get required array size
 */
needsub ()
{
	int	num[1];

	if (match ("]"))
		return (0);
	if (!number (num)) {
		error ("must be constant");
		num[0] = 1;
	}
	if (num[0] < 0) {
		error ("negative size illegal");
		num[0] = (-num[0]);
	}
	needbrack ("]");
	return (num[0]);
}

findglb (sname)
char	*sname;
{
	char	*ptr;

	ptr = STARTGLB;
	while (ptr != glbptr) {
		if (astreq (sname, ptr, NAMEMAX))
			return (ptr);
		ptr = ptr + SYMSIZ;
	}
	return (0);
}

findloc (sname)
char	*sname;
{
	char	*ptr;

	ptr = locptr;
	while (ptr != STARTLOC) {
		ptr = ptr - SYMSIZ;
		if (astreq (sname, ptr, NAMEMAX))
			return (ptr);
	}
	return (0);
}

addglb (sname, id, typ, value, stor)
char	*sname, id, typ;
int	value,
	stor;
{
	char	*ptr;

	if (cptr = findglb (sname))
		return (cptr);
	if (glbptr >= ENDGLB) {
		error ("global symbol table overflow");
		return (0);
	}
	cptr = ptr = glbptr;
	while (an (*ptr++ = *sname++));
	cptr[IDENT] = id;
	cptr[TYPE] = typ;
	cptr[STORAGE] = stor;
	cptr[OFFSET] = gsize & 0xff;	
	cptr[OFFSET+1] = (gsize >> 8) & 0xff;
	gsize = gsize + value;
	glbptr = glbptr + SYMSIZ;
	return (cptr);
}

addloc (sname, id, typ, value, stclass)
char	*sname, id, typ;
int	value, stclass;
{
	char	*ptr;
	int	k;

	if (cptr = findloc (sname))
		return (cptr);
	if (locptr >= ENDLOC) {
		error ("local symbol table overflow");
		return (0);
	}
	cptr = ptr = locptr;
	while (an (*ptr++ = *sname++));
	cptr[IDENT] = id;
	cptr[TYPE] = typ;
	cptr[STORAGE] = stclass;
	if (stclass == LSTATIC) {
		gdata();
		printlabel(k = getlabel());
		col();
		defstorage();
		onum(value);
		nl();
		gtext();
		value = k;
	} else
		value = galign(value);
	cptr[OFFSET] = value & 0xff;
	cptr[OFFSET+1] = (value >> 8) & 0xff;
	locptr = locptr + SYMSIZ;
	return (cptr);
}

/*
 *	test if next input string is legal symbol name
 *
 */
symname (sname)
char	*sname;
{
	int	k;
	char	c;

	blanks ();
	if (!alpha (ch ()))
		return (0);
	k = 0;
	while (an (ch ()))
		sname[k++] = gch ();
	sname[k] = 0;
	return (1);
}

illname ()
{
	error ("illegal symbol name");
    return 0;
}

multidef (sname)
char	*sname;
{
	error ("already defined");
	comment ();
	outstr (sname);
	nl ();
    return 0;
}

glint(syment) char *syment; {
	short l,u,r;
	l = syment[OFFSET];
	u = syment[OFFSET+1];
	r = (l & 0xff) + ((u << 8) & ~0x00ff);
	return (r);
}
Added src/cc8/cross/while.c.















































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*	File while.c: 2.1 (83/03/20,16:02:22) */
/*% cc -O -c %
 *
 */

#include <stdio.h>
#include "defs.h"
#include "data.h"

addwhile (ptr)
int	ptr[];
{
	int	k;

	if (wsptr == WSMAX) {
		error ("too many active whiles");
		return 0;
	}
	k = 0;
	while (k < WSSIZ)
		*wsptr++ = ptr[k++];
	return 0;
}

delwhile ()
{
	if (readwhile ())
		wsptr = wsptr - WSSIZ;
    return 0;
}

int readwhile ()
{
	if (wsptr == ws) {
		error ("no active do/for/while/switch");
		return (0);
	} else
		return (wsptr-WSSIZ);
}

int *findwhile ()
{
	int	*ptr;

	for (ptr = wsptr; ptr != ws;) {
		ptr = ptr - WSSIZ;
		if (ptr[WSTYP] != WSSWITCH)
			return (ptr);
	}
	error ("no active do/for/while");
	return (0);
}

int *readswitch ()
{
	int	*ptr;

	if (ptr = (int *)readwhile ())
		if (ptr[WSTYP] == WSSWITCH)
			return (ptr);
	return (0);
}

addcase (val)
int	val;
{
	int	lab;

	if (swstp == SWSTSZ)
		error ("too many case labels");
	else {
		swstcase[swstp] = val;
		swstlab[swstp++] = lab = getlabel ();
		printlabel (lab);
		col ();
		nl ();
	}
    return 0;
}
Added src/cc8/examples/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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Using the Examples

This directory contains several example programs. We will use the
`calc.c` example throughout this section.

The program may be compiled using the cc8 cross-compiler to SABR sources
like so:

    $ cc8 calc.c

You can then use the `txt2ptp` program to turn the resulting `calc.s`
file into a paper tape to be loaded into OS/8:

    $ txt2ptp < calc.s > calc.pt
    $ pidp8i             ⇠ ...start PDP-8 sim somehow, then hit Ctrl-E
    sim> att -r dt0 calc.pt
    sim> cont
    .R PIP
    *CALC.SB<PTR:        ⇠ hit Enter, then Escape twice

The <kbd>Enter</kbd> key starts the transfer in the final command. The
transfer stops when `PIP` sees the <kbd>Ctrl-Z</kbd> EOF marker added to
the end of the paper tape by `txt2ptp`. The first <kbd>Escape</kbd>
finalizes the transfer and the second exits PIP, returning you to the
OS/8 command prompt.

See the [assembly examples' `README.md` file][aerm] or the [U/W FOCAL
manual supplement][uwfs] for more ideas on how to get text files like
this SABR file into OS/8.

However you manage it, you can then assemble, load, and run the programs
on the OS/8 side with:

    .COMP CALC.SB
    .R LOADER
    *CALC,LIBC/G     ⇠ press Esc to execute command and exit LOADER

The `/G` flag causes the loader to run the linked program immediately,
but once you're done modifying the program, you probably want to save it
as a core image so it can be run directly instead of being linked and
loaded again each time. You can give `/M` instead, which finalizes the
link and then exits, printing a map file before it does so. You can then
save the result where the OS/8 `R` command can find it with:

    .SAVE SYS:CALC

That produces `SYS:CALC.SV`, which an `R CALC` command will load and
run.

If you wish to compile from C source code on the OS/8 side rather than
cross-compile, I recommend using the `CC0` front-end rather than the
method shown in the [top level `README.md` file][tlrm] involving the
`CC1` stage:

    .R CC0

    >calc.cc

    .COMP CC.SB

    .R LOADER
    *CC,LIBC/M

Notice that the front-end processor produces `CC.SB`, not `CALC.SB` as
you might be expecting. This is where the `CC` comes from in the `COMP`
and `LOADER` commands.

Note that `CC0` tolerates lowercase input.


[aerm]: /doc/trunk/examples/README.md
[tlrm]: /doc/trunk/src/cc8/README.md
[uwfs]: /doc/trunk/doc/uwfocal-manual-supp.md


# The Examples

In order of complexity, they are:

## calc.c

This is a simple 4-function calculator.


## ps.c

This prints several levels of [Pascal's triangle][pt].

[pt]: https://en.wikipedia.org/wiki/Pascal%27s_triangle


## fib.c

This program calculates [Fibonacci humbers][fn], which implicitly
demonstrates the C compiler's ability to handle [recursion][rec].

[fn]:  https://en.wikipedia.org/wiki/Fibonacci_number
[rec]: https://en.wikipedia.org/wiki/Recursion_(computer_science)


## basic.c

A very simple BASIC interpreter. This program tests a broad swath of the
compiler's functionality.
Added src/cc8/examples/basic.c.





















































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#include <libc.h>
#include <init.h>


#define SMAX 10
#define CMAX 256
#define BMAX 64
#define LMAX 32
#define DMAX 32
#define CBMX 1024
#define LXMX 999


int E [SMAX];   /* subroutine line number stack */
int L [CMAX];   /* FOR loop beginning line number */
int M [CMAX];   /* FOR loop maximum index value */
int P [CMAX];   /* program variable value */
char Lb[CBMX];  /* Line buffer of CBMX chars */
int l,i,j;
int *C;         /* subroutine stack pointer */
char B [BMAX];  /* command input buffer */
char F [2];     /* temporary search string */
char *m [LXMX]; /* pointers to lines of program. This is a real waste of space! */
char *p,*q,*x,*y,*z,*s,*d;



G(  ) {  /* get program line from buffer */
	atoi(B,&l);
	y=m[l];
	if(y){
		if(strstr(B," "))
			strcpy(y,B);
		else
			y=m[l]=0;
		return;
	}
	y=Lb;
	while(*y)
		y=y+DMAX;
	strcpy(y,B);
	m [l]=y;
} /* end G */

/* recursive descent parser for arithmetic/logical expressions */
S(  ) {
	int o;

	o=J( );
	switch(*p++){
	case '=': return o==S( );
		break;
	case '#': return o!=S( );
	default: p--; return o;
	}
} /* end S */

J(  ) {
	int o;

	o=K( );
	switch(*p++){
	case '<': return o<J( );
		break;
	case '>': return o>J( );
	default: p--; return o;
	}
} /* end J */

K(  ) {
	int o;

	o=V( );
	switch(*p++){
	case '$': return o<=K( );
		break;
	case '!': return o>=K( );
	default: p--; return o;
	}
} /* end K */

V(  ) {
	int o;

	o=W( );
	switch(*p++){
	case '+': return o+V( );
		break;
	case '-': return o-V( );
	default: p--; return o;
	}
} /* end V */

W(  ) {
	int o;

	o=Y( );
	switch(*p++){
	case '*': return o*W( );
		break;
	case '/': return o/W( );
	default: p--; return o;
	}
} /* end W */

Y(  ) {
	int o;

	if(*p=='-'){
		p++;
		return -Y();
	}
	q=p;
	if(*p>='0'&&*p<='9'){
		while(*p>='0'&&*p<='9')
			p++;
		atoi(q,&o);
		return o;
	}
	if(*p=='('){
		p++; o=S( ); p++;
		return o;
	}
	return P [*p++];
} /* end Y */

bufclear()
{
	memset(m,0,LXMX);
	memset(Lb,0,CBMX);
}

main(  ) {
	int tmp;  /* temp var to fix bug 07Sep2005 Somos */

	bufclear();
	while(puts("Ok\r\n"),gets(B))
		switch(*B){
		case 'R': /* "RUN" command */
			C=E;
			l=1;
			for(i=0; i<CMAX; i++) /* initialize variables */
				P [i]=0;
			while(l){
				while(!(s=m [l])) l++;
				while(*s!=' ') s++;                      /* skip line number */
				   if ( ! strstr ( s , "\"" ) ) {
				 while ( ( p = strstr ( s , "<>" ) ) ) * p ++ = '#' , * p = ' ' ;
				 while ( ( p = strstr ( s , "<=" ) ) ) * p ++ = '$' , * p = ' ' ;
				 while ( ( p = strstr ( s , ">=" ) ) ) * p ++ = '!' , * p = ' ' ;
				 }
				d=B;
				j=0;
				while(*s){
					if(*s=='"') j++;
					if(*s!=' '||(j&1)) *d++=*s;
					s++;
				}
				*d=j=0;
				d--; /* backup to last char in line */
				if(B [1]!='='){
					switch(*B){
					case 'E': /* "END" */
						l=-1;
						break;
					case 'R':                       /* "REM" */
						if(B [2]!='M') l=*--C;  /* "RETURN" */
						break;
					case 'I':
						if(B [1]=='N'){                         /* "INPUT" */
							tmp=*d;                         /* save for bug fix next line 07Sep2005 Somos */
							gets(p=B); P [tmp]=S( );
						} else {                                /* "IF" */
							*(tmp=strstr(B,"TH"))=0;        /* "THEN" */
							p=B+2;
							if(S( )){
								p=tmp+4; l=S( )-1;
							}
						}
						break;
					case 'P': /* "PRINT" */
						tmp=',';
						p=B+5;
						while(tmp==','){
							if(*p=='"'){
								while(*++p!='"')
									putc(*p);
								p++;
							} else {
								printf("%d",S( ));
							}
							tmp=*p++;
							putc(' ');
						}
						puts("\r\n");
						break;
					case 'G':               /* "GOTO" */
						p=B+4;
						if(B [2]=='S'){ /* "GOSUB" */
							*C++=l; p++;
						}
						l=S( )-1;
						break;
					case 'F':                               /* "FOR" */
						*(tmp=strstr(B,"TO"))=0;        /* "TO" */
						p=B+5;
						P [i=B [3]]=S( );
						p=tmp+2;
						M [i]=S( );
						L [i]=l;
						break;
					case 'N': /* "NEXT" */
						tmp=*d;
						if(P [tmp]<M [tmp]){
							l=L [tmp];
							P [tmp]++;
						}
						break;
					}
				} else {
					p=B+2;
					P [*B]=S( );
				}
				l++;
			} /* end while l */
			break;
		case 'L': /* "LIST" command */
			for(j=0; j<LXMX; j++)
				if(m[j]){
					puts(m[j]);
					puts("\r\n");
				}
			break;
		case 'N': /* "NEW" command */
			bufclear();
			break;
		case 'B': /* "BYE" command */
			return 0;
			break;
		case 0:
		default:
			G( );
		}/* end switch *B */
	return 0;
} /* end main */
Added src/cc8/examples/calc.c.




























































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#include <init.h>
#include <libc.h>

main()
{
    int x, y, ans;
    int choice;
    int bfr[10];

    /* CC8 doesn't let you initialize variables at declaration time. */
    ans = 'Y';                            

    /* This would be clearer as a do/while loop, but CC8 doesn't support
     * that yet. */
    while (1) {
        /* Force answer from tail end of loop to uppercase since CC8
         * doesn't know the || operator yet.  CC8's libc doesn't have
         * toupper(), and I can't seem to get its cupper() alternative
         * to work.  Don't rewrite with the -= operator: that doesn't
         * work yet, either. */
        if (ans > 'Z') ans = ans - 32;

        /* You might be tempted to write "if (ans != 'Y') break;" and
         * then do away with one indent level for the main body of code
         * that follows, but CC8 doesn't know the != operator yet. */
        if (ans == 'Y') {
            printf("\r\nENTER ANY TWO NUMBERS.\r\n");

            printf("ENTER THE FIRST NUMBER: ");
            gets(bfr);
            sscanf(bfr, "%d", &x);

            printf("ENTER THE SECOND NUMBER: ");
            gets(bfr);
            sscanf(bfr, "%d", &y);

            printf("SELECT THE OPERATION:\r\n");

            printf("1: ADDITION\r\n");
            printf("2: SUBTRACTION\r\n");
            printf("3: MULTIPLICATION\r\n");
            printf("4: DIVISION\r\n");

            printf("CHOICE: ");
            gets(bfr);
            sscanf(bfr, "%d", &choice);

            if (choice == 1) printf("Result: %d\r\n", x + y);
            if (choice == 2) printf("Result: %d\r\n", x - y);
            if (choice == 3) printf("Result: %d\r\n", x * y);
            if (choice == 4) printf("Result: %d\r\n", x / y);

            printf("DO YOU WISH TO CONTINUE (Y/N): ");
            ans = getc();
        }
        else {
            break;
        }
    }
}
Added src/cc8/examples/fib.c.


















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#include <init.h>
#include <libc.h>

fib(n)
{
    if (n < 2)
        return n;
    else
        return fib(n-1)+fib(n-2);
}


main()
{
        int i;
        for (i=0;i<10;i++)
                printf("Fib#:%d = %d\r\n", i, fib(i));
}
Added src/cc8/examples/init.h.

1
+
../include/init.h
Added src/cc8/examples/libc.h.

1
+
../include/libc.h
Added src/cc8/examples/ps.c.



















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#include <init.h>
#include <libc.h>

main()
{
	int ar[20],i,j,n;

	n=14;
	for (i=1;i<n;i++) {
		ar[i]=1;
		for (j=i-1;j>1;j--)
			ar[j]=ar[j-1]+ar[j];
		for (j=0;j<2*(n-i-1);j++)
			putc(' ');
		for (j=1;j<i+1;j++)
			printf("%4d",ar[j]);
		printf("\r\n");
	}
}
Added src/cc8/include/init.h.






















































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * This file is part of the CC8 cross-compiler.
 *
 * The CC8 cross-compiler is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The CC8 cross-compiler is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the CC8 cross-compiler as ../GPL3.txt.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#asm
/
/	PDP8/E Run time routines for the Small-C compiler.  SABR syntax.
/
ABSYM POP 160
ABSYM PSH 161
ABSYM JLC 162
ABSYM STKP 163
ABSYM PTSK 164
ABSYM POPR 165
ABSYM PCAL 166
ABSYM TMP 167
ABSYM GBL 170
ABSYM ZTMP 171
/
	DECIM
/
STK,	COMMN 3840
/
/
/
		ENTRY MAIN
MAIN,	BLOCK 2
		TAD GBLS
		DCA STKP
		TAD GBLS
		DCA GBL
		ISZ GBL		/ LOCAL LITERALS = STKP+1
		TAD PVL
		DCA PSH
		TAD OVL
		DCA POP
		TAD MVL
		DCA PTSK
		TAD PVR
		DCA POPR
		TAD PVC
		DCA PCAL
		RIF
		TAD (3201
		DCA PCL1
		TAD PCL1
		DCA DCC0
		JMS MCC0
		CLA CMA
		MQL
		CALL 1,LIBC
		ARG STKP
		CALL 0,OPEN
		JMSI PCAL
		XMAIN
		CALL 0,EXIT
/
PUSH,	0
		CDF1
		ISZ STKP
		DCAI STKP
		TADI STKP
		JMPI PUSH
PPOP,	0
		CDF1
		DCA TMP
		TADI STKP
		MQL
		CMA
		TAD STKP
		DCA STKP
		TAD TMP
		JMPI PPOP
PUTSTK,	0
		JMSI POP
		SWP
		DCA JLC
		SWP
		DCAI JLC
		TADI JLC
		JMPI PUTSTK
POPRET,	JMSI POP
		SWP
		DCA ZTMP
		SWP
		JMPI ZTMP
PCALL,	0
		CLA CLL
PCL1,	0
		TADI PCALL
		DCA ZTMP
		TAD PCALL
		IAC
		JMSI PSH		/ PUSH RETURN
		CLA
		JMPI ZTMP
PVL,	PUSH
OVL,	PPOP
MVL,	PUTSTK
SVL,	STK
PVR,	POPRET
PVC,	PCALL
/
#endasm
Added src/cc8/include/libc.h.


























































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * This file is part of the CC8 cross-compiler.
 *
 * The CC8 cross-compiler is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The CC8 cross-compiler is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the CC8 cross-compiler as ../GPL3.txt.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

/*
 * Libc header
 *
 * Please note: no function declarations are made, so make sure the
 * arg lists are correct in the calling code.
 * 
*/

#define itoa libc0
#define	puts libc1
#define	nl libc2
#define	getc libc3
#define	gets libc4
#define	atoi libc5
#define	sscanf vlibc6
#define	xinit libc7
#define	memcpy libc8
#define	kbhit libc9
#define	putc libc10
#define	strcpy libc11
#define	strcat libc12
#define	strstr libc13
#define	exit libc14
#define	isnum libc15
#define isdigit libc15
#define	isalpha libc16
#define	sprintf vlibc17
#define	memset libc18
#define	fgetc libc19
#define	fopen libc20
#define fputc libc21
#define fclose libc22
#define printf vlibc23
#define isalnum libc24
#define isspace libc25
#define fprintf vlibc26
#define	fputs libc27
#define strcmp libc28
#define cupper libc29
#define fgets libc30
Added src/cc8/os8/Makefile.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
########################################################################
# Makefile.in - Processed by autosetup's configure script to generate
#    an intermediate GNU make(1) file for building the PiDP-8/I software
#    from within its src/cc8/os8 subdirectory.
#
# The resulting Makefile will redirect simple "make" calls to the top
# level as well as the major top-level targets (e.g. "make clean") but
# purposefully will not redirect anything like an installation or "run
# the system" type target.  Its only purpose is to help out those who
# are working on CC8 from within this directory.  If you need to work
# on the wider system, do it from the project's top level.
#
# If you are seeing this at the top of a file called Makefile and you
# intend to make edits, do that in Makefile.in.  Saying "make" will then
# re-build Makefile from that modified Makefile.in before proceeding to
# do the "make" operation.
#
# Copyright © 2017 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 clean ctags distclean tags reconfig:
	cd @builddir@; make $@
Added src/cc8/os8/bldcc8.bi.















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
$JOB BUILD C COMPILER AND LIBRARY
.COMP C8.SB
.COMP P8.SB
.COMP N8.SB
.COMP LIBC.SB
.R LOADER
*P8,LIBC/I/O$
.SAVE SYS CC2
.R LOADER
*N8,LIBC/I/O$
.SAVE SYS CC1
.R LOADER
*C8,LIBC/I/O$
.SAVE SYS CC
$END
Added src/cc8/os8/c8.c.














































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * This file is part of the CC8 OS/8 C compiler.
 *
 * The CC8 OS/8 C compiler is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The CC8 OS/8 C compiler is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the CC8 OS/8 C compiler as ../GPL3.txt.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <libc.h>
#include <init.h>

/* C compiler driver: asks for input files, copies to CC.C & runs CC1 */

main()
{
	int bfr;
	int fnm[10];

	putc('>');
	gets(fnm);
	cupper(fnm);
	fopen(fnm,"r");
	fopen("CC.C","w");
	while (bfr=fgetc())
		if (bfr!=12)		/* Ignore form feed */
			fputc(bfr);
	fclose();
#asm	
	CALL 1,CHAIN
	ARG FNM
	HLT
FNM,	TEXT "CC1@@@"
#endasm


}
Added src/cc8/os8/cc.bi.






1
2
3
4
5
6
+
+
+
+
+
+
$JOB COMPILE AND RUN C PROGRAMME
.R CC
.COMP CC.SB
.R LOADER
*CC,LIBC/I/O/G
$END
Added src/cc8/os8/header.sb.







































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
//
// This file is part of the CC8 OS/8 C compiler.
//
// The CC8 OS/8 C compiler is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public License 
// as published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// The CC8 OS/8 C compiler is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with the CC8 OS/8 C compiler as ../GPL3.txt.  If not, see
// <http://www.gnu.org/licenses/>.
//

/	SABR DEFINITIONS
/	FRONT END (2.0:1/4/2017)

OPDEF ANDI 0400
OPDEF TADI 1400
OPDEF ISZI 2400
OPDEF DCAI 3400
OPDEF JMSI 4400
OPDEF JMPI 5400
OPDEF MQL 7421
OPDEF MQA 7701
OPDEF MQO 7501
OPDEF SWP 7521
OPDEF CDF1 6211
OPDEF CDF0 6201
OPDEF RIF 6224
OPDEF CAF0 6203
OPDEF BSW 7002
OPDEF CAM 7621
/
/	PDP8/E Run time routines for Small c compiler
/
ABSYM POP 160
ABSYM PSH 161
ABSYM JLC 162
ABSYM STKP 163
ABSYM PTSK 164
ABSYM POPR 165
ABSYM PCAL 166
ABSYM TMP 167
ABSYM GBL 170
ABSYM ZTMP 171
/
	DECIM
/
STK,	COMMN 3840
/
/
/
		ENTRY MAIN
MAIN,	BLOCK 2
		TAD GBLS
		DCA STKP
		TAD GBLS
		DCA GBL
		ISZ GBL		/ LOCAL LITERALS = STKP+1
		TAD PVL
		DCA PSH
		TAD OVL
		DCA POP
		TAD MVL
		DCA PTSK
		TAD PVR
		DCA POPR
		TAD PVC
		DCA PCAL
		RIF
		TAD (3201
		DCA PCL1
		TAD PCL1
		DCA DCC0
		JMS MCC0
		CLA CMA
		MQL
		CALL 1,LIBC
		ARG STKP
		CALL 0,OPEN
		JMSI PCAL
		XMAIN
		CALL 0,EXIT
/
PUSH,	0
		CDF1
		ISZ STKP
		DCAI STKP
		TADI STKP
		JMPI PUSH
PPOP,	0
		CDF1
		DCA TMP
		TADI STKP
		MQL
		CMA
		TAD STKP
		DCA STKP
		TAD TMP
		JMPI PPOP
PUTSTK,	0
		JMSI POP
		SWP
		DCA JLC
		SWP
		DCAI JLC
		TADI JLC
		JMPI PUTSTK
POPRET,	JMSI POP
		SWP
		DCA ZTMP
		SWP
		JMPI ZTMP
PCALL,	0
		CLA CLL
PCL1,	0
		TADI PCALL
		DCA ZTMP
		TAD PCALL
		IAC
		JMSI PSH		/ PUSH RETURN
		CLA
		JMPI ZTMP
PVL,	PUSH
OVL,	PPOP
MVL,	PUTSTK
SVL,	STK
PVR,	POPRET
PVC,	PCALL
/
Added src/cc8/os8/init.h.

1
+
../include/init.h
Added src/cc8/os8/libc.c.

























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * This file is part of the CC8 OS/8 C compiler.
 *
 * The CC8 OS/8 C compiler is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The CC8 OS/8 C compiler is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the CC8 OS/8 C compiler as ../GPL3.txt.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

/*
 * PDP8/E LIBC routines for Small-C compiler.
 *
 * This is a complex collection of mixed C and SABR assembly routines.
 * Some functions have been substantially shortened to save space
 * relative to the original versions.  Eventually, most of the C will
 * need to be rewritten in SABR and hand optimised; e.g. atoi().
 */

#asm
ABSYM POP 147
ABSYM PSH 150
ABSYM JLC 151
ABSYM STKP 152
ABSYM PTSK 153
ABSYM POPR 154
ABSYM PCAL 155
ABSYM TMP 156
ABSYM GBL 157
ABSYM ZTMP 146
ABSYM ZPTR 145
ABSYM ZCTR 144
ABSYM FPTR 160
/
	DECIM
/
/
/
/
		DUMMY ARGST
		DUMMY ARGNM
ARGST,	BLOCK 2
ARGNM,	BLOCK 2
/
		ENTRY LIBC
LIBC,	BLOCK 2
		CLA CLL
		TAD I LIBC
		DCA ARGST
		INC LIBC#
		TAD I LIBC
		DCA ARGST#
		INC LIBC#
		TAD I ARGST
		DCA STKP
		IAC
		TAD LCALL	/ INIT ? LCALL==-1 
		SZA CLA
		JMP LB1
		TAD STKP
		DCA GBL		/ SET LOCAL GBL(LITPTR)
		ISZ GBL
		TAD PVL
		DCA PSH
		TAD OVL
		DCA POP
		TAD MVL
		DCA PTSK
		TAD PVR
		DCA POPR
		TAD PVC
		DCA PCAL
		RIF
		TAD (3201
		DCA PCL1
		TAD PCL1
		DCA DCC0
		JMS MCC0
		TAD STKP
		DCA I ARGST		/ UPDATE MASTER STKP
		DCA ZPTR		/ INIT PRINTF FLAG
		DCA FPTR		/ INIT FPRINTF FLAG
LB1,	MQA				/ CALL INDEX IN MQ
		SPA
		JMP LRET
		TAD CPNT
		DCA LCALL
		TAD I LCALL
		DCA LCALL
		JMSI PCAL
LCALL,	-1
		RETRN LIBC
LRET,	CLA MQL
		DCA LCALL		/ INIT OK
		RETRN LIBC
/
PUSH,	0
		CDF1
		ISZ STKP
		DCAI STKP
		TADI STKP
		JMPI PUSH
PPOP,	0
		CDF1
		DCA TMP
		TADI STKP
		MQL
		CMA
		TAD STKP
		DCA STKP
		TAD TMP
		JMPI PPOP
PUTSTK,	0
		JMSI POP
		SWP
		DCA JLC
		SWP
		DCAI JLC
		TADI JLC
		JMPI PUTSTK
POPRET,	JMSI POP
		SWP
		DCA ZTMP
		TAD STKP
		DCA I ARGST		/ UPDATE MASTER STKP
		SWP
		CDF1
		JMPI ZTMP
PCALL,	0
		CLA CLL
PCL1,	0
		TADI PCALL
		DCA ZTMP
		TAD PCALL
		IAC
		JMSI PSH		/ PUSH RETURN
		CLA
		TAD STKP
		DCA I ARGST		/ UPDATE MASTER STKP
		CDF1
		JMPI ZTMP
PVL,	PUSH
OVL,	PPOP
MVL,	PUTSTK
PVR,	POPRET
PVC,	PCALL
/
CPNT,	CLIST
		CPAGE 24
/
/		THIS IS THE DISPATCH LIST FOR THIS LIBRARY
/		MAKE SURE LIBC.H MATCHES
/
CLIST,	ITOA
		PUTS
		DISPXY
		GETC
		GETS
		ATOI
		SSCANF
		XINIT
		MEMCPY
		KBHIT
		PUTC
		STRCPY
		STRCAT
		STRSTR
		EXIT
		ISNUM
		ISALPHA
		SPRINTF
		MEMSET
		FGETC
		FOPEN
		FPUTC
		FCLOSE
		PRINTF
		ISALNUM
		ISSPACE
		FPRINTF
		FPUTS
		STRCMP
		CUPPER
		FGETS
#endasm

#define stdout 0
#define NULL 0
#define isdigit isnum



fgetc()
{
#asm

	CLA CLL
	CALL 2,CHRIO
	ARG (-4
	ARG FRSL
	TAD FRSL
	TAD (-26		/^Z
	SNA CLA
	DCA FRSL
	TAD FRSL
	CDF1
	JMPI POPR
FRSL,BLOCK 2

//	CHRIO - CHARACTER I/O.
/
/	CALL CHRIO(IDEVN,ICHAR)
/
/	IDEV = FORT II DEVICE NUMBER.
/
/	ICHAR = 7 OR 8 BIT CHARACTER.
/
/	IF IDEV IS POSITIVE, THE CHAR IS OUTPUTTED.
/
/	IF IDEV IS NEGATIVE, THE NEXT CHAR IS
/	READ FROM THE DEVICE, AND PUT IN ICHAR.
/
//
		ENTRY CHRIO
CHRIO,	BLOCK 2
	JMS GETP
	SPA		/WHAT IS DEVICE SIGN?
	JMP RCHAR	/NEG DEV.   MEANS READ.
	JMS SETDEV	/POS DEV.  MEANS WRITE.
	0000
	JMS GETP
	DCA ICHAR
	JMS CHSUB
	JMP XIT

IDEV,	0
ICHAR,	0
ADDR,	0

RCHAR,	CIA		/READ A CHAR.
	JMS SETDEV
	1024		/SET BIT FOR READ. (8 UNITS NOW!)
	JMS GETP
	CLA
	TAD CDFB
	DCA CDFCH
	JMS CHSUB
CDFCH,	HLT
	AND (127	/ 7 BIT FOR NOW
	DCAI ADDR
XIT,	CLA
	RETRN CHRIO

SETDEV,	0
	TAD (-1
	AND (7
	CLL RAR;RTR;RTR
	TAD I SETDEV
	INC SETDEV
	DCA IDEV
	JMP I SETDEV

CHSUB,	0
	TAD ICHAR
	AND (255
	TAD IDEV
	CALL 0,GENIO
	JMP I CHSUB

GETP,	0
	TAD CHRIO
	DCA CDFA
CDFA,	HLT
	TADI CHRIO#
	DCA CDFB
	INC CHRIO#
	TADI CHRIO#
	DCA ADDR
	INC CHRIO#
CDFB,	HLT
	TADI ADDR
	JMP I GETP
#endasm
}

fputc(ch)
int ch;
{
	ch;
#asm
	DCA FRSL
	CALL 2,CHRIO
	ARG (4
	ARG FRSL
	CDF1
	TAD FRSL
#endasm
}
		sixbit(p)
char *p;
{
	*p++;
#asm
		AND (63
		BSW
		MQL
#endasm
	*p;
#asm
		AND (63
		MQO
#endasm
}

fputs(p)
int *p;
{
	while (*p++)
#asm
		DCA FRSL
		CALL 2,CHRIO
		ARG (4
		ARG FRSL
		CDF1
#endasm
}


fopen (fnm,flg)
char *fnm;
int flg;
{
	char *p;
	p=fnm;
	p=strstr(fnm,".");
	if (p==0)
		return(-1);
	if (*flg=='w') {
#asm
		CLA
		TAD FC1#
		DCA FBSE#
		JMP FC3
FC1,	CALL 0,OOPEN
FC2,	CALL 0,IOPEN
#endasm
	}
	if (*flg=='r') {
#asm
		CLA
		TAD FC2#
		DCA FBSE#
FC3,	CDF1
#endasm
	*p++=0;
	sixbit(p);
#asm
		PAGE
		/ OFFSET IOPEN+81 = FILEEX
		DCA ZTMP
		TAD FC2#		/ CODE
		AND (63
		TAD (128
		DCA FDCT
		CDF0
		TADI FDCT
		DCA FEX1
		TAD FDCT
		TAD (64
		DCA FDCT
		TADI FDCT
		TAD (81			/ OFFSET OF EXTENSION
		DCA FDCT
FEX1,	HLT
		TAD ZTMP
		DCAI FDCT
		CDF1
#endasm
	fnm;
#asm
		DCA ZTMP	/ PACK 6 8BIT CHARS INTO FILENAME
		TAD (-3
		DCA FDCT
		TAD FDCA
		DCA FP4
FP1,	CAM
		TADI ZTMP
		SNA
		JMP FP2
		AND (63
		BSW
		MQL
		ISZ ZTMP
FP2,	TADI ZTMP	/ WILL USE STACK FIELD
		AND (63
		SZA
		ISZ ZTMP
		MQO
FP4,	DCA FFNM
		ISZ FP4
		ISZ FDCT
		JMP FP1
		TAD (46
		DCAI ZTMP	/ PUT . BACK INTO FNM
		CLA CLL CMA
		TAD STKP
		DCA STKP
FBSE,	CALL 2,IOPEN
		ARG FDEV
		ARG FFNM
		JMPI POPR
FDCA,	DCA FFNM
FDCT,	0
FFNM,	TEXT /TM@@@@/
FDEV,	TEXT /DSK@@@/
#endasm
	}
}

fclose()
{
#asm
		CALL 0,OCLOS
#endasm
}


puts(p)
char *p;
	{
		while (*p++) 
#asm
		TLS
XC1,	TSF
		JMP XC1
#endasm
	}

dispxy(x,y)
int x,y;
{
	x;
#asm
	3115	/ DIX
#endasm
	y;
#asm
	3116	/ DIY
	3117	/ DIL
#endasm
}

getc()
{
#asm
	CLA
GT1, KSF
	 JMP GT1
	 KRB
	 TLS
	 AND (127	/* 7 BIT! */
#endasm
}

gets(p)
char *p;
{
int q,tm;
		tm=1;
		q=p;
		while (tm) {
#asm
XC2,	CLA CLL
		KSF
		JMP XC2
		KRB
		TAD (-255
		CLA
		KRB
		SNL			/ DO NOT ECHO BS
		TLS
XC3,	AND (127
		TAD (-13	/ CR IS END OF STRING -> 0
		SZA
		TAD (13
	    DCAI STKP
#endasm
		if (tm!=127)
		  *p++=tm;
		else
			if (p-q) {
		   puts("\b \b");
		   p--;
			}
	}
	putc(10);	/* newline */
	return 1;		
}


atoi(p,rsl)
char *p;
int *rsl;
{
#asm
	DCA ZTMP
	DCA ZCTR
	TAD (3584		/ NOP
	DCA XINV
	CDF1			/ Change DF back to 1 in case SABR changes it!
#endasm
	while (*p==' ')
	 p++;
	if (*p=='-') {
#asm
	CLA
	TAD (3617
	DCA XINV		/ CIA
	CDF1
#endasm
	p++;
	}
	while (*p++) {
#asm
	TAD (-48		/ '0' ... SEE CODE
	DCA JLC
	TAD JLC
	SPA CLA
	JMP XRET
	TAD (-10
	TAD JLC
	SMA CLA
	JMP XRET		/ EXIT IF NOT NUMBER
	TAD ZTMP
	CLL RTL			/ *4
	TAD ZTMP		/ *5
	CLL RAL			/ *10
	TAD JLC
	DCA ZTMP
	ISZ ZCTR		/ CHAR COUNTER
#endasm
	}
#asm
XRET,	TAD ZCTR
		MQL
		CMA
		TAD STKP	/ ->RSL
		DCA TMP
		TADI TMP
		DCA TMP
		TAD ZTMP
XINV,	NOP
		DCAI TMP	/ WRITE RSL
		MQA			/ RETURN LENGTH
#endasm
}


xinit()
{
	puts("PDP-8 C Compiler V1.0:\r\n");
}


memcpy(dst,src,cnt)
int dst,src,cnt;
{
#asm
	CLA
	TAD STKP
	TAD (-4
	DCA 12
	CMA
	TADI 12
	DCA 11
	CMA
	TADI 12
	DCA 10
	TADI 12
	CIA
	DCA ZTMP
CP1,	TADI 10
		DCAI 11
		ISZ ZTMP
		JMP CP1
#endasm

}

kbhit()
{
#asm
		CLA CMA
		KSF
		CLA
#endasm	
}

putc(p)
char p;
{
	p;
#asm
		TLS
MP1,	TSF
		JMP MP1
#endasm
}

strcmp( dm , sm )
char *dm,*sm;
{
	int rsl;

	rsl=0;
	while (*dm)
		rsl+=(*sm++-*dm++);
	return rsl;
}

strcpy( dm , sm )
char *dm,*sm;
{
	while (*dm++=*sm++);
}

strcat( dm , sm )
char *dm,*sm;
{
	int qm;
	qm=dm;
	while(*dm) dm++;
	strcpy(dm,sm);
	return qm;
}

strstr ( s , o )
char *s , *o ;
{
char *x , *y , *z ;
 for ( x = s ; * x ; x ++ ) {
  for ( y = x , z = o ; * z && * y == * z ; y ++ ) z ++ ;
  if ( z > o && ! * z ) return x ;
 } return 0 ;
}

exit(retval)
int retval;
{
#asm
		CALL 0,EXIT
		HLT
#endasm
}

isalnum(vl)
int vl;
{
	return (isnum(vl) + isalpha(vl));
}

isnum(vl)
int vl;
{
		vl;
#asm
		TAD (-48
		SPA
		JMP XNO
		TAD (-10
		SMA CLA
XNO,	CLA SKP
		IAC
#endasm
}

isspace(vl)
int vl;
{
		vl;
#asm
		SNA
		JMP YNO
		TAD (-33
		SMA CLA
YNO,	CLA SKP
		IAC
#endasm
}


isalpha(vl)
int vl;
{
		vl;				/* Include '?' and '@' as alpha vars */
#asm
		TAD (-65
		SPA
		JMP ANO
		TAD (-26
		SPA
		JMP BNO
		TAD (-6
		SPA
		JMP ANO
		TAD (-26
BNO,	SMA CLA
ANO,	CLA SKP
		IAC
#endasm
}

cupper(p)				/* In place convert to uppercase */
int p;
{
	p;
#asm
		DCA ZTMP
CPP1,	CLA
		TADI ZTMP
		SNA
		JMP CPP2
		TAD (-97
		SPA
		JMP CPP3
		TAD (-26
		SMA
		JMP CPP3
		TAD (91
		DCAI ZTMP
CPP3,	ISZ ZTMP
		JMP CPP1
CPP2,
#endasm
}

/* Arbitrary fgets(). Read until LF, CR/LF are retained*/
/* EOF returns null, else strlen(*p) */

fgets(p)
char *p;
{
char *q;
	q=p;
	while(*p=fgetc()) {
		if (*p++==10)
			break;
	}
	*p=0;
	return (p-q);
}

memset(dst, dt, sz)
char *dst;
int dt,sz;
{
	int i;
	for (i=0;i<sz;i++)
		*dst++=dt;
}

/*
** reverse string in place 
*/
reverse(s) char *s; {
  char *j;
  int c;
  j = s + strlen(s) - 1;
  while(s < j) {
    c = *s;
    *s++ = *j;
    *j-- = c;
    }
  }

/*
	This is somewhat involved in that the vararg system in SmallC is rather limited.
	For printf and sprintf, a char buffer is required supplied by the user or,
	as below, located at the end of the stack (7500 .. 64 locs). In addition,
	another page zero location (ZPTR) is required. This is always risky as the
	SABR/LOADER system uses a lot of locations here. See how this goes as it is possible
	to use arbitrary localions on the stack as well.
*/

fprintf(nxtarg) int nxtarg;
{
#asm
		ISZ FPTR
		JMP PRINTF
#endasm
}


printf(nxtarg) int nxtarg;
{
#asm
	TAD (3904	/ THIS IS THE PRINT BUFFER AT 7500 ON THE STACK
	DCA ZPTR
	JMP SPRINTF
#endasm
}

/*
** sprintf(obfr, ctlstring, arg, arg, ...)
** Called by printf().
*/
sprintf(nxtarg) int nxtarg; {
  int  arg, left, pad, cc, len, maxchr, width;
  char *ctl, *sptr, str[17],*obfr,zptr;

#asm
	TAD ZPTR
	DCAI STKP	/ POINTS TO ZPTR
#endasm
  cc = 0;
  nxtarg = &nxtarg-nxtarg;
  if (zptr)
    obfr=zptr;
  else
	obfr = *nxtarg++;
  ctl = *nxtarg++;                          
  while(*ctl) {
    if(*ctl!='%') {*obfr++=*ctl++; ++cc; continue;}
    else ++ctl;
    if(*ctl=='%') {*obfr++=*ctl++; ++cc; continue;}
    if(*ctl=='-') {left = 1; ++ctl;} else left = 0;       
    if(*ctl=='0') pad = '0'; else pad = ' ';
	width=0;
    if(isdigit(*ctl)) {
      ctl+=atoi(ctl, &width);
      }
	maxchr=0;
    if(*ctl=='.') {            
      ctl+=atoi(++ctl,&maxchr)+1;
      }
    arg = *nxtarg++;
    sptr = str;
    switch(*ctl++) {
      case 'c': str[0] = arg; str[1] = NULL; break;
      case 's': sptr = arg;        break;
      case 'd': itoa(arg,str);     break;
      case 'b': itoab(arg,str,2);  break;
      case 'o': itoab(arg,str,8);  break;
      case 'u': itoab(arg,str,10); break;
      case 'x': itoab(arg,str,16); break;
      default:  return (cc);
      }
    len = strlen(sptr);
    if(maxchr && maxchr<len) len = maxchr;
    if(width>len) width = width - len; else width = 0; 
    if(!left) while(width--) {*obfr++=pad; ++cc;}
    while(len--) {*obfr++=*sptr++; ++cc; }
    if(left) while(width--) {*obfr++=pad; ++cc;}  
    }
  *obfr=0;
  zptr;
#asm
		SNA				/ IF ZPTR, EITHER USE PUTS OR FPUTS
		JMP PF1
		JMSI PSH
		CLA
		TAD FPTR
		SNA CLA
		JMP PF2
		JMSI PCAL
		FPUTS
		JMP PF3
PF2,	JMSI PCAL
		PUTS
PF3,	JMSI POP
PF1,	CLA
		DCA ZPTR
		DCA FPTR
#endasm

  return(cc);
  }

/*
** itoa(n,s) - Convert n to characters in s 
*/
itoa(n, s) char *s; int n; {
  int sign;
  char *ptr;
  ptr = s;
  if ((sign = n) < 0) n = -n;
  do {
    *ptr++ = n % 10 + '0';
    } while ((n = n / 10) > 0);
  if (sign < 0) *ptr++ = '-';
  *ptr = '\0';
  reverse(s);
  }

/*
** itoab(n,s,b) - Convert "unsigned" n to characters in s using base b.
**                NOTE: This is a non-standard function.
*/
itoab(n, s, b) int n; char *s; int b; {
  char *ptr;
  int lowbit;
  ptr = s;
  b >>= 1;
  do {
    lowbit = n & 1;
    n = (n >> 1) & 4095;
    *ptr = ((n % b) << 1) + lowbit;
    if(*ptr < 10) *ptr += '0'; else *ptr += 55;
    ++ptr;
    } while(n /= b);
  *ptr = 0;
  reverse (s);
  }


strlen(p)
char *p;
{
	int n;

	n=0;
	while (*p++)
		n++;
	return n;
}

#define EOF 0

sscanf(nxtarg) int nxtarg; {
  char *ctl;
  int u;
  int  *narg, wast, ac, width, ch, cnv, base, ovfl, sign, *ibfr;
  ac = 0;
  nxtarg = &nxtarg-nxtarg;
  ibfr = *nxtarg++;
  ctl = *nxtarg++;
  while(*ctl) {
    if(*ctl++ != '%') continue;
    narg = *nxtarg++;
    ctl += atoi(ctl, &width);
	if (!width)
		width=-1;
    if(!(cnv = *ctl++)) break;
    switch(cnv) {
      case 'c':
        *narg = *ibfr++;
        break;
      case 's':
        while(width--)
          if((*narg++ = *ibfr++) == 0) break;
        *narg = 0;
        break;
      default:
        switch(cnv) {
          case 'b': base =  2; break;
          case 'd': base = 10; break;
          case 'o': base =  8; break;
          case 'x': base = 16; break;
          default:  return (ac);
          }
        *narg = u = 0;
		sign = 1;
        while(width-- && (ch=*ibfr++)>32) {
          if(ch == '-') {sign = -1; continue;}
          if(ch < '0') break;
          if(ch >= 'a')      ch -= 87;
          else if(ch >= 'A') ch -= 55;
          else               ch -= '0';
          u = u * base + ch;
          }
        *narg = sign * u;
      }
    ++ac;                          
    }
  return (ac);
  }

Added src/cc8/os8/libc.h.

1
+
../include/libc.h
Added src/cc8/os8/n8.c.


































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * This file is part of the CC8 OS/8 C compiler.
 *
 * The CC8 OS/8 C compiler is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The CC8 OS/8 C compiler is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the CC8 OS/8 C compiler as ../GPL3.txt.  If not, see
 * <http://www.gnu.org/licenses/>.
 */


#include <libc.h>
#include <init.h>


#define SMAX 10
#define CMAX 256
#define BMAX 64
#define LMAX 32
#define DMAX 32
#define CBMX 1024
#define LXMX 999

int asm[CBMX];
int ltbf[512];
int xlt[CMAX];
int gm[CMAX];		/* Global symbol table */
int tkbf[LMAX];
int *p,*q,*s,*ltpt;
int gsym,lsym,gadr,ladr,stkp,lctr,*fptr,gsz,ctr,tm,ectr,cop;
int glim,*n,ccm;
int tmp;
int tkn[BMAX];
int bfr[BMAX];
int tmbf[LMAX];
int smbf[LMAX];
int Lb[BMAX];
int lm[CMAX];		/* Auto symbol table */
int fstk[BMAX];		/* Push down stack for For etc. */
int inproc,addr,cbrk;
int izf,ixf,idf,ssz,icd;


skpsp()
{
	while (isspace(*p))
		p++;
}

getsym()
{
	q=tkbf;
	skpsp();
	while (isalnum(*p))
		*q++=*p++;
	*q=0;
	skpsp();
	return *tkbf;
}


/* recursive descent parser for arithmetic/logical expressions */

S(  ) {

	cop=0;
	J( );
	switch(*p++){
	case '=':
		S();
		stri(1);
		stkp--;
		break;
	case ']':
	case ')':
		return 1;
	case ',':
		break;
	default: 
		p--;
	}
	return 0;
} /* end S */

J(  ) {

	K( );
	switch(*p++){
	case '&': J( ); stri(20); break;
	case '|': J( ); stri(-20); break;
	default: p--; return;
	}
	stkp--;
} /* end J */

K(  ) {

	V( );
	switch(*p++){
	case '<': K( ); stri(11); break;
	case '>': K( ); stri(-11); break;
	case '$': K( ); stri(24); break;
	default: p--; return;
	}
	stkp--;
} /* end K */

V(  ) {

	W( );
	switch(*p++){
	case '+': V(); stri(2); break;
	case '-': V(); stri(3); break;
	default: p--; return;
	}
	stkp--;
} /* end V */

W(  ) {

	Y( );
	skpsp();
	cop=*p;
	switch(*p++) {
	case '*': W( ); stri(13); break;
	case '/': W( ); stri(14); break;
	case '%': W( ); stri(14);stri(-14); break;
	case '=': if (*p=='=') {
				*p='$';return;
			  }
	default: p--; return;
	}
	stkp--;
} /* end W */


Y(  ) {
	int o,ctx;
	int txbf[10];

	skpsp();

	if (!*p)
		return;

	if (cop) {
		stri(19);
		stkp++;
	}

	if (*p=='"') {
		stri(10);
		stri(ltpt-ltbf);
		while (*++p-'"') {
			if (*p=='\\')
				switch (*++p) {
				case 'r':
					*p=13;
					break;
				case 'n':
					*p=10;
			}
			*ltpt++=*p;
		}
		*ltpt++=0;
		p++;
		return;
	}
	n=q=p;
	if (*p=='-')
		p++;
	if(isdigit(*p)) {
		while(isdigit(*p))
			p++;
		stri(4);
		atoi(q,&tmp);
		stri(tmp);
		return;
	}
	if (*p==39) {
		stri(4);
		stri(*++p);
		p+=2;
		return;
	}
	ixf=izf=idf=icd=0;
	if (!getsym()) {
		switch (*p++) {
			case '&':
				getsym();
				stri(21);
				stri(fndlcl(tkbf));
				return;
			case '*':
				getsym();
				ixf++;
				break;
			case '!':
				Y();
				stri(26);
				return;
			case '~':
				Y();
				stri(-26);
				return;
			case '(':
				S();
				return;
			case ')':
				icd=1;
				return;
		}
	}
	if(*p=='('){
		strcpy(txbf,tkbf);
		ctx=o=0;p++;
		while (*p && !o) {
			o=S( );
			if (icd)
				break;
			stkp++;
			stri(19);
			ctx++;		/* arg count */
		}
		stri(9);
		stri(ctx);
		stkp-=ctx;
		if ((o=strstr(gm,txbf))){
			stri(o-gm);
		}
		else {
			stri(gsz);
			strpad(txbf);
			strcat(gm,smbf);
			gsz+=9;
		}
		return;
	}
	/* Digraphs */

	q=p+1;
	if (tmp=*q==*p) 
		switch (*p) {
		case '+':
			izf=-tmp;
			p+=2;
			break;
		case '-':
			idf=-tmp;
			p+=2;
			break;
	}

	o=fndlcl(tkbf);
	tmp=-17;
	if (ssz>1) {
		if (*p=='[') {
			stri(21);
			stri(o);
			stri(19);
			stkp++;
			p++;S();
			stri(2);
			if (*p=='=')
				stri(19);
			else {
				stri(22);
				stkp--;
			}
			return;
		}
		tmp=21;
	}
	switch (*p) {
		case '=':
			if (*q=='=')
				break;
			tmp=8;
			if (ixf)
				tmp=-8;
			ixf=0;
			stkp++;
			break;
	}
	stri(tmp);
	stri(o);
	if (*n=='-')
		stri(27);
	if (izf)
		stri(15);
	if (idf)
		stri(25);
	if (ixf)
		stri(22);
	return;
} /* end Y */

procst(trm)
char trm;
{
	ccm=ctr=1;
	p=q=Lb;
	while(1) {
		tm=fgetc();
		ctr-=tm=='(';
		ctr+=tm==')';
		ccm-=tm==',';
		if (!ctr || tm==trm)
			break;
		*q++=tm;
	}
	*q=0;
	if (inproc)
		while (*p)
			S();
}

strpad(sym)
char *sym;
{
	char *a,*b;

	strcpy(a=smbf,"         ");  /* 9 spaces */
	while (*sym)
		*a++=*sym++;
}

addsym(sym,sz)
char *sym;
int sz;
{
	strpad(sym);
	smbf[8]=sz;
	if (inproc+(sz<0)) {
		smbf[7]=stkp+1;
		stkp+=sz;
		strcat(lm,smbf);
		stri(6);
		stri(sz);
		return;
	}
	smbf[7]=gadr;
	gadr+=sz;
	strcat(gm,smbf);
	gsz+=9;
}

fndlcl(sym)
char *sym;
{
	strpad(sym);
	smbf[7]=0;
	if (s=strstr(lm,smbf)) {
		ssz=s[8];
		s=s+7;
		return *s-stkp;
	}
	if (s=strstr(gm,smbf)) {
		ssz=s[8];
		s=s+7;
		return *s;
	}
	return 0;
}

gettk()
{
	char xtm;

	q=tkbf;
	while (isspace(xtm=fgetc()));
	while (isalnum(xtm)) {
		*q++=xtm;
		xtm=fgetc();
	}
	*q=0;
	return xtm;
}

popfr()
{
	while (*fptr==inproc) {
		cbrk=*--fptr;
		stri(23);
		stri(*--fptr);
		stri(5);
		stri(*fptr+2);
		fptr--;
	}
}

dostt()
{
	p=tmbf;
	while (tm!=';') {
		*p++=tm;
		tm=fgetc();
	}
	*p=0;
	strcpy(Lb,tkbf);
	strcat(Lb,tmbf);
	p=Lb;
	S();
	tm=1;
}

fnbrk()
{
	while (tm!='(')
		tm=fgetc();
}

next()
{
	char *lp;
	int fflg;

	lp=0;
	if (*tkbf) {
		strcat(tkbf," ");
		lp=strstr(tkn,tkbf);
	}
	fflg=lctr;
	if (lp) {
			switch(lp-tkn) {
			case 0:
				while (tm!=';' && tm!='{') {
					tm=gettk();
					strcpy(bfr,tkbf);
					while (isspace(tm))
						tm=fgetc();
					switch (tm) {
			case '[':
				tm=gettk();
				atoi(tkbf,&fflg);
				addsym(bfr,fflg);
				tm=fgetc();
				break;
			case '(':
				stri(7);
				stri(gsz);
				if (strstr("main",tkbf))
					strcpy(tkbf,"XMAIN");
				addsym(tkbf,1);
				procst(')');
				stkp=-(ccm+1);
				while (*p) {
					getsym();
					addsym(tkbf,-1);
					p++;
					stkp+=2;
				}
				stkp=0;
				tm=gettk();
				cbrk=100;
				break;
			case ',':
			case ';':
				addsym(tkbf,1);
				break;
					}				/* end whie */
				}					/* end case 0: */
				break;
			case 4:
				fflg=fflg+100;
			case 12:
				fnbrk();
				stri(5);
				*++fptr=fflg;
				stri(fflg);
				procst(0);
				stri(12);
				stri(tm=*fptr+2);
				*++fptr=cbrk;
				if (fflg<100)
					cbrk=tm;
				*++fptr=inproc;
				lctr+=3;
				tm=0;
				stri(99);
				break;
			case 7:
				tm=0;
				break;
			case 18:
				stri(23);
				stri(cbrk);
				break;
			case 24:
				procst(';');
				stri(23);
				stri(ectr);
				tm=1;
				break;
			case 31:
				fnbrk();
				procst(';');
				stri(5);
				stri(lctr++);
				*++fptr=lctr;
				procst(';');
				stri(12);
				stri(lctr+2);
				stri(23);
				stri(lctr+1);
				stri(5);
				stri(lctr++);
				procst(')');
				*++fptr=cbrk;
				*++fptr=inproc;
				stri(23);
				stri(lctr-2);
				stri(5);
				stri(lctr++);
				cbrk=lctr++;
				tm=0;
				break;
			default:
				dostt();
		} /* End switch */
	} else
		switch (tm) {
				case '{':
					tm=1;
					inproc++;
					break;
				case '}':
					break;
				case -1:
				case 0:
					stri(0);
#asm
					CALL 1,CHAIN
					ARG FNM
					HLT
FNM,				TEXT "CC2@@@"
#endasm
				case '/':
					while (fgetc()!='/');			/* Skip comment */
					tm=1;
					break;
				default:
					dostt();
	}
	return tm;
}



main()
{
	char trm;

	memset(ltbf,0,&ssz-ltbf);
	fopen("CC.C","r");
	strcpy(tkn,"int if else while break return for ");
	lctr = 10;
	ectr = 900;
	ltpt = ltbf;
	fptr = fstk;
	*fptr = -1;
	gadr = 128; /* Start of globals */
	iinit(asm);
	tm=gettk();
	while (1) {
		trm=next();
		tm=gettk();
		switch (trm) {
			case '{':
				inproc++;
				break;
			case '}':
				inproc--;
				if (!inproc) {
					stri(5);
					stri(ectr++);
					stri(16);
					stri(-stkp);
					stkp = *lm = 0;
					break;
				}
			case ';':
			case 1:
				stri(99);
				if (!strcmp("else",tkbf)) {
					stri(-23);
					stri(100+lctr+2);
					popfr();
					*++fptr=100+lctr++;
					*++fptr=cbrk;
					*++fptr=inproc;
				}
				else
					popfr();
			case 0:
				break;
			default:
				procst(';');
		}
	}
}

Added src/cc8/os8/p8.c.
















































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * This file is part of the CC8 OS/8 C compiler.
 *
 * The CC8 OS/8 C compiler is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The CC8 OS/8 C compiler is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the CC8 OS/8 C compiler as ../GPL3.txt.  If not, see
 * <http://www.gnu.org/licenses/>.
 */


#include <libc.h>
#include <init.h>

#define SMAX 10
#define CMAX 256
#define BMAX 64
#define LMAX 32
#define DMAX 32
#define CBMX 1024
#define LXMX 999

int asm[CBMX];
int ltbf[512];
int xlt[CMAX];
int gm[CMAX];		/* Global symbol table */
int tkbf[LMAX];
int *p,*q,*s,*ltpt;
int gsym,lsym,gadr,ladr,stkp,*as,lctr,*fptr,gsz,ctr,tm,ectr;
int glim;
int ltsz,pflg,t;
int tmstr[32];

main()
{

	as=asm;
	fopen("HEADER.SB","r");
	fopen("CC.SB","w");
	strcpy(xlt,"ITOA    PUTS    DISPXY  GETC    GETS    ATOI    SSCANF  XINIT   MEMCPY  KBHIT   PUTC    STRCPY  STRCAT  STRSTR  EXIT    ISNUM   ");
	strcat(xlt,"ISALPH  SPRINTF MEMSET  FGETC   FOPEN   FPUTC   FCLOSE  PRINTF  ISALNUM ISSPACE FPRINTF FPUTS   STRCMP  CUPPER  FGETS   ");
	while (t=fgetc())
		if (t!=12)			/* Ignore form feed */
			fputc(t);
	cupper(gm);
	while (*as) {
		pflg=0;
		*tmstr=0;
		switch (*as++) {
			case 99:
				fprintf("/\r");
				break; 
			case 1:
				fprintf("\tJMSI PTSK\r");
				break;
			case 3:
				strcpy(tmstr,"\tCIA\r");
			case 2:
				fprintf("%s\tTADI STKP\r\tJMSI POP\r",tmstr);
				break;
			case 4:
				fprintf("\tCLA\r\tTAD (%d\r",*as++);
				break;
			case 5:
				if (*as<0)
					*as=100-*as;
				fprintf("CC%d,\r",*as++);
				break;
			case 6:
				if (*as>1)
					fprintf("\tTAD STKP\r\tTAD (%d\r\tDCA STKP\r",*as);
				else
					if (*as>0)
						fputs("\tISZ STKP\r");
				as++;
				break;
			case 7:
				p=gm+*as++;
				while (*p-' ')
					fputc(*p++);
				fputs(",\r");
				break;
			case -8:
				strcpy(tmstr,"\tDCA JLC\r\tTADI JLC\r");
			case 8:
				if (*as>0)
					fprintf("\tCLA\r\tTAD (%d\r%s\tJMSI PSH\r",*as++,tmstr);
				else
					fprintf("\tCLA\r\tTAD STKP\r\tTAD (%d\r%s\tJMSI PSH\r",*as++,tmstr);
				break;
			case 9:
				tm=*as++;
				p=gm+*as++;
				strcpy(tkbf,"        ");
				memcpy(tkbf,p,7);
				if (p=strstr(xlt,tkbf)) {
					t=(p-xlt)>>3;
					if ((t==6) + (t==17) + (t==23)) 
						fprintf("\tCLA\r\tTAD (%d\r\tJMSI PSH\r",tm++);
					fprintf("\tCLA\r\tTAD (%d\r\tMQL\r\tCALL 1,LIBC\r\tARG STKP\r\tCDF1\r",t);
				}
				else
					fprintf("\tJMSI PCAL\r\t%s\r",tkbf);
				if (tm)
				    fprintf("\tMQL\r\tTAD (%d\r\tTAD STKP\r\tDCA STKP\r\tSWP\r",-tm);
				break;
			case 10:
				fprintf("\tCLA\r\tTAD GBL\r\tTAD (%d\r",*as++);
				break;
			case -11:
				fprintf("\tCIA\r\tTADI STKP\r\tJMSI POP\r\tSMA SZA CLA\r\tCMA\r");
				break;
			case 11:
				fprintf("\tCIA\r\tTADI STKP\r\tJMSI POP\r\tSPA CLA\r\tCMA\r");
				break;
			case 12:
					fprintf("\tSNA\r\tJMP CC%d\r",*as++);
				break;
			case 13:
				fprintf("\tJMSI POP\r\tDCA JLC\r\tSWP\r\tCALL 1,MPY\r\tARG JLC\r\tCDF1\r");
				break;
			case -14:
				fprintf("\tCALL 1,IREM\r\tARG 0\r\tCDF1\r");
				break;
			case 14:
				fprintf("\tJMSI POP\r\tDCA JLC\r\tSWP\r\tCALL 1,DIV\r\tARG JLC\r\tCDF1\r");
				break;
			case 15:
				fprintf("\tISZI JLC\r\tNOP\r");
				break;
			case 16:
				fprintf("\tMQL\r\tTAD STKP\r\tTAD (%d\r\tDCA STKP\r\tSWP\r\tJMPI POPR\r/\r",*as++);
				break;
			case 17:
				pflg++;
			case -17:
				if (*as>0) 
					fprintf("\tCLA\r\tTAD (%d\r\tDCA JLC\r\tTADI JLC\r",*as++);
				else
					fprintf("\tCLA\r\tTAD STKP\r\tTAD (%d\r\tDCA JLC\r\tTADI JLC\r",*as++);
				if (pflg==0)
					break;
			case 19:
				fprintf("\tJMSI PSH\r");
				break;
			case 20:
				fprintf("\tANDI STKP\r\tJMSI POP\r");
				break;
			case -20:
				fprintf("\tJMSI POP\r\tMQO\r");
				break;
			case 21:
				if (*as>0) 
					fprintf("\tCLA\r\tTAD (%d\r",*as++);
				else
					fprintf("\tCLA\r\tTAD STKP\r\tTAD (%d\r",*as++);
				break;
			case 22:
				fprintf("\tDCA JLC\r\tTADI JLC\r");
				break;
			case 23:
				if (*as<100)
					fprintf("\tJMP CC%d\r",*as);
				as++;
				break;
			case -23:
					fprintf("\tJMP CC%d\r",*as++);
				break;
			case 24:
				fprintf("\tCIA\r\tTADI STKP\r\tJMSI POP\r\tSNA CLA\r\tCMA\r");
				break;
			case 25:
				fprintf("\tMQL\r\tCMA\r\tTADI JLC\r\tDCAI JLC\r\tSWP\r");
				break;
			case 26:
				fprintf("\tSNA CLA\r");
			case -26:
				fprintf("\tCMA\r");
				break;
			case 27:
				fputs("\tCIA\r");
		}
	}
	ltsz=ltpt-ltbf;
	fprintf("\tLAP\r\tCPAGE %d\rLCC0,\t%d\rXCC0,\tCC0\rCC0,\t\r",ltsz+2,-ltsz);
	p=ltbf;
	while (ltsz) {
		fprintf("%d",*p++);
		if (ltsz>1)
			fputs("; ");
		if ((ltsz&7)==0) 
			fputc(13);
		ltsz--;
	}
	fprintf("\r\tEAP\rGBLS,\t%d\r",gadr);
	fprintf("\rMCC0,\t0\r\tCDF1\r\tTAD LCC0\r\tSNA CLA\r\tJMP I MCC0\r\tTAD XCC0\r\tDCA JLC\rDCC0,\tCDF0\r\tTADI JLC\r");
	fprintf("\tJMSI PSH\r\tCLA\r\tISZ JLC\r\tISZ LCC0\r\tJMP DCC0\r\tJMP I MCC0\rCCEND,\t0\r\t\END\r");

	fclose();
}
Added src/d8tape/LICENSE.md.












1
2
3
4
5
6
7
8
9
10
11
12
+
+
+
+
+
+
+
+
+
+
+
+
I received the following license grant via private email, which
supercedes the LICENSE file text distributed with d8tape in 2002:

---------

    On Fri, Nov 10, 2017 at 6:38 PM, Rob Krten <rob@krten.com> wrote:

    As far as d8tape goes:

          I, Robert Krten, hereby provide a royalty free, perpetual, worldwide
          license to use the source code and information provided in my PDP-8
          disassembler, called "d8tape" for any purpose whatsoever.
Added src/d8tape/d8tape.h.
















































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

/*
 *	d8tape.h
 *
 *	(C) Copyright 2007 by Robert Krten, all rights reserved.
 *	Please see the LICENSE file for more information.
 *
 *	This module contains the manifest constants and other header
 *	information.
 *
 *	2007 10 25	R. Krten		created
 *	2007 10 28	R. Krten		added TAG_INDIRECTFC
*/

// constants
#define	CORE_SIZE		4096				// size of core memory

#define	TAG_DATA		0x0001				// memory region is tagged as data,
#define	TAG_SUB			0x0002				// subroutine target, or,
#define	TAG_LABEL		0x0004				// label
#define	TAG_RETURN		0x0008				// return from subroutine
#define	TAG_TYPE_MASK	0x00ff				// mask of above types
#define	TAG_WRITABLE	0x0100				// set if anyone writes to this data location (else constant)
#define	TAG_KONSTANT	0x0200				// can be changed from Caaaa -> Kvvvv
#define	TAG_INDIRECTFC	0x0400				// target of an indirect flow control (JMS I / JMP I) (only meaningful if not writable)

#include <stdint.h>

// segment info

typedef struct
{
	uint16_t	saddr;						// starting address
	uint16_t	nwords;						// number of contiguous words
}	segment_t;


// prototypes

// flow.c
extern	void			flow (void);

// dasm.c
extern	int				ea (int addr, int opcode, int *indirect, int *curpage);
extern	void			disassemble (void);
extern	int				is_data (int v);
extern	int				fetch_iot (int code, char *dis, char *com);

Added src/d8tape/dasm.c.















































































































































































































































































































































































































































































































































































































































































































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

/*
 *	dasm.c
 *
 *	(C) Copyright 2003 by Robert Krten, all rights reserved.
 *	Please see the LICENSE file for more information.
 *
 *	This module contains the PDP-8 disassembler.
 *	Note that the 8/I and 8/L are featured at this point; other models
 *	should be added (particularly the 8/E's EAE instructions, as well
 *	as new IOT decodings, etc)
 *
 *	2003 12 16	R. Krten		created
 *	2007 10 29	R. Krten		added better output formatting
 *	2007 11 02	R. Krten		added xrefs
 *	2009 02 08	D. North		fixups for missing 7002 BSW and removal of
 *								7600 CLA case (conflicts with 7200 on reassembly)
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

#include "d8tape.h"
#include "iot.h"							// decode table for IOTs

extern	char	*progname;					// main.c
extern	char	*version;					// version.c

extern	int		optv;						// main.c
extern	char	*tapename;					// main.c, name of tape image

extern	short int	core [];				// main.c, in-core image (-1 means location never used)
extern	uint16_t	tags [];				// main.c, analysis tags
extern	segment_t	*segments;				// main.c, used to accumulate runs of data (from origin for nwords)
extern	int			nsegments;				// main.c, indicates how many segments we have

static void header (void);
static void disassemble_range (int start, int end);
static void pad (char *buf, int off, int pos);
static void xrefs (int addr);

/*
 *	dasm8
 *
 *	This takes the current address and the instruction value
 *	and prints the decoded instruction.  A static variable
 *	is used to kludge up 2-word instructions (e.g., MUY <const>).
 *
 *	The IOTs are coded in a table; in some cases, conflicts exist, you'll
 *	need to select the appropriate #defines to make them go away :-)
 *	As shipped, the #defines match my preferences.
 *
 *	Formatting rules:
 *		- the following types of output exist:
 *			- labels
 *			- banners
 *			- code
 *			- data
 *
 *	For each type, the following format is used (all tabs, as shown by the single backtick
 *	character, are assumed to be at 4 character tabstops.  If you don't like this, pipe
 *	the output through "expand -4").
 *
 *		Labels:
 *			          1111111111222222222233333333334444444444555555555566666666667777777777
 *			01234567890123456789012345678901234567890123456789012345678901234567890123456789
 *			t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t
 *			TAAAA,
 *
 *				where "T" is the label type, one of "D" for data, "L" for executable label,
 *				and "S" for subroutine entry, and "AAAA" is the four digit octal address.
 *				(See "Data" below for additional details of the "D" type).
 *
 *		Banners:
 *			          1111111111222222222233333333334444444444555555555566666666667777777777
 *			01234567890123456789012345678901234567890123456789012345678901234567890123456789
 *			t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t
 *			////////////////////////////////////////////////////////////////////////////////
 *			/
 *			/```CONTENT
 *			/
 *			////////////////////////////////////////////////////////////////////////////////
 *
 *				Where "CONTENT" is the content of the banner, e.g., "SUBROUTINE S1234".
 *
 *		Code:
 *			          1111111111222222222233333333334444444444555555555566666666667777777777
 *			01234567890123456789012345678901234567890123456789012345678901234567890123456789
 *			t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t
 *				op1`````````````````````/ COMMENTS..............................@@=AAAA,OOOO
 *				opc1````````````````````/ COMMENTS..............................@@=AAAA,OOOO
 *				op1 TAAAA```````````````/ COMMENTS..............................@@=AAAA,OOOO
 *				op1 I TAAAA`````````````/ COMMENTS..............................@@=AAAA,OOOO
 *				op1 op2 op3 op4 op5 op6`/ COMMENTS..............................@@=AAAA,OOOO
 *				12345678911234567892123   1234567891123456789212345678931234567		(DISLEN and COMLEN, resp.)
 *			01234567891123456789212345678 234567891123456789212345678931234567890	(COMSTART and DATASTART, resp.)
 *
 *				Where "op1", "opc1", "op2" through "op6" are 3 or 4 character mnemonic
 *				opcodes.  "T" is the label type (as above), and "AAAA" is the address.
 *				Tabs are used to fill whitespace to the "/" comment delimiter, and from the
 *				end of the comment to the @@.  The area at the "@@" indicates the address
 *				and the contents.
 *
 *				This is where the COMLEN and DISLEN buffer sizes are derived from, and the
 *				COMSTART position (28, the start of the "/")
 *
 *		Data:
 *			          1111111111222222222233333333334444444444555555555566666666667777777777
 *			01234567890123456789012345678901234567890123456789012345678901234567890123456789
 *			t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t	t
 *			CAAAA,``VVVV````````````````/ op1 op2 op3 op4 op5 op6 COMMENTS..................
 *			DAAAA,``VVVV````````````````/ op1 op2 op3 op4 op5 op6 COMMENTS..................
 *			KVVVV,``VVVV````````````````/ op1 op2 op3 op4 op5 op6 COMMENTS..................
 *
 *				Where "C" is used for constants whose values are not unique, "D" is used
 *				for writable data, and "K" is used for constants that can be added in the
 *				symbol table.  The distinction between "C" and "K" is that if two different
 *				memory locations both define the same constant value, we need to use "C"
 *				because it's tagged based on the address, whereas "K" is tagged based on
 *				the value.
 *
 *	Other types to consider are comment blocks imported from a control file.
*/

static const char *codes [6] = {"AND", "TAD", "ISZ", "DCA", "JMS", "JMP"};	// IOT and OPR are decoded elsewhere

static unsigned short int two_word = 0;		// set to hold two-word instruction (e.g., EAE ops), else zero (instruction 0, AND, is not a two-word instruction)

#define	COMLEN			37					// length of comment, see "Code", above (both the number of bytes and the max number of characters; tabs will only make the number of bytes less)
#define	DISLEN			23					// length of disassembly area, see "Code" above (ditto)
#define	COMSTART		28					// 0-based column number where the comments start
#define	DATASTART		40					// 0-based column number where the data (@@=AAAA,OOOO) starts
static char disbuf [DISLEN + 1];
static char combuf [COMLEN + 1];

void
dasm8 (int addr, unsigned short int buf)
{
	int		ind, cur;
	int		eff_addr;
	int		primary;						// is primary disassembly 'c'ode or 'd'ata?

	if (optv > 1) {
		printf ("dasm8 (addr 0%04o word 0%04o tag 0x%04x)\n", addr, buf, tags [addr]);
	}

	eff_addr = ea (addr, buf, &ind, &cur);

	if (two_word) {
		printf ("\t%04o /\t\t\t\t\t / (operand)\n", buf);
		two_word = 0;
		return;
	}

	// prepare buffer areas for disassembly and comments.
	memset (disbuf, 0, sizeof (disbuf));
	memset (combuf, 0, sizeof (combuf));

	primary = 'c';							// default to code disassembly

	if (tags [addr] & TAG_LABEL) {
		printf ("L%04o,\n", addr);
	}

	if (tags [addr] & TAG_SUB) {
		printf ("\n");
		printf ("////////////////////////////////////////////////////////////////////////////////\n");
		printf ("/\n");
		printf ("/\tSUBROUTINE:  S%04o\n", addr);
		printf ("/\n");
		xrefs (addr);
		printf ("////////////////////////////////////////////////////////////////////////////////\n");
		printf ("S%04o,\n", addr);
		printf ("\t0\t\t\t\t\t\t/ return area\n");
		// done; can't be SUB and anything else (except label, perhaps)
		return;
	}

	if (tags [addr] & TAG_DATA) {
		// if it's data, set primary as data
		primary = 'd';
		if ((addr & 07770) == 00010) {	// addresses 0010 -> 0017 are autoindex
			printf ("AI%o,\t%04o\t\t\t\t/ AUTO-INDEX REGISTER", addr & 7, core [addr]);
		} else {
			if (tags [addr] & TAG_INDIRECTFC) {
				printf ("C%04o,\n", addr);
			}
			printf ("%c%04o,\t", tags [addr] & TAG_KONSTANT ? 'K' : tags [addr] & TAG_WRITABLE ? 'D' : 'C', tags [addr] & TAG_KONSTANT ? core [addr] : addr);
			printf ("%04o\t\t\t\t/", core [addr]);
		}
	}

	switch (buf & 07000) {
	case	00000:			// AND
	case	01000:			// TAD
	case	02000:			// ISZ
	case	03000:			// DCA
	case	04000:			// JMS
	case	05000:			// JMP
		sprintf (disbuf, "%s ", codes [buf >> 9]);

		if (ind) {
			strcat (disbuf, "I ");
		} else {
			strcat (disbuf, "  ");
		}

		if (tags [eff_addr] & TAG_SUB) {
			strcat (disbuf, "S");
		} else if (tags [eff_addr] & TAG_LABEL) {
			strcat (disbuf, "L");
		} else {
			if ((eff_addr & 07770) == 00010) {	// addresses 0010 -> 0017
				strcat (disbuf, "AI");
				strcat (combuf, "AUTO INDEX REGISTER");
			} else {
				strcat (disbuf, (tags [eff_addr] & TAG_KONSTANT) ? "K" : tags [eff_addr] & TAG_WRITABLE ? "D" : "C");
			}
		}

		if (tags [addr] & TAG_RETURN) {
			strcat (combuf, "return ");
		} else {
			// comment indirect flow control change to reflect ultimate target
			switch (buf & 07400) {
			case	04400:
				sprintf (combuf + strlen (combuf), "long call to S%04o ", core [eff_addr]);
				break;
			case	05400:
				sprintf (combuf + strlen (combuf), "long jump to L%04o ", core [eff_addr]);
				break;
			}
		}

		if ((eff_addr & 07770) == 00010) {		// address 0010 -> 0017
			sprintf (disbuf + strlen (disbuf), "%o ", eff_addr & 7);
		} else {
			sprintf (disbuf + strlen (disbuf), "%04o ", (tags [eff_addr] & TAG_KONSTANT) ? core [eff_addr] : eff_addr);
		}

		break;

	case	06000:			// IOT
		fetch_iot (buf, disbuf, combuf);
		break;

	case	07000:			// OPR
		// perform "short form" OPRs here first...
		switch (buf) {
			case	07000: sprintf (disbuf, "NOP");																		break;
			case	07002: sprintf (disbuf, "BSW");																		break;
			case	07041: sprintf (disbuf, "CIA");																		break;
			case	07120: sprintf (disbuf, "STL");																		break;
			case	07204: sprintf (disbuf, "GLK");																		break;
			case	07240: sprintf (disbuf, "STA");					strcat (combuf, "AC = 7777 (-0001)");				break;
			case	07300: sprintf (disbuf, "CLA CLL");				strcat (combuf, "AC = 0000");						break;
			case	07301: sprintf (disbuf, "CLA CLL IAC");			strcat (combuf, "AC = 0001");						break;
			case	07302: sprintf (disbuf, "CLA IAC BSW");			strcat (combuf, "AC = 0100 (64)");					break;
			case	07305: sprintf (disbuf, "CLA CLL IAC RAL");		strcat (combuf, "AC = 0002");						break;
			case	07325: sprintf (disbuf, "CLA CLL CML IAC RAL");	strcat (combuf, "AC = 0003");						break;
			case	07326: sprintf (disbuf, "CLA CLL CML RTL");		strcat (combuf, "AC = 0002");						break;
			case	07307: sprintf (disbuf, "CLA CLL IAC RTL");		strcat (combuf, "AC = 0004");						break;
			case	07327: sprintf (disbuf, "CLA CLL CML IAC RTL");	strcat (combuf, "AC = 0006");						break;
			case	07330: sprintf (disbuf, "CLA CLL CML RAR");		strcat (combuf, "AC = 4000 (-4000 = -2048 dec)");	break;
			case	07332: sprintf (disbuf, "CLA CLL CML RTR");		strcat (combuf, "AC = 2000 (1024)");				break;
			case	07333: sprintf (disbuf, "CLA CLL CML IAC RTL");	strcat (combuf, "AC = 6000 (-2000 = -1024 dec)");	break;
			case	07340: sprintf (disbuf, "CLA CLL CMA");			strcat (combuf, "AC = 7777 (-0001)");				break;
			case	07344: sprintf (disbuf, "CLA CLL CMA RAL");		strcat (combuf, "AC = 7776 (-0002)");				break;
			case	07346: sprintf (disbuf, "CLA CLL CMA RTL");		strcat (combuf, "AC = 7775 (-0003)");				break;
			case	07350: sprintf (disbuf, "CLA CLL CMA RAR");		strcat (combuf, "AC = 3777 (2047)");				break;
			case	07352: sprintf (disbuf, "CLA CLL CMA RTR");		strcat (combuf, "AC = 5777 (-2001 = -1025 dec)");	break;
			case	07401: sprintf (disbuf, "NOP");																		break;
			case	07410: sprintf (disbuf, "SKP");																		break;
			case	07600: sprintf (disbuf, "7600");				strcat (combuf, "AKA \"CLA\"");						break;
			case	07610: sprintf (disbuf, "SKP CLA");																	break;
			case	07604: sprintf (disbuf, "LAS");																		break;
			case	07621: sprintf (disbuf, "CAM");																		break;
			default:

				// determine group (0401 is 0000/0001 for group 1, 0400 for group 2, 0401 for EAE)
				switch (buf & 00401) {
				case	00000:	// group 1
				case	00001:	// group 1
					// sequence 1
					if (buf & 00200) {
						strcat (disbuf, "CLA ");
					}
					if (buf & 00100) {
						strcat (disbuf, "CLL ");
					}
					// sequence 2
					if (buf & 00040) {
						strcat (disbuf, "CMA ");
					}
					if (buf & 00020) {
						strcat (disbuf, "CML ");
					}
					// sequence 3
					if (buf & 00001) {
						strcat (disbuf, "IAC ");
					}
					// sequence 4
					if (buf & 00010) {
						if (buf & 00002) {
							strcat (disbuf, "RTR ");
						} else {
							strcat (disbuf, "RAR ");
						}
					}
					if (buf & 00004) {
						if (buf & 00002) {
							strcat (disbuf, "RTL ");
						} else {
							strcat (disbuf, "RAL ");
						}
					}
					break;

				case	00400:	// group 2
					// sequence 1
					if (buf & 00100) {
						if (buf & 00010) {
							strcat (disbuf, "SPA ");
						} else {
							strcat (disbuf, "SMA ");
						}
					}
					if (buf & 00040) {
						if (buf & 00010) {
							strcat (disbuf, "SNA ");
						} else {
							strcat (disbuf, "SZA ");
						}
					}
					if (buf & 00020) {
						if (buf & 00010) {
							strcat (disbuf, "SZL ");
						} else {
							strcat (disbuf, "SNL ");
						}
					}
					// sequence 2
					if (buf & 00200) {
						strcat (disbuf, "CLA ");
					}
					// sequence 3
					if (buf & 00004) {
						strcat (disbuf, "OSR ");
					}
					if (buf & 00002) {
						strcat (disbuf, "HLT ");
					}
					break;
				case	00401:	// EAE
					// sequence 1
					if (buf & 00200) {
						strcat (disbuf, "CLA ");
					}
					// sequence 2
					if (buf & 00100) {
						strcat (disbuf, "MQA ");
					}
					if (buf & 00040) {
						strcat (disbuf, "SCA ");
					}
					if (buf & 00020) {
						strcat (disbuf, "MQL ");
					}
					// sequence 3
					switch (buf & 00016) {
					case	0:		// no further ops, done
						break;
					case	002:
						strcat (disbuf, "SCL ");
						two_word = buf;
						break;
					case	004:
						strcat (disbuf, "MUY ");
						two_word = buf;
						break;
					case	006:
						strcat (disbuf, "DVI ");
						two_word = buf;
						break;
					case	010:
						strcat (disbuf, "NMI");
						break;
					case	012:
						strcat (disbuf, "SHL ");
						two_word = buf;
						break;
					case	014:
						strcat (disbuf, "ASR ");
						two_word = buf;
						break;
					case	016:
						strcat (disbuf, "LSR ");
						two_word = buf;
						break;
					}
					break;
				}
				break;
		}
		break;
	}
	if (two_word) {
		strcat (disbuf, "+");
	}
	// trim any trailing spaces
	while (*disbuf && disbuf [strlen (disbuf) - 1] == ' ') {
		disbuf [strlen (disbuf) - 1] = 0;
	}

	if (primary == 'c') {				// if primary is code, then spill data too
		pad (disbuf, 0, COMSTART - 4);	// add tabs to get disassembly to comment start (one tab less because we print it next)
		printf ("\t%s", disbuf);		// print disassembly so far
		printf ("/ ");					// print comment start
		pad (combuf, 2, DATASTART);		// pad comment buffer to get to data area
		printf ("%s@@%04o=%04o\n", combuf, addr, buf);	// print comment, address and opcode
	} else {							// else we've already spilled both, just terminate the line
		pad (disbuf, 2, DATASTART);
		printf (" %s", disbuf);
		printf ("\n");
		two_word = 0;					// we don't care that it's a two-word when we're printing it as data
	}
}

/*
 *	ea
 *
 *	Calculate the effective address given the
 *	address and opcode.  Opcodes that don't have
 *	an effective address (e.g., IOTs), return -1.
 *
 *	The indirect pointer is optional, and, if specified,
 *	will cause the location to be returned with a zero
 *	or one indicating indirection.  The indirect pointer
 *	is not modified in case of a non-EA opcode.
 *
 *	Similarly for the curpage pointer.
*/

int
ea (int addr, int opcode, int *indirect, int *curpage)
{
	int		eff_addr;
	int		c;
	int		i;

	if (opcode >= 06000) {			// IOTs and OPRs don't have an EA
		return (-1);
	}

	i = opcode & 00400;
	c = opcode & 00200;
	eff_addr = c ? (addr & 07600) + (opcode & 00177) : opcode & 00177;
	if (indirect) {
		*indirect = i;
	}
	if (curpage) {
		*curpage = c;
	}
	return (eff_addr);
}

/*
 *	disassemble
 *
 *	This drives disassembly once the flow analysis has been done.
 *
 *	We disassemle in segment order.
*/

void
disassemble (void)
{
	int		snum;

	header ();
	for (snum = 0; snum < nsegments; snum++) {
		printf ("\n*%04o\n", segments [snum].saddr);
		disassemble_range (segments [snum].saddr, segments [snum].saddr + segments [snum].nwords);
	}
}

static void
header (void)
{
	struct	tm *tm;
	time_t	now;
	int		nused, ndata, ncode;
	int		i;

	time (&now);
	tm = localtime (&now);

	nused = ndata = ncode = 0;
	for (i = 0; i < CORE_SIZE; i++) {
		if (core [i] >= 0) {
			nused++;
			if (tags [i] & TAG_DATA) {
				ndata++;
			} else {
				ncode++;
			}
		}
	}

	printf ("TITLE \"AUTOMATIC DISASSEMBLY OF %s BY D8TAPE\"\n", tapename);
	printf ("////////////////////////////////////////////////////////////////////////////////\n");
	printf ("/\n");
	printf ("/\tAutomatic Disassembly of %s\n", tapename);
	printf ("/\tGenerated %04d %02d %02d %02d:%02d:%02d\n", tm -> tm_year + 1900, tm -> tm_mon + 1, tm -> tm_mday, tm -> tm_hour, tm -> tm_min, tm -> tm_sec);
	printf ("/\tGenerated by d8tape version %s\n", version);
	printf ("/\tVisit http://www.pdp12.org/pdp8/software/index.html for updates\n");
	printf ("/\n");
	printf ("/\tSymbol format:\n");
	printf ("/\t\tAIx   -- Auto-index variables (address range 001x)\n");
	printf ("/\t\tCaaaa -- Constants (non-unique)\n");
	printf ("/\t\tDaaaa -- Data (read/write variables)\n");
	printf ("/\t\tKvvvv -- Program-wide unique constants\n");
	printf ("/\t\tLaaaa -- Labels for control flow targets\n");
	printf ("/\t\tSaaaa -- Subroutines\n");
	printf ("/\n");
	printf ("/\tWhere:\n");
	printf ("/\t\taaaa is the definition address\n");
	printf ("/\t\tvvvv is the value of the constant\n");
	printf ("/\t\tx    is the last digit of the address 001x for auto-index variables\n");
	printf ("/\n");
	printf ("/\t%04o locations used, %04o code and %04o data\n", nused, ncode, ndata);
	printf ("////////////////////////////////////////////////////////////////////////////////\n");
}

static void
disassemble_range (int start, int end)
{
	int		addr;

	for (addr = start; addr < end; addr++) {
		dasm8 (addr, core [addr]);
	}
}

/*
 *	fetch_iot
 *
 *	This function looks up in the iot table (iot.h) to find
 *	the opcode passed in "code" and updates the disassembled
 *	output "dis" and the comment "com".
 *
 *	More work needs to be done here for conflicting IOTs.
 *
 *	Current, I assume that there are no conflicts (actually, I
 *	return the first match, regardless of conflicts).  A command
 *	line / control file option needs to be created to allow
 *	the selection of devices.  Something like "-i vc8i", for example
 *	to allow the VC8/I IOTs to be enabled.
*/

int
fetch_iot (int code, char *dis, char *com)
{
	int		i;

	for (i = 0; i < sizeof (iots) / sizeof (iots [0]); i++) {
		if (code == iots [i].code) {
			if (dis) {
				strcpy (dis, iots [i].mnemonic);
			}
			if (com) {
				strncpy (com, iots [i].comment, COMLEN - 1);
			}
			return (1);
		}
	}
	if (dis) {
		sprintf (dis, "%04o", code);
	}
	if (com) {
		sprintf (com, "unknown IOT");
	}
	return (0);
}

/*
 *	pad
 *
 *	Figures out where the current print position is based on expanding
 *	the current tabs in "buf" and adds more tabs to get to "pos".
*/

static void
pad (char *buf, int loc, int pos)
{
	for (; *buf; buf++) {
		if (*buf == '\t') {
			if ((loc & 3) == 0) {
				loc += 4;
			} else {
				loc += 4 - (loc & ~3);
			}
		} else {
			loc++;
		}
	}

	loc = pos / 4 - loc / 4;
	while (loc--) {
		*buf++ = '\t';
	}
	*buf = 0;
}

int
is_data (int v)
{
	return ((v & TAG_TYPE_MASK) == TAG_DATA);
}

static void
xrefs (int addr)
{
	int		i;
	int		eff;
	int		count;

	count = 0;
	for (i = 0; i < CORE_SIZE; i++) {
		if (core [i] < 0) {
			continue;
		}

		if (tags [i] & TAG_DATA) {
//printf ("+XREF ADDR %04o CHECK %04o IS DATA\n", addr, i);
			continue;
		}


		if ((core [i] & 07400) == 04000) {	// direct JMS
			eff = ea (i, core [i], NULL, NULL);
//printf ("+XREF ADDR %04o CHECK %04o %04o JMS EA %04o\n", addr, i, core [i], eff);
			if (eff == addr) {
				if (!count) {
					printf ("/\tCalled from:\n/\t");
				}
				printf ("%04o ", i);
				count++;
				if ((count % 15) == 0) {
					printf ("\n/\t");
				}
			}
		} else if ((core [i] & 07400) == 04400) {	// indirect JMS
			eff = ea (i, core [i], NULL, NULL);
//printf ("+XREF ADDR %04o CHECK %04o %04o JMS I EA %04o\n", addr, i, core [i], eff);
			if (tags [eff] & TAG_WRITABLE) {
				continue;
			}
//printf ("+XREF ADDR %04o CHECK %04o %04o JMS I is not writable\n", addr, i, core [i]);
			if (core [eff] < 0) {
				continue;
			}
//printf ("+XREF ADDR %04o CHECK %04o %04o JMS I has valid indirect value\n", addr, i, core [i]);
			if (core [eff] == addr) {
				if (!count) {
					printf ("/\tCalled from:\n/\t");
				}
				printf ("%04o ", i);
				count++;
				if ((count % 15) == 0) {
					printf ("\n/\t");
				}
			}
		}
	}
	if (count) {
		printf ("\n");
		printf ("/\tTotal %04o (%d) calls\n", count, count);
	} else {
		printf ("/\tNever called\n");
	}
}

Added src/d8tape/flow.c.


















































































































































































































































































































































































































































































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

/*
 *	flow.c
 *
 *	(C) Copyright 2007 by Robert Krten, all rights reserved.
 *	Please see the LICENSE file for more information.
 *
 *	This module contains the PDP-8 flow analyzer.
 *
 *	2007 10 25	R. Krten		created
 *	2007 10 28	R. Krten		added TAG_INDIRECTFC
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include "d8tape.h"

extern	char	*progname;					// main.c
extern	char	*version;					// version.c

extern	int		optv;						// main.c
extern	short int	core [];				// main.c, in-core image (-1 means location never used)
extern	uint16_t	tags [];				// main.c, analysis tags

char	konstmap [4096];					// 12 bits gives us 0..4095, so 4096 different constants (0 == not used, 1 == unique, 2 == used but not unique)

static void pass1 (void);
static void pass2 (void);
static void pass3 (void);
static void pass4 (void);
static void verify_subroutines (void);
static int valid_opr (int opr);

/*
 *	Main flow analysis
 *
 *	See individual functions for more details
 *
 *	Basic idea is that there is a shadow array called "tags", which indicates
 *	information about the given memory location (e.g., tags [0017] gives
 *	information about core [0017]).
 *
 *	The various passes affect the tags[] array with whatever they can detect.
*/

void
flow (void)
{
	pass1 ();
	pass2 ();
	pass3 ();
//	pass4 ();	// this pass is fundamentally broken (well, maybe not the pass, but the interpretation of the results in dasm.c)
	verify_subroutines ();
}

/*
 *	pass1
 *
 *	On pass 1, we can say with certainty only the following statements:
 *		- if it's an invalid IOT or OPR, then it's data.
 *		- if the instruction must have a valid EA, and it's invalid,
 *		   then it's data.
 *		- if the JMS (direct only) target is not zero or the same as the
 *		  address, then it's data
*/

static void
pass1 (void)
{
	int		addr, eff;

	for (addr = 0; addr < CORE_SIZE; addr++) {
		// skip unused
		if (core [addr] == -1) {
			continue;
		}
		// handle IOTs
		if ((core [addr] & 07000) == 06000) {
			// if we can't decode it, it's not valid
			if (!fetch_iot (core [addr], NULL, NULL)) {
				tags [addr] |= TAG_DATA;
			}
			continue;
		}
		// check OPRs; if they're invalid, tag as DATA, else skip
		if ((core [addr] & 07000) == 07000) {
			if (!valid_opr (core [addr])) {
				tags [addr] |= TAG_DATA;
				if (optv > 1) {
					printf ("+FLOW1 %04o %04o has invalid OPR -> DATA\n", addr, core [addr]);
				}
			}
			// done OPRs, skip
			continue;	// @@@ NOTE:  this does not work with EAE OPRs that take the next location as their parameter...
		}

		// ea() is always valid for opcodes < 06000
		eff = ea (addr, core [addr], NULL, NULL);

		// if the instruction should have a valid EA and doesn't...
		if (core [eff] == -1) {
			// then it's not an instruction
			tags [addr] |= TAG_DATA;
			if (optv > 1) {
				printf ("+FLOW1 %04o %04o has EA %04o (invalid or not in core) and OP < 6000 -> DATA\n", addr, core [addr], eff);
			}
			continue;
		}

		// if it's a plain JMS
		if ((core [addr] & 07400) == 04000) {
			// and the target isn't zero or it's not the same as the address
			if (core [eff] && core [eff] != eff) {
				tags [addr] |= TAG_DATA;
				if (optv > 1) {
					printf ("+FLOW1 %04o %04o JMS target not 0000 or ADDR (EA %04o is %04o)\n", addr, core [addr], eff, core [eff]);
				}
				continue;
			}
			// else, if it's ok, then the target is writable, after all (JMS drops the return address there)
			tags [eff] |= TAG_WRITABLE;
			if (optv > 1) {
				printf ("+FLOW1 %04o %04o JMS target is 0000 or ADDR, marking EA %04o as WRITABLE\n", addr, core [addr], eff);
			}
		}
	}
}

/*
 *	pass2
 *
 *	In this pass, we operate on the direct targets (no indirection)
 *	and mark the targets as data, variable, label, or subroutine
 *	target.
*/

static void
pass2 (void)
{
	int		addr;
	int		eff;

	for (addr = 0; addr < CORE_SIZE; addr++) {
		// skip unused or data locations
		if (core [addr] == -1 || (tags [addr] & TAG_DATA)) {
			continue;
		}

		eff = ea (addr, core [addr], NULL, NULL);

		switch (core [addr] & 07400) {		// check opcode
		case	00000:						// AND
		case	00400:						// AND I
		case	01000:						// TAD
		case	01400:						// TAD I
		case	02000:						// ISZ
		case	02400:						// ISZ I
		case	03000:						// DCA
		case	03400:						// DCA I
			tags [eff] |= TAG_DATA;			// the referenced EA is data
			if (optv > 1) {
				printf ("+FLOW2 %04o %04o is AND/TAD/ISZ/DCA's EA %04o tagged as DATA\n", addr, core [addr], eff);
			}

			// mark as writable if someone writes to it directly (ISZ and DCA only)
			if ((core [addr] & 07400) == 02000 || (core [addr] & 07400) == 03000) {
				tags [eff] |= TAG_WRITABLE;
				if (optv > 1) {
					printf ("+FLOW2 %04o %04o is ISZ/DCA so EA %04o is WRITABLE\n", addr, core [addr], eff);
				}
			}
			break;

		case	04000:						// JMS
			if (core [eff] == 0 || core [eff] == eff) {	// first word of JMS target must be zero or the address itself
				tags [eff] |= TAG_SUB;		// otherwise, it's a valid subroutine target
				if (optv > 1) {
					printf ("+FLOW2 %04o %04o is JMS with good target %04o content (%04o) so tagged as SUB\n", addr, core [addr], eff, core [eff]);
				}
			} else {
				tags [addr] |= TAG_DATA;	// then invalidate this "instruction", it's bogus
				if (optv > 1) {
					printf ("+FLOW2 %04o %04o is JMS with bad target %04o content (%04o) so tagged as DATA\n", addr, core [addr], eff, core [eff]);
				}
			}
			break;

		case	05000:						// JMP
			tags [eff] |= TAG_LABEL;
			if (optv > 1) {
				printf ("+FLOW2 %04o %04o is JMP so EA %04o is LABEL\n", addr, core [addr], eff);
			}
			break;

			break;

		// JMS I, JMP I, IOTs, and OPRs are not handled in this pass
		}
	}
}

/*
 *	pass3
 *
 *	In this pass, we verify and mark the indirects
*/

static void
pass3 (void)
{
	int		addr;
	int		eff;

	for (addr = 0; addr < CORE_SIZE; addr++) {
		// skip unused, data, or non-indirect opcodes
		if (core [addr] == -1 || (tags [addr] & TAG_DATA) || core [addr] >= 06000 || !(core [addr] & 00400)) {
			if (optv > 2 && core [addr] != -1) {
				printf ("+FLOW3 %04o %04o tags 0x%04x skipped\n", addr, core [addr], tags [addr]);
			}
			continue;
		}

		eff = ea (addr, core [addr], NULL, NULL);

		switch (core [addr] & 07000) {		// check opcode (indirectness assured above)
		case	00000:						// AND I
		case	01000:						// TAD I
		case	02000:						// ISZ I
		case	03000:						// DCA I
			if (core [eff] != -1 && !(tags [eff] & TAG_WRITABLE)) {	// if it's valid and constant
				tags [core [eff]] |= TAG_DATA;	// then the target is data
				if (optv > 1) {
					printf ("+FLOW3 %04o %04o is AND/TAD/ISZ/DCA I through constant EA %04o so target %04o is DATA\n", addr, core [addr], eff, core [eff]);
				}

				// mark as writable if someone writes to it (ISZ and DCA only)
				if ((core [addr] & 07000) == 02000 || (core [addr] & 07000) == 03000) {
					tags [core [eff]] |= TAG_WRITABLE;
					if (optv > 1) {
						printf ("+FLOW3 %04o %04o is ISZ/DCA I thorugh constant EA %04o so target %04o is WRITABLE\n", addr, core [addr], eff, core [eff]);
					}
				}
			}
			break;

		case	04000:						// JMS I
			if (core [eff] != -1 && !(tags [eff] & TAG_WRITABLE)) {	// if it's valid and constant
				if (core [core [eff]] == 0 || core [core [eff]] == core [eff]) {	// valid first word of JMS I target
					tags [core [eff]] |= TAG_SUB;	// ultimate target is a valid subroutine target
					tags [eff] |= TAG_DATA;			// and the pointer is a valid data type
					tags [eff] |= TAG_INDIRECTFC;	// and the pointer is used in an indirect flow control target
					if (optv > 1) {
						printf ("+FLOW3 %04o %04o is JMS I through constant EA %04o so target %04o (content %04o) is ok, so tagged as SUB\n", addr, core [addr], eff, core [eff], core [core [eff]]);
					}
				} else {
					tags [addr] |= TAG_DATA;	// then this isn't a valid instruction
					if (optv > 1) {
						printf ("+FLOW3 %04o %04o is JMS I through constant EA %04o with target %04o (invalid content %04o), so tagged as DATA\n", addr, core [addr], eff, core [eff], core [core [eff]]);
					}
				}
			}
			break;

		case	05000:						// JMP I
			if (core [eff] != -1) {				// if it's valid
				if (!(tags [eff] & TAG_WRITABLE)) {	// and constant
					tags [eff] |= TAG_DATA;			// pointer is a valid data type
					tags [eff] |= TAG_INDIRECTFC;	// and the pointer is used in an indirect flow control target
					tags [core [eff]] |= TAG_LABEL;	// ultimate target is a valid JMP target
					if (optv > 1) {
						printf ("+FLOW3 %04o %04o is JMP I through constant EA %04o with valid target %04o, so EA tagged as DATA | INDIRECTFC and target data %04o tagged as LABEL\n", addr, core [addr], eff, core [eff], core [core [eff]]);
					}
				}
			} else {
				tags [addr] |= TAG_DATA;	// else, it's not really a valid constant expression
				if (optv > 1) {
					printf ("+FLOW3 %04o %04o is JMP I through constant EA %04o with invalid target %04o, so tagged as DATA\n", addr, core [addr], eff, core [eff]);
				}
			}
			break;
		}
	}
}

/*
 *	pass4
 *
 *	In this pass, we update the constant map (konstmap).
 *
 *	This effectively converts Cxxxx to Kxxxx, and results in more human-
 *	friendly code.  So, instead of:
 *
 *		C1234, 0777
 *
 *	you'd see:
 *
 *		K0777, 0777
 *
 *	The only trick to this is that since symbols must be unique in 6
 *	characters (for PAL compatibility), we need to ensure that the
 *	items that convert from Cxxxx to Kxxxx are unique.  That's why
 *	konstmap[] has the values 0, 1, and 2:
 *
 *		0 == constant is not used anywhere in the code
 *		1 == constant is defined exactly once (can be converted)
 *		2 == constant has been defined more than once (cannot be converted)
 *
 *	Any constant that's used in an indirect flow control manner, however,
 *	is not a candidate, because technically it's not used as a K-style
 *	constant.
*/

static void
pass4 (void)
{
	int		i;

	memset (konstmap, 0, sizeof (konstmap));

	// populate konstant[] map
	for (i = 0; i < CORE_SIZE; i++) {
		if (core [i] == -1) {
			continue;
		}

		if (tags [i] & TAG_WRITABLE) {
			if (optv > 1) {
				printf ("+FLOW4 %04o %04o TAG %02X is writable, therefore, not a constant\n", i, core [i], tags [i]);
			}
			continue;
		}

		if (optv > 1) {
			printf ("+FLOW4 %04o %04o TAG %02X\n", i, core [i], tags [i]);
		}
		if ((tags [i] & TAG_DATA) && !(tags [i] & TAG_INDIRECTFC)) {
			switch (konstmap [core [i]]) {
			case	0:
				if (optv > 1) {
					printf ("+FLOW4 %04o %04o FRESH KONSTANT\n", i, core [i]);
				}
				konstmap [core [i]]++;		// this is our first one, bump the counter
				break;
			case	1:
				if (optv > 1) {
					printf ("+FLOW4 %04o %04o NO LONGER UNIQUE KONSTANT\n", i, core [i]);
				}
				konstmap [core [i]]++;		// this is our second one, go to "2"
				break;
			case	2:
				if (optv > 1) {
					printf ("+FLOW4 %04o %04o PROMISCUOUS CONSTANT\n", i, core [i]);
				}
				// do nothing, we're at "2" indicating "non-unique"
				break;
			}
		}
	}

	// analyze konstant[] map
	for (i = 0; i < CORE_SIZE; i++) {
		if (core [i] == -1) {
			continue;
		}
		if (tags [i] & TAG_WRITABLE) {
			if (optv > 1) {
				printf ("+FLOW4 %04o %04o TAG %02X is writable, therefore, not a constant\n", i, core [i], tags [i]);
			}
			continue;
		}

		if (optv > 1) {
			printf ("+FLOW4 %04o %04o TESTING (%02X)\n", i, core [i], tags [i]);
		}
		if (tags [i] & TAG_DATA) {
			if (optv > 1) {
				printf ("+FLOW4 %04o %04o TESTING...DATA\n", i, core [i]);
			}
			if (konstmap [core [i]] == 1) {	// if it's unique
				tags [i] |= TAG_KONSTANT;	// then go ahead and tag it
				if (optv > 1) {
					printf ("+FLOW4 %04o %04o TAGGED AS KONSTANT\n", i, core [i]);
				}
			}
		}
	}
}

/*
 *	verify_subroutines
 *
 *	This is used to verify that a target really is a subroutine.
 *	Verification consists of ensuring that somewhere within the
 *	same page is a JMP I through the return address.  If not,
 *	then the subroutine is bogus, because you can't return from
 *	it, so we knock down the "TAG_SUB" flag.
 *
 *	BUG:  This misses the following case:
 *
 *		*0
 *		0 /return area
 *		*4000
 *		jmp i 0	/ return through zero page
 *
 *	because we only search for returns within the page that the
 *	subroutine definition is in.  I don't think this is a major
 *	problem, just something to be aware of.  Plus, the TAG_RETURN
 *	is *really* only used as a comment field indicator anyway.
*/

static void
verify_subroutines (void)
{
	int		addr;
	int		page;
	int		found;

	for (addr = 0; addr < CORE_SIZE; addr++) {
		if (!(tags [addr] & TAG_SUB)) {
			continue;
		}

		// try and find returns within page
		found = 0;
		for (page = addr; page <= (addr | 00177); page++) {
			if ((core [page] & 07400) == 05400) {
				if (ea (page, core [page], NULL, NULL) == addr) {	// JMP I <start of subroutine> found!
					tags [page] |= TAG_RETURN;	// mark the returns
					found++;
				}
			}
		}
		if (!found) {
			tags [addr] &= ~TAG_SUB;			// not a subroutine, no return
		}
	}
}

static int
valid_opr (int opr)
{
	// a valid OPR must be 07xxx
	if ((opr & 07000) != 07000) {
		return (0);
	}

	if ((opr & 07400) == 07000) {						// group 1
		if ((opr & 00014) == 00014) {					// with both L and R rotate bits on
			return (0);
		}
	} else if ((opr & 07401) == 07400) {				// group 2
		// all ok
	} else if ((opr & 07401) == 07401) {				// EAE
		// if bits 6, 8, 9, or 10 are set...
		if (opr & 00056) {
			return (0);										// @@@ we're disabling EAE for now
		}
		// otherwise it's an "MQ microinstruction", which is ok
	}
	return (1);
}

Added src/d8tape/iot.h.


































































































































































































































































































































































































































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

/*
 *	iot.h
 *
 *	(C) Copyright 2003 by Robert Krten, all rights reserved.
 *	Please see the LICENSE file for more information.
 *
 *	This module contains the IOT decode table.
 *
 *	2003 12 16	R. Krten		created
*/

/*
 *	Conflicting decodes for IOTs
 *
 *	6050 - 6077		VC8I or KV8I; one only
 *	6440 - 6457		PT08 uses some of the data areas from the Data Communications System 680/I -- definition of PT08 is optional
 *	6530 - 6547		AF01A or AF04A; one only
 *	6551			used by AA01A, AA05, or not used.  Define zero or one only.
 *	6571			used by AF04A, AC01A, or not used.  Define zero or one only.
 *	6600 - 6667		used by DP01AA, overrides DF32/RF08 selection.  If defining DP01AA, don't bother with DF32/RF08
 *	6600 - 6627		used by DF32 and RF08, define one only.
 *	6640 - 6647		used by RF08 or not used, define RF08 or not.
 *	6700 - 6727		used by TC58, TR02, and TA8A, define one only
*/

// Select one of the following:
#define	VC8I
//#define	KV8I

// Select PT08 if required; it will override some of the IOTs from the Data Communications System 680/I
//#define PT08

// Select one of the following:
#define	AF01A
//#define	AF04A

// Select one of the following:
#define	AA01A
//#define AA05

// If selecting DP01AA, don't bother with DF32/RF08
//#define	DP01AA
#ifndef	DP01AA
// Select one of the following:
#define	DF32
// #define RF08
#endif	// DP01AA

// Select one of the following:
#define	TA8A
//#define TC58
//#define TR02

typedef struct
{
	int		code;				// code number, e.g. 06001
	char	*option;			// hardware option, e.g., vc8i
	char	*mnemonic;			// decoded value, e.g., "ION"
	char	*comment;			// code description of IOT, e.g., "clear x coordinate buffer"
}	one_iot_t;

#define	OP_ALL		""
one_iot_t iots [] =
{
	// 6000			Interrupts
{	06001,		OP_ALL,		"ION",		"Enable Interrupts"},
{	06002,		OP_ALL,		"IOF",		"Disable Interrupts"},
	// 6010			High Speed Perforated Tape Reader and Control
{	06011,		OP_ALL,		"RSF",		"Skip if reader flag is a 1."},
{	06012,		OP_ALL,		"RFB",		"Read the content of the reader buffer and clear the reader flag. This instructions does not clear the AC.  RB v AC4-11 -> AC4-11"},
{	06014,		OP_ALL,		"RFC",		"Clear reader flag and reader buffer, fetch one character from tape and load it into the reader buffer, and set the reader flag when done."},
	// 6020			High Speed Perforated Tape Punch and Control
{	06021,		OP_ALL,		"PSF",		"Skip if punch flag is a 1"},
{	06022,		OP_ALL,		"PCF",		"Clear punch flag and punch buffer."},
{	06024,		OP_ALL,		"PPC",		"Load the punch buffer from bits 4 through 11 of the AC and punch the character.  This instructions does not clear the punch flag or punch buffer.  AC4-11 v PB -> PB"},
{	06026,		OP_ALL,		"PLS",		"Clear the punch flag, clear the bunch buffer, load the punch buffer from the content of bits 4 through 11 of the accumulator, punch the character, and set the punch flag to 1 when done."},
	// 6030			Teletype Keyboard / Reader
{	06031,		OP_ALL,		"KSF",		"Skip if keyboard flag is a 1."},
{	06032,		OP_ALL,		"KCC",		"Clear AC and clear keyboard flag."},
{	06034,		OP_ALL,		"KRS",		"Read keyboard buffer static. This is a static command in that neither the AC nor the keyboard flag is cleared.  TTI v AC4-11 -> AC4-11"},
{	06036,		OP_ALL,		"KRB",		"Clear AC, clear keyboard flag, and read the content of the keyboard buffer into the content of AC4-11."},
	// 6040			Teletype Teleprinter / Punch
{	06041,		OP_ALL,		"TSF",		"Skip if teleprinter flag is a 1."},
{	06042,		OP_ALL,		"TCF",		"Clear teleprinter flag."},
{	06044,		OP_ALL,		"TPC",		"Load the TTO from the content of AC4-11 and print and/or punch the character."},
{	06046,		OP_ALL,		"TLS",		"Load the TTO from the content of AC4-11, clear the teleprinter flag, and print and/or punch the character."},
#ifdef	VC8I
	// 6050			Oscilloscope Display Type VC8/I [VC8/L]
{	06051,		OP_ALL,		"DCX",		"Clear X coordinate buffer"},
{	06053,		OP_ALL,		"DXL",		"Clear and load X coordinate buffer.  AC2-11 -> YB"},
{	06054,		OP_ALL,		"DIX",		"Intensify the point defined by the content of the X and Y coordinate buffers."},
{	06057,		OP_ALL,		"DXS",		"Executes the combined functions of DXL followed by DIX"},
	// 6060			(continued)
{	06061,		OP_ALL,		"DCY",		"Clear Y coordinate buffer"},
{	06063,		OP_ALL,		"DYL",		"Clear and load Y coordinate buffer.  AC2-11 -> YB"},
{	06064,		OP_ALL,		"DIY",		"Intensify the point defined by the content of the X and Y coordinate buffers."},
{	06067,		OP_ALL,		"DYS",		"Executes the combined functions of DYL followed by DIY"},
	// 6070			(continued)
{	06071,		OP_ALL,		"DSF",		"(Light Pen Type 370) Skip if display flag is a 1."},
{	06072,		OP_ALL,		"DCF",		"(Light Pen Type 370) Clear the display flag."},
{	06074,		OP_ALL,		"DSB",		"Zero brightness"},
{	06075,		OP_ALL,		"DSB",		"Set minimum brightness"},
{	06076,		OP_ALL,		"DSB",		"Set medium brightness"},
{	06077,		OP_ALL,		"DSB",		"Set maximum brightness"},
#endif
#ifdef	KV8I
	// 6050			Storage Tube Display Control, Type KV8/I [KV8/L]
{	06051,		OP_ALL,		"SNC",		"Senses the condition of the cursor interrupt flag.  The flag produces an interrupt request when set by operation of the interrupt pushbutton on the joystick.  The flag is initially cleared when the computer is started.  As with all flag-sent instructions, SNC can be used under interrupt conditions to detect the source of the interrupt, or it can be used under interrupt on (ION) when the interrupt request has been caused by the operation of thecursor interrupt button.  In a program running with the interrupt off, SNC can be used to ignore the cursor successive approximation subroutine in the program if a request for service has not been made from the joystick controller."},
{	06052,		OP_ALL,		"CCF",		"This instruction is used to clear the cursor flag after a request for service has been acknowledged by the program."},
	// 6060			(continued)
{	06062,		OP_ALL,		"SAC",		"The analog comparator is set to compare the analog content of any one of six analog sources with the content of the digital-to-analog converter.  The analog sources are chosen according to a 3-bit binary code.  This code establishes the parameter for choosing the wanted register according to the content of AC2, AC3, and AC6."},
{	06063,		OP_ALL,		"LDF",		"This instruction is used to establish the mode in which a wanted graphic is to be produced according to a 2-bit binary code.  This code determines whether the wanted vector will be linear absolute relative, whether the point plot mode will be used, or whether the cursor will be displayed.  This code establishes the paramteres for these formats according to the content of AC2 and AC3.  The LDF instruction must precede the LDX and LDY instructions."},
{	06064,		OP_ALL,		"LDX",		"The X-axis sample and hold register is loaded with the binary equivalent of the X-axis coordinate according to the contents of AC2-11.  This data appears at the output of the digital-to-analog converter as the analog equivalent of the X-axis value of the binary word stored in the AC.  The LDX instruction clears an existing ready flag and sets the ready flag after 100 +/- 20 us."},
{	06065,		OP_ALL,		"LDY",		"The Y-axis sample and hold register is loaded with the binary equivalent of the Y-axis coordinate according to the contents of AC2-11.  This data appears at the output of the digital-to-analog converter as the analog equivalent of the binary word in the AC.  The LDY instruction clears an existing ready flag and sets the ready flag after 100 +/- 20 us."},
{	06066,		OP_ALL,		"EXC",		"Used to execute the wanted vector according to the contents of AC2-4 and AC6-11.  The parameter word establishes long or short formats, circular vectors, display erasure, reset of the integrators, and intensification of the vector.  The EXC instruction clears an existing ready flag and sets the ready flag as follows:  a) after 20 +/- 5 us for a point or vector continue; b) after 250 us for short vectors; c) after 4.05 ms for long vectors; d) after 500 ms for an erase."},
	// 6070			(continued)
{	06071,		OP_ALL,		"SRF",		"Used to determine when the controller is ready to perform the next execute instruction.  The ready flag produces an interrupt condition when set.  The flag can be set by pressing the erase pushbutton on the VT01 unit.  Normally, however, the state of this flag is determined by the controller.  This flag is initially cleared when the computer is started and prior to an LDX, LDY, or EXC instruction."},
{	06072,		OP_ALL,		"CRF",		"This instruction clears the ready flag after a skip instruction has been acknowledged."},
{	06073,		OP_ALL,		"SDA",		"Used in the successive approximation subroutine to determine the digital equivalent of the selected analog holding register.  This instruction is used with the SAC (6062) instruction."},
{	06074,		OP_ALL,		"LDA",		"This instruction is used to load the content of AC2-11.  This instruction is used with DSA (6073) in the successive approximation subroutine to determine the digital value of the content of the selected analog holding register.  Does not change flag states."},
#endif
	// 6100			Memory Parity Type MP8/I [MP8/L]
	// 6100			Automatic Restart Type KP8/I [KP8/L]
{	06101,		OP_ALL,		"SMP",		"(MP8/I) Skip if memory parity error flag = 0."},
{	06102,		OP_ALL,		"SPL",		"(KP8/I) Skip if power low"},
{	06104,		OP_ALL,		"CMP",		"(MP8/I) Clear memory parity error flag."},
	// 6110			Multiple Asynchronous Serial Line Interface Unit, Type DC02D
{	06111,		OP_ALL,		"MKSF",		"Skip the next instruction if the keyboard flag is set."},
{	06112,		OP_ALL,		"MKCC",		"Clear the keyboard and reader flags; clear the AC."},
{	06113,		OP_ALL,		"MTPF",		"(DC02A) Transfer status of teleprinter flags to AC 0-3."},
{	06114,		OP_ALL,		"MKRS",		"Transfer the shift register contents to AC 4-11."},
{	06115,		OP_ALL,		"MINT",		"(DC02A) Interrupt on if AC 11 is set (interrupt request, if any flags)."},
{	06116,		OP_ALL,		"MKRB",		"Clear the keyboard and reader flags, clear the AC, trasnfer the shift register contents to AC 4-11 (MKCC and MKRS combined)."},
{	06117,		OP_ALL,		"MTON",		"Transfer AC0-3 to selection register (SELF) (select stations when bit is set)."},
	// 6120			Multiple Asynchronous Line Unit, Type DC02A
{	06121,		OP_ALL,		"MTSF",		"Skip the next instruction if the teleprinter flag is set."},
{	06122,		OP_ALL,		"MTCF",		"Clear the teleprinter flag."},
{	06123,		OP_ALL,		"MTKF",		"Transfer status of keyboard flags to AC 0-3."},
{	06124,		OP_ALL,		"MTPC",		"Load AC4-11 into the shift register (begin print/punch)."},
{	06125,		OP_ALL,		"MINS",		"Skip if the interrupt request is active (if interrupt is on and any flag is raised)."},
{	06126,		OP_ALL,		"MTLS",		"Clear the teleprinter flag and load AC4-11 into the shift register (MTCF and MTPC combined)"},
{	06127,		OP_ALL,		"MTRS",		"Trasnfer the status of the selection register to AC 0-3."},
	// 6130			Real Time Clock, Type KW8/I [KW8/L]
{	06132,		OP_ALL,		"CCFF",		"The flag, flag buffer, clock enable, and interrupt enable flip-flops are cleared.  This disables the real-time clock."},
{	06133,		OP_ALL,		"CSCF",		"When the flag flip-flop has been set by a clock pulse, the flag buffer flip-flop is set to a 1.  Upon execution of this instruction, an IO BUS IN SKIP is generated if the flag is set.  The content of the PC is incremented by 1, so that the next sequential instruction is skipped.  The flag flip-flop is then cleared.  If the flag flip-flop has not been set, no skip is generated nor is the flag flip-flop cleared."},
{	06134,		OP_ALL,		"CRCA",		"The output buffer is gated to the I/O BUS during IOP4, and a CLK AC CLR signal generated.  This register contains the last count in the count register.  The transfer from the count register is synchronized with this instruction so that a transfer that would occur during this instruction is not made."},
{	06136,		OP_ALL,		"CCEC",		"All clock control flip-flops are first cleared, then the clock enable flip-flop is set.  For the variable frequency clock, the frequency source is enabled synchronously with program operation.  With all clocks, the data input to the flag is enabled after IOP2 time.  This represents an 800-ns mask, after the clock is enabled."},
{	06137,		OP_ALL,		"CECI",		"All clock control flip-flops are cleared, then the clock enable, and interrupt enable flip-flops are set.  The clock enable flip-flop is described with the CCEC instruction.  The interrupt enable flip-flop allows an IO BUS IN INT signal when the flag is set."},
	// 6140
	// 6150
	// 6160
	// 6170
	// 6200 through 6277	Memory Extension Control Type MC8/I [MC8/L]
{	06201,		OP_ALL,		"CDF0",		"Change to data field 0. The data field register is loaded with the selected field number (0).  All subsequent memory requests for operands are automatically switched to that data field until the data field number is changed by a new CDF command."},
{	06202,		OP_ALL,		"CIF0",		"Prepare to change to instruction field N (0).  The instruction buffer register is loaded with the selected field number (0).  The next JMP or JMS instruction causes the new field to be entered."},
{	06204,		OP_ALL,		"CINT",		"(KT8/I) Clear user interrupt.  Resets the user interrupt (UINT) flip-flop to the 0 state."},
	// 6210
{	06211,		OP_ALL,		"CDF0",		"Change to data field 1. The data field register is loaded with the selected field number (1).  All subsequent memory requests for operands are automatically switched to that data field until the data field number is changed by a new CDF command."},
{	06212,		OP_ALL,		"CIF0",		"Prepare to change to instruction field N (1).  The instruction buffer register is loaded with the selected field number (1).  The next JMP or JMS instruction causes the new field to be entered."},
{	06214,		OP_ALL,		"RDF",		"Read data field into AC6-8.  Bit 0-5 and 9-11 of the AC are not affected."},
	// 6220
{	06221,		OP_ALL,		"CDF0",		"Change to data field 2. The data field register is loaded with the selected field number (2).  All subsequent memory requests for operands are automatically switched to that data field until the data field number is changed by a new CDF command."},
{	06222,		OP_ALL,		"CIF0",		"Prepare to change to instruction field N (2).  The instruction buffer register is loaded with the selected field number (2).  The next JMP or JMS instruction causes the new field to be entered."},
{	06224,		OP_ALL,		"RIF",		"Same as RDF except reads the instruction field"},
	// 6230
{	06231,		OP_ALL,		"CDF0",		"Change to data field 3. The data field register is loaded with the selected field number (3).  All subsequent memory requests for operands are automatically switched to that data field until the data field number is changed by a new CDF command."},
{	06232,		OP_ALL,		"CIF0",		"Prepare to change to instruction field N (3).  The instruction buffer register is loaded with the selected field number (3).  The next JMP or JMS instruction causes the new field to be entered."},
{	06234,		OP_ALL,		"RIB",		"Read interrupt buffer.  The instruction field and data field stored during an interrupt are read into AC6-8 and AC9-11, respectively."},
	// 6240
{	06241,		OP_ALL,		"CDF0",		"Change to data field 4. The data field register is loaded with the selected field number (4).  All subsequent memory requests for operands are automatically switched to that data field until the data field number is changed by a new CDF command."},
{	06242,		OP_ALL,		"CIF0",		"Prepare to change to instruction field N (4).  The instruction buffer register is loaded with the selected field number (4).  The next JMP or JMS instruction causes the new field to be entered."},
{	06244,		OP_ALL,		"RMF",		"Restore memory field.  Used to exit from a program interrupt."},
	// 6250
{	06251,		OP_ALL,		"CDF0",		"Change to data field 5. The data field register is loaded with the selected field number (5).  All subsequent memory requests for operands are automatically switched to that data field until the data field number is changed by a new CDF command."},
{	06252,		OP_ALL,		"CIF0",		"Prepare to change to instruction field N (5).  The instruction buffer register is loaded with the selected field number (5).  The next JMP or JMS instruction causes the new field to be entered."},
{	06254,		OP_ALL,		"SINT",		"(KT8/I) Skip on user interrupt.  When the user interrupt (UINT) flip-flop is in the 1 state, sets the user skip flip-flop (USF) to the 1 state and causes the program to skip the next instruction."},
	// 6260
{	06261,		OP_ALL,		"CDF0",		"Change to data field 6. The data field register is loaded with the selected field number (6).  All subsequent memory requests for operands are automatically switched to that data field until the data field number is changed by a new CDF command."},
{	06262,		OP_ALL,		"CIF0",		"Prepare to change to instruction field N (6).  The instruction buffer register is loaded with the selected field number (6).  The next JMP or JMS instruction causes the new field to be entered."},
{	06264,		OP_ALL,		"CUF",		"(KT8/I) Clears the user flag.  Clears the user buffer (UB) flip-flop."},
	// 6270
{	06271,		OP_ALL,		"CDF0",		"Change to data field 7. The data field register is loaded with the selected field number (7).  All subsequent memory requests for operands are automatically switched to that data field until the data field number is changed by a new CDF command."},
{	06272,		OP_ALL,		"CIF0",		"Prepare to change to instruction field N (7).  The instruction buffer register is loaded with the selected field number (7).  The next JMP or JMS instruction causes the new field to be entered."},
{	06274,		OP_ALL,		"SUF",		"(KT8/I) Sets the user flag.  Sets user buffer (UB) and inhibits processor interrupts until the next JMP or JMS instruction.  Generation of IB -> IF during the next JMP or JMS instruction transfers the state of UB to the user field (UF) flip-flop."},
	// 6400			Data Communications System 680/I
{	06401,		OP_ALL,		"TTINCR",	"This instruction causes the contents of the line register to be incremented by 1.  This command, when microprogrammed with a TTO command is executed."},
{	06402,		OP_ALL,		"TTI",		"Causes a JMS to be executed (N+3) if the R register does not equal 0 and either the line hold bit of the selected line (specified by bits 2-8 of the LSW) is in the 1 state, or as a result of jamming the line state into and shifting the CAW; bit 11 of the CAW is a 1."},
{	06404,		OP_ALL,		"TTO",		"Clears the link and shifts the link and accumulator one bit position to the right.  Bit 11 of the accumulator is shifted into the line unit specified by the line register.  The previuos contents (1 bit) of the selected line unit is lost."},
	// 6410
{	06411,		OP_ALL,		"TTCL",		"The command sets the contents of the line register to 0."},
{	06412,		OP_ALL,		"TTSL",		"The contents of AC5-11 are ORed into the line register."},
{	06413,		OP_ALL,		"TTLL",		"The contents of AC5-11 are trasnferred into the line register.  This is a microprogram of TTCL and TTSL."},
{	06414,		OP_ALL,		"TTRL",		"The contents of the line register are ORed into AC5-11.  The AC must be 0 for a true transfer."},
	// 6420
{	06421,		OP_ALL,		"T1skip",	"(Data Communications System 680/I) Clock Control Instruction:  Causes the program to skip the next instruction if clock flag 1 is in the 1 state.  To clear the flag, either T1on or T1off can be used."},
{	06422,		OP_ALL,		"T1off",	"(Data Communications System 680/I) Clock Control Instruction:  Inhibits clock 1 from setting its flag.  This instruction also sets the flag to the 0 state."},
{	06424,		OP_ALL,		"T1on",		"(Data Communications System 680/I) Clock Control Instruction:  Enables clock 1 to set its flag at the predetermined lcock rate.  The flag in the 1 state causes a program interrupt when the interrupt is enabled.  This instruction also sets the flag to the 0 state."},
	// 6430
{	06431,		OP_ALL,		"T2skip",	"(Data Communications System 680/I) same as T1skip except for clock 2"},
{	06432,		OP_ALL,		"T2off",	"(Data Communications System 680/I) same as T1off except for clock 2"},
{	06434,		OP_ALL,		"T2on",		"(Data Communications System 680/I) same as T1on except for clock 2"},
#ifdef	PT08
	// 6440			Asynchronous Serial Line Interface, Type PT08
{	06441,		OP_ALL,		"TSFXXX",		"Skip if teleprinter/punch 3 flag is a 1."},
{	06442,		OP_ALL,		"TCFXXX",		"Clear teleprinter/punch 3 flag."},
{	06444,		OP_ALL,		"TPCXXX",		"Load teleprinter 3 buffer (TTOX) with AC4-11 and print/punch the character."},
{	06446,		OP_ALL,		"TLSXXX",		"Load TTOX with AC4-11, 3 flag, print/punch the character and clear teleprinter/punch."},
{		"6450"},
{	06451,		OP_ALL,		"KSFXXX",		"Skip if keyboard/reader 3 flag is a 1."},
{	06452,		OP_ALL,		"KCCXXX",		"Clear AC and keyboard/reader 3 flag."},
{	06454,		OP_ALL,		"KRSXXX",		"Read keyboard/reader 3 buffer (TTI3) static.  TTI3 is loaded into AC4-11 by an OR transfer."},
{	06456,		OP_ALL,		"KRBXXX",		"Clear the AC, read TTI3 into AC4-11, and clear keyboard 3 flag."},
{#else		"PT08"},
{	06441,		OP_ALL,		"T3skip",		"(Data Communications System 680/I) same as T1skip except for clock 3"},
{	06442,		OP_ALL,		"T3off",		"(Data Communications System 680/I) same as T1off except for clock 3"},
{	06444,		OP_ALL,		"T3on",		"(Data Communications System 680/I) same as T1on except for clock 3"},
{	06451,		OP_ALL,		"T4skip",		"(Data Communications System 680/I) same as T1skip except for clock 4"},
{	06452,		OP_ALL,		"T4off",		"(Data Communications System 680/I) same as T1off except for clock 4"},
{	06454,		OP_ALL,		"T4on",		"(Data Communications System 680/I) same as T1on except for clock 4"},
#endif
{	06461,		OP_ALL,		"TTRINC",		"(Data Communications System 680/I) This command causes the contents of the R register to be incremented by 1.  Because it is loaded with a 2's complement number, the result is a subtract.  This instruction can be microprogrammed with TTRR."},
{	06464,		OP_ALL,		"TTRR",		"(Data Communications System 680/I) This command reads the contents of the R register into AC7-11.  The contents of the AC must be 0s before issuing this instruction.  This instruction, when microprogrammed with TTINCR, causes the incremented results to be read into the AC."},
	// 6470
{	06471,		OP_ALL,		"TTCR",		"(Data Communications System 680/I) This command causes the R register to be set to 0"},
{	06472,		OP_ALL,		"TTLR",		"(Data Communications System 680/I) This command causes the contents of AC7-11 to be trasnferred into the R register."},
	// 6500			Incremental Plotter and Control Type VP8/I
{	06501,		OP_ALL,		"PLSF",		"Skip if plotter flag is a 1."},
{	06502,		OP_ALL,		"PLCF",		"Clear plotter flag."},
{	06504,		OP_ALL,		"PLPU",		"Plotter pen up.  Raise pen off of paper."},
	// 6510
{	06511,		OP_ALL,		"PLPR",		"Plotter pen right."},
{	06512,		OP_ALL,		"PLDU",		"Plotter drum (paper) upward"},
{	06514,		OP_ALL,		"PLDD",		"Plotter drum (paper) downward."},
	// 6520
{	06521,		OP_ALL,		"PLPL",		"Plotter pen left."},
{	06522,		OP_ALL,		"PLUD",		"Plotter drum (paper) upward. Same as 6512"},
{	06524,		OP_ALL,		"PLPD",		"Plotter pen down.  Lower pen on to paper."},
#ifdef	AF01A
	// 6530			General Purpose Converter and Multiplexer Control Type AF01A (this option is mutually exclusive with AF04A)
{	06531,		OP_ALL,		"ADSE",		"Skip if A/D converter flag is a 1."},
{	06532,		OP_ALL,		"ADCV",		"Clear A/D converter flag and convert input voltage to a digital number, flag will set to 1 at end of conversion.  Number of bits in converted number determined by switch setting, 11 bits maximum."},
{	06534,		OP_ALL,		"ADRB",		"Read A/D converter buffer into AC, left justified, and clear flag."},
	// 6540
{	06541,		OP_ALL,		"ADCC",		"Clear multiplexer channel address register."},
{	06542,		OP_ALL,		"ADSC",		"Set up multiplexer channel as per AC6-11.  Maximum of 64 single ended or 32 differntial input channels."},
{	06544,		OP_ALL,		"ADIC",		"Index multiplexer channel address (present address + 1).  Upon reaching address limit, increment will cause channel 00 to be selected."},
#endif	// AF01A
#ifdef	AF04A
	// 6530			Guarded Scanning Digital Voltmeter Type AF04A (this option is mutually exclusive with AF01A)
{	06531,		OP_ALL,		"VSDR",		"Skip if data ready flag is a 1."},
{	06532,		OP_ALL,		"VRD",		"Selected byte of voltmeter is transferred to the accumulator and the data ready flag is cleared."},
{	06534,		OP_ALL,		"VBA",		"BYTE ADVANCE command requests next twelve bits, data ready flag is set."},
	// 6540
{	06541,		OP_ALL,		"VCNV",		"The contents of the accumulator are transferred to the AF04A channel address register.  Analog signal on selected channel is automatically digitized."},
{	06542,		OP_ALL,		"VSEL",		"The contents of the accumulator are transferred to the AF04A control register."},
{	06544,		OP_ALL,		"VINX",		"The last channel address is incremented by one and the analog signal on the selected channel is automatically digitized."},
#endif	// AF04A
	// 6550
#ifdef	AA01A
{	06551,		OP_ALL,		"DAL1",		"(AA01A) The character in the accumulator is loaded into the channel 1 buffer.  The DAC then converts the buffered value to the analog equivalent.  NOTE: Similar instructions for DAL2 and DAL3 load respective DACs."},
#else	// AA01A
#ifdef	AA05
{	06551,		OP_ALL,		"CLDA",		"The address register in the AA05/AA07 is cleared."},
#else	// AA05
#endif	// AA05
#endif	// AA01A
{	06552,		OP_ALL,		"LDAD",		"(AA05/AA07) The address register in the AA05/AA07 is loaded with the contents of AC0-5."},
	// 6560
{	06562,		OP_ALL,		"LDAR",		"(AA05/AA07) The buffer (input buffer, if the channel is double-buffered) of the DAC is loaded from AC0-9."},
{	06564,		OP_ALL,		"UPDT",		"(AA05/AA07) The contents of the input buffers of all double-buffered channels are trasnferred to their respective output buffers.  The input buffer is not affected by this instruction."},
	// 6570
#ifdef	AF04A
{	06571,		OP_ALL,		"VSCC",		"SAMPLE CURRENT CHANNEL when required to digitize analog signal on current channel repeatedly (AF04A)"},
#else	// AF04A
#ifdef	AC01A
{	06571,		OP_ALL,		"HRAN",		"The contents of AC3-5 are trasnferred to the channel address register (CHAR).  The 3-bit code is decoded to address any of the 8 channels."},
#else	// AC01A
#endif	// AC01A
#endif	// AF04A
#ifdef	AC01A
{	06572,		OP_ALL,		"HSIM",		"Simultaneously places all 8 channels into the hold mode."},
#else	// AC01A
#endif	// AC01A
{	06574,		OP_ALL,		"SAMP",		"(AA01A) Places all 8 channels into the sample (or track) mode."},
#ifdef	DP01AA
	// 6600			Synchronous Modem Interface, Type DP01AA
{	06601,		OP_ALL,		"TAC",		"Causes the contents of the AC (6, 7, 8, or 9 bits right-justified) to be transferred into the TB."},
{	06602,		OP_ALL,		"CTF",		"Resets the trasnmit flag.  If trasnmit active flag is set, CTF also causes the program to skip the next instruction."},
{	06604,		OP_ALL,		"CIM",		"Resets the transmit logic idle mode (IM) flip-flop."},
{	06611,		OP_ALL,		"STF",		"Causes the program to skip the next instruction if the transmit flag is in the 0 state.  When the transmit flag is in the 1 state, the trasnmit buffer register (TB) is ready to accept another character."},
{	06612,		OP_ALL,		"RRB",		"Transfers the contents of the receiver buffer (RB) (6, 7, 8, or 9 bits, right-justified) to the computer AC.  RRB also resets the receive flag."},
{	06614,		OP_ALL,		"SIM",		"Sets the transmit idle mode (IM) flip-flop."},
{	06621,		OP_ALL,		"SEF",		"Causes the program to skip the next instruction if the receive end flag is 0.  The receive end flag slip-flop is set when the receive logic has stopped receiving serial data from the communications equipment due to termination of th SERIAL CLOCK RECEIVE pulse train."},
{	06622,		OP_ALL,		"CEF",		"Clears the receive end flag"},
{	06624,		OP_ALL,		"SRE",		"Sets the ring enable (RE) flip-flop to a 1, which permits the ring flag to request a program interrupt."},
{	06631,		OP_ALL,		"SRI",		"Causes the program to skip the next instruction if the ring flag is 0.  The ring flag is set when a ring input is received."},
{	06632,		OP_ALL,		"CRF",		"Clears the ring flag."},
{	06634,		OP_ALL,		"STR",		"Sets the terminal read (TR) flip-flop to the 1 state.  This causes the terminal ready lead to the modem to be set on the ON state.  The state changes to OFF for CTR"},
{	06641,		OP_ALL,		"SSR",		"Causes the program to skip the next instruction if the data-set-ready lead from the modem is in the ON state."},
{	06642,		OP_ALL,		"CTR",		"Clears the terminal ready (TR) flip-flop (see STR)"},
{	06644,		OP_ALL,		"CRE",		"Clears the ring enable (RE) flip-flop."},
{	06651,		OP_ALL,		"SRF",		"Causes the program to skip the next instruction if the receive flag is 0.  The flag is set when a received character is ready for trasnfer to the AC and the flag is cleared when an RRB instruction is issued."},
{	06652,		OP_ALL,		"CRA",		"Clears the receive active (RA) flip-flop, taking the receive logic out of the active state.  This inhibits any more receive flags until a new sync character is received."},
{	06654,		OP_ALL,		"XOB",		"Causes an exclusive OR of the AC with the buffer register (BR)."},
{	06661,		OP_ALL,		"COB",		"Clears the XOR buffer."},
{	06662,		OP_ALL,		"ROB",		"Transfers the buffer register (BR) content to the AC."},
{	06664,		OP_ALL,		"IOB",		"Transfers 1s from the AC to the buffer register (BR)."},
#else	// DP01AA
#ifdef	DF32
	// 6600			Random Access Disc File (type DF32)
{	06601,		OP_ALL,		"DCMA",		"Clears memory address register, parity erorr and completion flags.  This instruction clears the disk memory request flag and interrupt flags."},
{	06603,		OP_ALL,		"DMAR",		"The contents of the AC are loaded into the disk memory address register and the AC is cleared.  Begin to read information from the disk into the specified core location.  Clears parity error and completion flags.  Clears interrupt flags."},
{	06605,		OP_ALL,		"DMAW",		"The contents of the AC are loaded into the disk memory address register and the AC is cleared.  Begin to write information into the disk from the specified core location.  Clears parity error and completion flags."},
	// 6610
{	06611,		OP_ALL,		"DCEA",		"Clears the disk extended address and memory address extension register."},
{	06612,		OP_ALL,		"DSAC",		"Skips next instruction if address confirmed flag is a 1.  AC is cleared."},
{	06615,		OP_ALL,		"DEAL",		"The disk extended-address extension registers are cleared and loaded with the track data held in the AC."},
{	06616,		OP_ALL,		"DEAC",		"Clear the AC then loads the contents of the disk extended-address register into the AC to allow program evaluation.  Skip next instruction if address confirmed flag is a 1."},
	// 6620
{	06621,		OP_ALL,		"DFSE",		"Skip next instruction if the completion flag is a 1.  Indicates data transfer is complete."},
{	06626,		OP_ALL,		"DMAC",		"Clear the AC then loads contents of disk memory address register into the AC to allow program evaluation."},
#endif	// DF32
#ifdef	RF08
	// 6600			Disk File and Control, Type RF08/Expander Disk File, Type RS08
	// 6610
{	06611,		OP_ALL,		"DCIM",		"Clear the disk interrupt enable and core memory address extension register."},
{	06615,		OP_ALL,		"DIML",		"Clear the interrupt enable and memory address extension register, then load the interrupt enable and memory address extension registers with data held in the AC.  Then clear the AC.  NOTE: Transfers cannot occur across memory fields.  Attempts to do so will cause the transfer to "wrap around" within the specified memory field."},
{	06616,		OP_ALL,		"DIMA",		"Clear the AC.  Then load the contents of the status register (STR) into the AC to allow program evaluation."},
	// 6620
{	06621,		OP_ALL,		"DFSE",		"Skip next instruction if there is a parity error, data request late, write lock status, or nonexistent disk flag set."},
{	06623,		OP_ALL,		"DISK",		"If either the error or data completion flag (or both) is set, the next instruction is skipped."},
#endif	// RF08
	// 6630			Card Reader and Control Type CR8/I [CR8/L] (see also 6670)
{	06631,		OP_ALL,		"RCSF",		"Generates an IOP pulse (IOP 1) to test the data-ready flag output.  If the data ready flag is 1, the next sequential program instruction is skipped."},
{	06632,		OP_ALL,		"RCRA",		"Generates an IOP pulse (IOP 2) to read the alphanumeric data at the control-logic buffer register and clear the data ready flag."},
{	06634,		OP_ALL,		"RCRB",		"Generates an IOP pulse (IOP 4) to read the BCD data at the control logic buffer register and clear the data ready flag."},
#ifdef	RF08
	// 6640
{	06641,		OP_ALL,		"DCXA",		"Clear the high order 8-bits of the disk address register (DAR)."},
{	06643,		OP_ALL,		"DXAL",		"Clear the high order 8 bits of the DAR.  Then load the DAR from data stored in the AC.  Then clear the AC."},
{	06645,		OP_ALL,		"DXAC",		"Clear the AC; then load the contents of the high order 8-bit DAR into the AC."},
{	06646,		OP_ALL,		"DMMT",		"For maintenance purposes only with the appropriate maintenance cable connections and the disk disconnected from the RS08 logic, the (given) standard signals may be generated by IOT 6646 and associated AC bits.  The AC is cleared and the maintenance register is initiated by issuing an IOT 6601 command."},
#else	// RF08
	// 6640
#endif	// RF08
	// 6650			Automatic Line Printer and Control Type 645
{	06651,		OP_ALL,		"LSE",		"Skip if line printer error flag is a 1."},
{	06652,		OP_ALL,		"LCB",		"Clear both sections of the printing buffer."},
{	06654,		OP_ALL,		"LLB",		"Load printing buffer from the content of AC6-11 and clear the AC"},
	// 6660			Automatic Line Printer and Control Type 645 (continued)
{	06661,		OP_ALL,		"LSD",		"Skip if the printer done flag is a 1."},
{	06662,		OP_ALL,		"LCF",		"Clear line printer done and error flags."},
{	06664,		OP_ALL,		"LPR",		"Clear the format register, load the format register from the content of AC9-11, print the line contained in the section of the printer buffer loaded last, clear the AC, and advance the paper in accordance with the selected channel of the format tape if the content of AC8=1.  If the content of AC8=0, the line is printed and paper advance is inhibited."},
#endif	// DP01AA
	// 6670			Card Reader and Control Type CR8/I [CR8/L] (see also 6630)
{	06671,		OP_ALL,		"RCSD",		"Generates an IOP pulse (IOP 1) to test the card-done flag output.  If the card done flag is 1, the next sequential program instruction is skipped."},
{	06672,		OP_ALL,		"RCSE",		"Generates an IOP pulse (IOP 2) to advance the card, clear the card done flag, and produce a skip flag is reader is ready.  If skip flag is generated, the next sequential program instruction is skipped."},
{	06674,		OP_ALL,		"RCRD",		"Generates an IOP pulse (IOP 4) to clear the card done flag."},
#ifdef	TA8A
	// 6700 -> 6707	TU60 DECassette Controller TA8A
{	06700,		OP_ALL,		"KCLR",		"Clear All; clear the status A and B register"},
{	06701,		OP_ALL,		"KSDR",		"Skip the next instruction if the data flag is set during read or write operations"},
{	06702,		OP_ALL,		"KSEN",		"Skip the next instruction if any of the following are true:  a) tape is at EOT/BOT, b) the TU60 is not ready or the selected drive is empty"},
{	06703,		OP_ALL,		"KSBF",		"Skip the next instruction if the ready flag is set"},
{	06704,		OP_ALL,		"KLSA",		"Load status A from AC4-AC11, clear the AC, and load the complement of status A back into the AC"},
{	06705,		OP_ALL,		"KSAF",		"Skip on any flag or error condition"},
{	06706,		OP_ALL,		"KGOA",		"Assert the contents of the status A register and transfer data into the AC during a read operation or out of the AC to the Read/Write buffer during a write operation.  This instruction has three functions:  a) enables the command in the status A register to be executed by the TU60, b) for read operations, the first KGOA instruction causes the tape to start moving, and when the data flag sets, a second KGOA transfers the first byte from the read/write buffer to the AC.  The data flag sets after each 8-bit byte is read from the TU60. c) for write operations, the status A register is set up for a write, and the AC contains the first byte to be written on tape.  When the KGOA instruction is executed, the tape starts to move and the first byte is transferred to the TU60."},
{	06707,		OP_ALL,		"KRSB",		"Transfer the contents of the status B register into AC4-AC11."},
#endif
#ifdef	TC58
	// 6700			Automatic Magnetic Tape Control Type TC58
{	06701,		OP_ALL,		"MTSF",		"Skip on error flag or magnetic tape flag.  The status of the error flag (EF) and the magnetic tape flag (MTF) are sampled.  If either or both are set to 1, the content of the PC is incremented by one to skip the next sequential instruction."},
{	06702,		OP_ALL,		"6702",		"(no mnemonic assigned) Clear the accumulator."},
{	06704,		OP_ALL,		"6704",		"(no mnemonic assigned) Inclusively OR the contents of the status register into AC0-11"},
{	06706,		OP_ALL,		"MTRS",		"Read the contents of the status register into AC0-11."},
	// 6710
{	06711,		OP_ALL,		"MTCR",		"Skip on tape control ready (TCR).  If the tape control is ready to receive a command, the PC is incremented by one to skip the next sequential instruction."},
{	06712,		OP_ALL,		"MTAF",		"Clear the status and command registers, and the EF and MTF if tape control ready.  If tape control not ready, clears MTF and EF flags only."},
{	06714,		OP_ALL,		"MTCM",		"Inclusively OR the contents of AC0-5, AC9-11 into the command register; JAM transfer bits 6, 7, 8 (command function)"},
{	06716,		OP_ALL,		"MTLC",		"Load the contents of AC0-1j1 into the command register."},
	// 6720
{	06721,		OP_ALL,		"MTTR",		"Skip on tape transport ready (TTR).  The next sequential instruction is skipped if the tape transport is ready."},
{	06722,		OP_ALL,		"MTGO",		"Set "go" bit to execute command in the command register if command is legal."},
{	06724,		OP_ALL,		"MTRC",		"Inclusively OR the contents of the contents of the command register into AC0-11."},
#endif	// TC58
#ifdef	TR02
	// 6700			Incremental Magnetic Tape Controller, Type TR02
{	06701,		OP_ALL,		"IRS",		"When data is ready to be strobed into the AC from the read buffer (RB), the PC is incremented by one to skip the next sequential instruction.  The read done flag is cleared only if the skip occurs."},
{	06702,		OP_ALL,		"ISR",		"The content of the status register (STR) is read into AC0-8.  The AC should be cleared before it is read by this instruction."},
{	06703,		OP_ALL,		"IWS",		"If the write done flag is set, the next instruction is skipped and the write done flag is cleared."},
{	06704,		OP_ALL,		"IMC",		"The move command decoded from AC0-2 is generated.  This instruction also clears the read done, write done, and gap detect flags.  The indicated flag is set when the command has been executed."},
{	06705,		OP_ALL,		"IGS",		"If the gap detect flag is set, the next instruction is skipped and the gap detect flag is cleared."},
{	06706,		OP_ALL,		"IWR",		"The contents of the AC are loaded into the tape input data buffer (WB) and a write step command is generated.  The write done flag is set when writing is completed."},
{	06707,		OP_ALL,		"IRD",		"The AC is cleared and the content of the read buffer (RB) is loaded into the AC.  Data bits are transferred into AC6-11 (7-track) or AC4-11 (9-track).  Parity error is transferred into AC0 which is 0 if there is no parity error."},
	// 6710
{	06711,		OP_ALL,		"IRSA",		"When data is ready to be strobed into the AC from the read buffer (RB), the PC is incremented by one to skip the next sequential instruction.  The read done flag is cleared only if the skip occurs."},
{	06712,		OP_ALL,		"ISRA",		"The content of the status register (STR) is read into AC0-8.  The AC should be cleared before it is read by this instruction."},
{	06713,		OP_ALL,		"IWSA",		"If the write done flag is set, the next instruction is skipped and the write done flag is cleared."},
{	06714,		OP_ALL,		"IMCA",		"The move command decoded from AC0-2 is generated.  This instruction also clears the read done, write done, and gap detect flags.  The indicated flag is set when the command has been executed."},
{	06715,		OP_ALL,		"IGSA",		"If the gap detect flag is set, the next instruction is skipped and the gap detect flag is cleared."},
{	06716,		OP_ALL,		"IWRA",		"The contents of the AC are loaded into the tape input data buffer (WB) and a write step command is generated.  The write done flag is set when writing is completed."},
{	06717,		OP_ALL,		"IRDA",		"The AC is cleared and the content of the read buffer (RB) is loaded into the AC.  Data bits are transferred into AC6-11 (7-track) or AC4-11 (9-track).  Parity error is transferred into AC0 which is 0 if there is no parity error."},
	// 6720
#endif	// TC58
	// 6730
	// 6740
	// 6750
	// 6760			DECtape Transport Type TU55 and DECtape Control Type TC01
{	06761,		OP_ALL,		"DTRA",		"The content of status register A is read into AC0-9 by an OR transfer.  The bit assignments are:  AC0-2 = Transport unit select numnber; AC3-4 = Motion; AC5 = Mode; AC6-8 = Function; AC9 = Enable/disable DECtape control flag."},
{	06762,		OP_ALL,		"DCTA",		"Clear status register A.  All flags undisturbed."},
{	06764,		OP_ALL,		"DTXA",		"Status register A is loaded by an exclusive OR transfer from the content of the AC, and AC10 and AC11 are sampled.  If AC10 = 0, the error flags are cleared.  If AC11 = 0, the DECtape control flag is cleared."},
	// 6770
{	06771,		OP_ALL,		"DTSF",		"Skip if error flag is a 1 or if DECtape control flag is a 1."},
{	06772,		OP_ALL,		"DTRB",		"The content of status register B is read into the AC by an OR transfer.  The bit assignments are:  AC0 = Error flag; AC1 = Mark track error; AC2 = End of tape ; AC3 = Select error ; AC4 = Parity error; AC5 = Timing error; AC6-8 = Memory field; AC9-10 = Unused; AC11 = DECtape flag."},
{	06774,		OP_ALL,		"DTLB",		"The memory field portion of status register B is loaded from the content of AC6-8."},
};

Added src/d8tape/main.c.
















































































































































































































































































































































































































































































































































































































































































































































































































































































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

/*
 *	main.c
 *
 *	(C) Copyright 2000 by Robert Krten, all rights reserved.
 *	Please see the LICENSE file for more information.
 *
 *	This module represents the main module for the RIM/BIN
 *	dumper/disassembler/converter/reverse engineering tool.
 *
 *	This program will dump a RIM/BIN-formatted image to stdout, or
 *	convert a tape from one format to another, or clean up a tape,
 *	or disassemble a tape with reverse-engineering aids.
 *
 *	2001 01 07	R. Krten		created
 *	2003 12 16	R. Krten		added disassembler
 *	2003 12 17	R. Krten		made it RIM/BIN aware.
 *	2007 10 25	R. Krten		added reverse-engineering features
 *	2007 11 04	R. Krten		fixed header skip logic (see dumptape())
*/

#ifdef __USAGE

%C [options] papertapefile [papertapefile...]

where [options] are optional parameters chosen from:
    -b             generate a BIN-formatted output (adds ".bin")
    -d             suppress disassembly (useful for conversion-only mode)
    -r             generate a RIM-formatted output (adds ".rim")
    -T             generate test pattern 0000=0000..7756=7756
    -v             verbose operation

Dumps the RIM- or BIN-formatted input file(s) specified on
the command line.  If the "-r" and/or "-b" options are
present, also creates a RIM and/or BIN version of the output
by adding ".rim" and/or ".bin" (respectively) to the input
filename (can be used to "clean up" BIN and RIM images by
deleting excess data before and after the leader/trailer).

#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>

#include <sys/types.h>
#include <sys/stat.h>

#include "d8tape.h"

static	void seg_add (uint16_t addr, int len);
static	void seg_more (int len);

static	void	optproc (int, char **);
static	int		dumptape (unsigned char *t, int n);
static	int		dumprim (unsigned char *t, int n);
static	int		dumpbin (unsigned char *t, int n);
static	int		checkrim (unsigned char *t, int n);
static	int		checkbin (unsigned char *t, int n);
static	void	writebin (void);
static	void	writerim (void);
static	int		blank (short int *core, int size);

const	char	*progname = "d8tape";
const	char	*blankname= "      ";
extern	char	*version;					// version.c

int				optb;
int				optd;
int				optr;
int				optT;
int				optv;
unsigned char	*tape;						// paper tape image
short int		core [CORE_SIZE];			// in-core image (-1 means location never used)
uint16_t		tags [CORE_SIZE];			// analysis tags
segment_t		*segments;					// used to accumulate runs of data (from origin for nwords)
int				nsegments;					// indicates how many segments we have
char			*tapename;

/*
 *	main
 *
 *	Main simply calls the option processor, from which everything happens.
*/

int
main (int argc, char **argv)
{
	optproc (argc, argv);
	exit (EXIT_SUCCESS);
}

/*
 *	usageError
 *
 *	This is the usage message
*/

static void
usageError ()
{
	fprintf (stderr, "\nUsage:  %s [options] papertapefile [papertapefile...]\n\n", progname);
	fprintf (stderr, "where [options] are optional parameters chosen from:\n");
	fprintf (stderr, "    -b             generate a BIN-formatted output (adds \".bin\")\n");
	fprintf (stderr, "    -d             suppress disassembly (useful for conversion-only mode)\n");
	fprintf (stderr, "    -r             generate a RIM-formatted output (adds \".rim\")\n");
	fprintf (stderr, "    -v             verbose operation\n");
	fprintf (stderr, "\n");
	fprintf (stderr, "Dumps the RIM- or BIN-formatted input file(s) specified on\n");
	fprintf (stderr, "the command line.  If the \"-r\" and/or \"-b\" options are\n");
	fprintf (stderr, "present, also creates a RIM and/or BIN version of the output\n");
	fprintf (stderr, "by adding \".rim\" and/or \".bin\" (respectively) to the input\n");
	fprintf (stderr, "filename (can be used to \"clean up\" BIN and RIM images by\n");
	fprintf (stderr, "deleting excess data before and after the leader/trailer).\n");
	fprintf (stderr, "\n");
	fprintf (stderr, "Disassembly conforms to PAL III input requirements\n");
	fprintf (stderr, "\n");
	exit (EXIT_FAILURE);
}

/*
 *	optproc
 *
 *	This is the option processor.  It detects the command line options, and
 *	then processes the individual files.
*/

static void
optproc (int argc, char **argv)
{
	int		opt;
	int		got_any;
	int		fd;
	int		i;
	struct	stat statbuf;
	int		sts;

	if (!argc) {
		usageError ();
	}

	// clear out option values to defaults
	optb = optr = optT = 0;

	// handle command line options
	got_any = 0;
	while ((opt = getopt (argc, argv, "bdrTv")) != -1) {
		switch (opt) {
		case	'b':
			optb++;
			break;
		case	'd':
			optd++;
			break;
		case	'r':
			optr++;
			break;
		case	'T':
			optT++;
			break;
		case	'v':
			optv++;
			if (optv > 1) {
				fprintf (stderr, "Verbosity is %d\n", optv);
			}
			break;
		default:
			usageError ();
			break;
		}
	}

	// handle command line arguments
	for (; optind < argc; optind++) {
		got_any++;
		tapename = argv [optind];				// snap tapename to global

		// open the tape
		fd = open (tapename, O_RDONLY);
		if (fd == -1) {
			fprintf (stderr, "%s:  couldn't open %s for O_RDONLY, errno %d\n", progname, tapename, errno);
			perror (NULL);
			exit (EXIT_FAILURE);
		}
		fstat (fd, &statbuf);					// get the size, so we can read it into memory

		tape = calloc (1, statbuf.st_size);
		if (tape == NULL) {
			fprintf (stderr, "%s:  can't allocate %zu bytes during processing of %s, errno %d (%s)\n", progname, statbuf.st_size, tapename, errno, strerror (errno));
			exit (EXIT_FAILURE);
		}

		// initialize data areas
		memset (core, 0xff, sizeof (core));		// set to -1 -- since we are only using 12 bits of each 16 bit word, -1 isn't a valid PDP-8 core value
		memset (tags, 0, sizeof (tags));		// reset tags
		nsegments = 0;
		segments = NULL;

		read (fd, tape, statbuf.st_size);
		close (fd);

		// dump the tape (this also reads the tape into "core" and disassembles)
		sts = dumptape (tape, statbuf.st_size);

		free (tape);

		if (!sts) {
			continue;							// skip tape
		}

		// convert to RIM/BIN if required (-b and/or -r)
		if (optb || optr) {
			// see if there is any data there at all..
			if (blank (core, CORE_SIZE)) {
				fprintf (stderr, "%s:  tape image from %s is empty, not creating a BIN version\n", progname, tapename);
				return;
			}
			if (optb) {
				writebin ();
			}
			if (optr) {
				writerim ();
			}
		}
	}

	// if no arguments given, dump usage message
	if (optT) {
		memset (core, 0xff, sizeof (core));		// set to -1 (assumption; all 0xff's in an int is -1; pretty safe)
		for (i = 0; i < 07600; i++) {
			core [i] = ((i & 07700) >> 6) + ((i & 00077) << 6);
		}
		tapename = "test";
		if (optb) {
			writebin ();
		}
		if (optr) {
			writerim ();
		}
	} else {
		if (!got_any) {
			usageError ();
		}
	}
}

/*
 *	dumptape
 *
 *	This function does some basic processing (detecting and skipping the
 *	header, killing off data past the trailer) and then determines via
 *	checkrim() and ckeckbin() the format of the tape.  Depending on the
 *	type of tape, dumprim() or dumpbin() is called to read the tape into
 *	the "core" array and disassemble it.
 *
 *	20071104:  Changed the 'skip header' logic to look for a character
 *	less than 0x80, not just not equal to, because a tape I received
 *	had the following:
 *		0000000   200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200
 *		*
 *		0000100   300 100 000 000 000 050 001 000 002 000 003 000 000 000 000 054
 *
 *	Notice the "300" (0xc0) at location 0100.
 *
 *	Returns 0 on error.
*/

static int
dumptape (unsigned char *t, int n)
{
	int		i;

	// basic preprocessing; find header
	for (i = 0; i < n; i++) {
		if (t [i] == 0x80) {		// got a header
			break;
		}
	}
	if (i == n) {
		fprintf (stderr, "%s:  couldn't find a 0x80 leader on tape %s; tape ignored\n", progname, tapename);
		return (0);
	}

	// skip header
	for (; i < n; i++) {
		if (t [i] < 0x80) {			// RK 20071104 was "!= 0x80"; 
			break;
		}
	}
	if (i == n) {
		fprintf (stderr, "%s:  no data content found after 0x80 leader on tape %s; tape ignored\n", progname, tapename);
		return (0);
	}

	// at this point, we're positioned on the first-non-leader byte of the tape
	if (n - i < 4) {
		fprintf (stderr, "%s:  tape %s is too short; tape ignored\n", progname, tapename);
		return (0);
	}

	// skip leader (t now points to start of tape; n indicates remaining # bytes)
	if (optv) {
		fprintf (stderr, "%s:  tape %s skipped %d (0x%0X, 0%0o) bytes of header, original size %d new size %d\n", progname, tapename, i, i, i, n, n - i);
	}
	t += i;
	n -= i;

	// find first 0x80 -- trailer
	for (i = 0; i < n; i++) {
		if (t [i] == 0x80) {
			break;
		}
	}
	if (i == n) {
		fprintf (stderr, "%s:  warning -- tape %s does not have a trailer\n", progname, tapename);
		// find first data >= 0x80, then
		for (i = 0; i < n; i++) {
			if (t [i] >= 0x80) {
				// at least stop on the first invalid character, then
				break;
			}
		}
	}

	// reset end-of-tape to last character
	if (optv > 2) {
		printf ("%s:  tape %s skipped %d bytes of trailer, new size is %d bytes\n", progname, tapename, n - i, i);
	}
	n = i;

	// determine type of tape and dump it
	if (checkrim (t, n)) {
		if (!dumprim (t, n)) {
			return (0);
		}
	} else if (checkbin (t, n)) {
		if (!dumpbin (t, n)) {
			return (0);
		}
	} else {
		fprintf (stderr, "%s:  tape %s is neither RIM nor BIN (first four bytes are 0%03o 0%03o 0%03o 0%03o)\n", progname, tapename, t [0], t [1], t [2], t [3]);
		return (0);
	}

	if (!optd) {
		flow ();				// perform flow analysis
		disassemble ();			// disassemble
		printf ("\n$\n");
	}
	return (1);
}

/*
 *	checkrim
 *	checkbin
 *
 *	These two functions try to determine what format the tape is in.
 *	A zero return indicates the tape is not in the given format; a one
 *	indicates it is.  The heuristics used here are fairly simple.
*/

static int
checkrim (unsigned char *t, int n)
{
	int		i;

	if (n % 4) {
		if (optv > 2) {
			printf ("%s:  tape %s size (%d bytes) is not divisible by four; not a RIM tape\n", progname, tapename, n);
		}
		return (0);
	}

	// see if it's a RIM-formatted tape; we're looking for 01xxxxxx 00xxxxxx 00xxxxxx 00xxxxxx
	for (i = 0; i < n; i += 4) {
		if ((t [i] & 0xC0) != 0x40 || (t [i + 1] & 0xC0) || (t [i + 2] & 0xC0) || (t [i + 3] & 0xC0)) {
			if (optv > 2) {
				printf ("%s:  tape %s does not have the RIM signature at offset 0%04o; expected 01xxxxxx 00xxxxxx 00xxxxxx 00xxxxxx, got 0%04o 0%04o 0%04o 0%04o\n", progname, tapename, i, t [i], t [i + 1], t [i + 2], t [i + 3]);
			}
			return (0);
		}
	}
	return (1);
}

static int
checkbin (unsigned char *t, int n)
{
	if (n % 2) {
		if (optv > 2) {
			printf ("%s:  tape %s size (%d bytes) is not divisible by two; not a BIN tape\n", progname, tapename, n);
		}
		return (0);
	}

	// see if it's a BIN-formatted tape; 01xxxxxx 00xxxxxx (i.e., we at least expect an origin)
	if ((t [0] & 0xC0) != 0x40 || (t [1] & 0xC0)) {
		if (optv > 2) {
			printf ("%s:  tape %s does not have the BIN origin signature; expected header of 01xxxxxx 00xxxxxx, got 0%04o 0%04o\n", progname, tapename, t [0], t [1]);
		}
		return (0);
	}

	return (1);
}

/*
 *	From the PDP-8/I & PDP-8/L Small Computer Handbook (099-00272-A1983 / J-09-5)
 *	Appendix D, "Perforated-Tape Loader Sequences", page 383
 *
 *	READIN MODE LOADER
 *	The readin mode (RIM) loader is a minimum length, basic perforated-tape
 *	reader program for the ASR33, it is initially stored in memory by manual use
 *	of the operator console keys and switches.  The loader is permanently stored in
 *	18 locations of page 37.
 *
 *	A perforated tape to be read by the RIM loader must be in RIM format:
 *
 *	   Tape Channel
 *	 8 7 6 5 4 3 2 1  OCTAL  Format
 *	 ---------------  -----  ------------------------
 *	 1 0 0 0 0 0 0 0   200   Leader-trailer code
 *	 0 1 -A1-  -A2-    1AA   Absolute address to
 *	 0 0 -A3-  -A4-    0AA   contain next 4 digits
 *	 0 0 -X1-  -X2-    0XX   Content of previous
 *	 0 0 -X3-  -X4-    0XX   4-digit address
 *	 0 1 -A1-  -A2-    1AA   Address
 *	 0 0 -A3-  -A4-    0AA
 *	 0 0 -X1-  -X2-    0XX   Content
 *	 0 0 -X3-  -X4-    0XX
 *	     (etc)                (etc)
 *	 1 0 0 0 0 0 0 0   200   Leader-trailer code
 *
 *	The RIM loader can only be used in conjunction with the ASR33 reader (not
 *	the high-speed perforated-tape reader).  Because a tape in RIM format is, in
 *	effect, twice as long as it need be, it is suggested that the RIM loader be used
 *	only to read the binary loader when using the ASR33. (Note that PDP-8 diag-
 *	nostic program tapes are in RIM format.)
 *
 *	The complete PDP-8/I RIM loader (SA=7756) is as follows:
 *
 *	Absolute	Octal
 *	Address		Content		Tag		Instruction I Z		Comments
 *	7756,		6032		BEG,	KCC					/CLEAR AC AND FLAG
 *	7757,		6031				KSF					/SKIP IF FLAG = 1
 *	7760,		5357				JMP .-1				/LOOKING FOR CHARACTER
 *	7761,		6036				KRB					/READ BUFFER
 *	7762,		7106				CLL RTL
 *	7763,		7006				RTL					/CHANNEL 8 IN AC0
 *	7764,		7510				SPA					/CHECKING FOR LEADER
 *	7765,		5357				JMP BEG+1			/FOUND LEADER
 *	7766,		7006				RTL					/OK, CHANNEL 7 IN LINK
 *	7767,		6031				KSF
 *	7770,		5367				JMP .-1
 *	7771,		6034				KRS					/READ, DO NOT CLEAR
 *	7772,		7420				SNL					/CHECKING FOR ADDRESS
 *	7773,		3776				DCA I TEMP			/STORE CONTENT
 *	7774,		3376				DCA TEMP			/STORE ADDRESS
 *	7775,		5356				JMP BEG				/NEXT WORD
 *	7776,		0			TEMP,	0					/TEMP STORAGE
 *	7777,		5XXX				JMP X				/JMP START OF BIN LOADER
*/

/*
 *	dumprim
 *
 *	This is a finite-state-machine that runs through the tape reading the address
 *	and data records, stuffs them into core[], and disassembles the opcodes unless
 *	"-d" is specified.
 *
 *	Note that disassembly is done after the complete tape has been read in, this
 *	allows us to do some flow analysis.
*/

#define	RIM_Initial			1				// waiting for address
#define	RIM_Addr			2				// got top part of address, process bottom part
#define	RIM_Data1			3				// got address, process top part of data
#define	RIM_Data2			4				// got top part of data, process bottom part

static int
dumprim (unsigned char *t, int n)
{
	int			state;
	int			i;
	uint16_t	addr, data;
	uint16_t	cur_addr;

	state = RIM_Initial;

	cur_addr = 0xffff;						// impossible address for PDP-8

	for (i = 0; i < n; i++) {
		if (optv > 2) {
			printf ("[%03o] ", t [i]); fflush (stdout);
			if ((i % 13) == 12) {
				printf ("\n");
			}
		}
		switch (state) {
		case	RIM_Initial:
			if (t [i] & 0100) {				// indicates 1st part of address
				addr = (t [i] & 0077) << 6;	// store top part
				state = RIM_Addr;
			}
			break;
		case	RIM_Addr:
			addr |= (t [i] & 0077);
			state = RIM_Data1;
			break;
		case	RIM_Data1:
			data = (t [i] & 0077) << 6;		// store top part
			state = RIM_Data2;
			break;
		case	RIM_Data2:					// final decode complete, store data
			data |= (t [i] & 0077);
			core [addr] = data;				// stash data into core image

			// segment management -- if it's the next byte, add, else create new
			if (addr == cur_addr) {
				cur_addr++;
				seg_more (1);
			} else {
				seg_add (addr, 1);
				cur_addr = addr + 1;
			}
			state = RIM_Initial;
			break;
		}
	}
	if (optv > 2) {
		printf ("\n");
	}
	return (1);
}

/*
 *	BIN format, from the same doc as above, page 384:
 *
 *	BINARY LOADER
 *	The binary loader (BIN) is used to read machine language tapes (in binary
 *	format) produced by the program assembly language (PAL).  A tape in binary
 *	format is about one-half the length of the comparable RIM format tape.  It can,
 *	therefore, be read about twice as fast as a RIM tape and is, for this reason,
 *	the more desirable format to use with the 10 cps ASR33 reader or the Type
 *	PR8/I High-Speed Perforated-Tape Reader.
 *
 *	The format of a binary tape is as follows:
 *
 *		LEADER: about 2 feet of leader-trailer codes.
 *
 *		BODY: characters representing the absolute, machine language program
 *		in easy-to-read binary (or octal) form.  The section of tape may contain
 *		characters representing instructions (channel 8 and 7 not punched) or
 *		origin resettings (channel 8 not punched, channel 7 punched) and is
 *		concluded by 2 characters (channel 8 and 7 not punched) that represent
 *		a check sum for the entire section.
 *
 *		TRAILER: same as leader.
 *
 *	I.e.,
 *
 *	  Tape Channel
 *	8 7 6 5 4 3 2 1  OCTAL  Format
 *	---------------  -----  ------------------------
 *	1 0 0 0 0 0 0 0	  200	Leader
 *	0 1 A A A A A A   1AA	Address (top)
 *	0 1 B B B B B B	  1BB	Address (bottom)
 *	0 0 C C C C C C	  0CC	Data (top)
 *	0 0 D D D D D D	  0DD	Data (bottom)
 *	0 0 C C C C C C	  0CC	Data (top)
 *	0 0 D D D D D D	  0DD	Data (bottom)
 *	     . . .		  ...	next data (2 bytes)
 *	0 1 A A A A A A   1AA	New address (top)
 *	0 1 B B B B B B	  1BB	New address (bottom)
 *	     . . .		  ...	next data (2 bytes)
 *	0 0 X X X X X X	  0XX	Checksum (top)
 *	0 0 Y Y Y Y Y Y	  0YY	Checksum (bottom)
 *		
*/

/*
 *	dumpbin
 *
 *	This is a finite-state-machine that runs through the tape looking for the
 *	origin and subsequent data fields, stuffs them into the core[] array, and
 *	optionally disassembles the opcodes.
 *
 *	Every time we hit an origin change, we create a new segment and accumulate
 *	bytes into it.
*/

#define	BIN_Initial				1			// initial state; we require an origin to get out of it
#define	BIN_Origin				2			// we got the top part of the origin, now need to get the bottom part
#define	BIN_DataHW				3			// we have an address, so we are looking for another origin or the top part of the data
#define	BIN_DataLW				4			// we have the top part of the data, now fetching the low part

static int
dumpbin (unsigned char *t, int n)
{
	int		tape_checksum;					// checksum stored on tape
	int		calc_checksum;					// calculated checksum
  	int		i;
  	int		state;
  	unsigned short int addr, data;

	if (n < 4) {
		fprintf (stderr, "%s:  tape %s is too short; tape skipped\n", progname, tapename);
		return (0);
	}

	tape_checksum = ((t [n - 2] & 0x3f) << 6) + (t [n - 1] & 0x3f);
	if (optv > 1) {
		printf ("%s:  tape %s expected checksum 0%04o\n", progname, tapename, tape_checksum);
	}
	n -= 2;									// tape is now shorter by the two bytes

	// now calculate checksum
	calc_checksum = 0;
	for (i = 0; i < n; i++) {
		calc_checksum += t [i];
	}
	calc_checksum &= 07777;					// mask to 12 bits
	if (optv > 1) {
		printf ("%s:  tape %s calculated checksum 0%04o\n", progname, tapename, calc_checksum);
	}

	if (tape_checksum != calc_checksum) {
		fprintf (stderr, "%s:  tape %s calculated checksum [0%04o] != stored checksum [0%04o]; tape skipped\n", progname, tapename, calc_checksum, tape_checksum);
		return (0);
	}

	// now we can dump the binary data via the state machine
	state = BIN_Initial;
	for (i = 0; i < n; i++) {
		if (optv > 2) {
			printf ("[%03o] ", t [i]); fflush (stdout);
			if ((i % 13) == 12) {
				printf ("\n");
			}
		}
		switch (state) {
		case	BIN_Initial:
			if (t [i] & 0100) {				// indicates origin setting code
				addr = (t [i] & 0077) << 6;	// store top part
				state = BIN_Origin;
			}
			break;
		case	BIN_Origin:
			addr += (t [i] & 0077);			// store bottom part
			state = BIN_DataHW;
			seg_add (addr, 0);
			break;
		case	BIN_DataHW:
			if (t [i] & 0100) {				// another origin; skip loading data and load address instead
				addr = (t [i] & 0077) << 6;
				state = BIN_Origin;
			} else {
				data = (t [i] & 0077) << 6;	// store top part of data
				state = BIN_DataLW;
			}
			break;
		case	BIN_DataLW:
			data += (t [i] & 0077);
			core [addr] = data;
			seg_more (1);
			addr++;							// the magic of BIN-format is the autoincrement of the address
			state = BIN_DataHW;
		}
	}
	if (optv > 2) {
		printf ("\n");
	}
	return (1);
}

/*
 *	writebin
 *	writerim
 *
 *	These two functions write the BIN and RIM format tapes to a file.
 *	The filename is constructed by appending ".bin" or ".rim" to the
 *	input filename.
 *
 *	The header and trailer written are short, LEADER_LENGTH bytes.
 *
 *	The writebin() uses a finit-state-machine to generate the origin.
*/

#define	LEADER_LENGTH	16					// 16 chars of leader/trailer should be plenty

#define	WBIN_Initial	1					// looking for first/next in-use core[] element
#define	WBIN_Writing	2					// origin written, dumping consecutive words

static	void
writebin (void)
{
	char	fname [PATH_MAX];
	char	leader [LEADER_LENGTH];
	FILE	*fp;
	int		i;
	int		cksum;
	int		state;

	// create filename and open it
	sprintf (fname, "%s.bin", tapename);
	if ((fp = fopen (fname, "w")) == NULL) {
		fprintf (stderr, "%s:  unable to open BIN output file %s for w, errno %d (%s); creation of output file skipped\n", progname, fname, errno, strerror (errno));
		return;
	}

	// write leader
	memset (leader, 0x80, sizeof (leader));
	fwrite (leader, 1, sizeof (leader), fp);

	// now scan through "core" and write the data out...
	cksum = 0;
	state = WBIN_Initial;
	for (i = 0; i < CORE_SIZE; i++) {
		switch (state) {
		case	WBIN_Initial:				// transit out of WBIN_Initial on a "used" core position
			if (core [i] != -1) {
				state = WBIN_Writing;
				fprintf (fp, "%c%c", 0x40 | ((i & 07700) >> 6), i & 00077);		// write origin directive
				fprintf (fp, "%c%c", (core [i] & 07700) >> 6, core [i] & 00077);	// write data
				cksum += (0x40 | ((i & 07700) >> 6)) + (i & 00077) + ((core [i] & 07700) >> 6) + (core [i] & 00077);
			}
			break;
		case	WBIN_Writing:
			if (core [i] == -1) {
				state = WBIN_Initial;		// waiting again for a used core position
			} else {
				fprintf (fp, "%c%c", (core [i] & 07700) >> 6, core [i] & 00077);
				cksum += ((core [i] & 07700) >> 6) + (core [i] & 00077);
			}
			break;
		}
	}

	// now write the checksum
	fprintf (fp, "%c%c", (cksum & 07700) >> 6, cksum & 00077);

	// write trailer
	fwrite (leader, 1, sizeof (leader), fp);

	fclose (fp);
}

static	void
writerim (void)
{
	char	fname [PATH_MAX];
	char	leader [LEADER_LENGTH];
	FILE	*fp;
	int		i;

	// create the filename and open it
	sprintf (fname, "%s.rim", tapename);
	if ((fp = fopen (fname, "w")) == NULL) {
		fprintf (stderr, "%s:  unable to open RIM output file %s for w, errno %d (%s); creation of output file skipped\n", progname, fname, errno, strerror (errno));
		return;
	}

	// write leader
	memset (leader, 0x80, sizeof (leader));
	fwrite (leader, 1, sizeof (leader), fp);

	for (i = 0; i < CORE_SIZE; i++) {
		if (core [i] != -1) {
			fprintf (fp, "%c%c%c%c", 0x40 + ((i & 07700) >> 6), i & 00077, (core [i] & 07700) >> 6, core [i] & 00077);
		}
	}

	// write trailer
	fwrite (leader, 1, sizeof (leader), fp);

	fclose (fp);
}

/*
 *	blank
 *
 *	A utility routine to see if core[] is blank (returns 1).
 *	Used to avoid writing an empty tape.
*/

static int
blank (short int *c, int size)
{
	int		i;

	for (i = 0; i < size; i++) {
		if (c [i] != -1) {
			return (0);
		}
	}
	return (1);
}

/*
 *	seg_add (addr, len)
 *	seg_more (more)
 *
 *	These functions manipulate the segment data stored
 *	in "segments[]" and "nsegments".
 *
 *	seg_add creates a new segment with the given address
 *	and length.
 *
 *	seg_more lengthens the current segment by "more"
 *	words.
*/

static void
seg_add (uint16_t addr, int len)
{
	if (optv > 3) {
		printf ("seg_add (0%04o, %d (0%04o))\n", addr, len, len);
	}

	segments = realloc (segments, (nsegments + 1) * sizeof (segments [0]));
	if (segments == NULL) {
		fprintf (stderr, "%s:  couldn't realloc segments array to be %d elements (%ld bytes) long, errno %d (%s)\n", progname, nsegments + 1, (nsegments + 1) * sizeof (segments [0]), errno, strerror (errno));
		exit (EXIT_FAILURE);
	}
	segments [nsegments].saddr = addr;
	segments [nsegments].nwords = len;
	nsegments++;
}

static void
seg_more (int len)
{
	if (optv > 3) {
		printf ("seg_more (+%d (0%04o))\n", len, len);
	}

	if (nsegments) {
		segments [nsegments - 1].nwords += len;
	} else {
		fprintf (stderr, "%s:  seg_more called with no segments in existence\n", progname);
		exit (EXIT_FAILURE);
	}
}

Added src/d8tape/version.c.

1
+
const char *version="0.352";
Changes to src/gpio-common.c.in.
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
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







+
+
+
+
+
+
+
+











-
+








// Flow-control switch states which are owned by -- that is, primarily
// modified by -- the PDP8/pidp8i module, but we can't define these
// there because we refer to them below, and not all programs that link
// to us link to that module as well.  For such programs, it's fine if
// these two flags stay 0.
int swStop = 0, swSingInst = 0;

// Flag to override ILS mode, forcing fallback to NLS mode.  Set when
// the PDP-8 instruction decoding loop detects that we're using the
// ratio form of SET THROTTLE, which prevents the use of ILS due to the
// way instructions are executed in that mode.  Defined here rather than
// in gpio-ils.c because we don't want to make code that sets this
// conditional based on whether ILS is in fact actually enabled.
int suppressILS = 0;

// Flag set when sim_instr() exits due to some SIMH event like Ctrl-E,
// which lets us resume from our imposed "pause" display state.
int resumeFromInstructionLoopExit = 0;


//// MEMORY MAPPED GPIO FUNCTIONS //////////////////////////////////////

//// map_peripheral ////////////////////////////////////////////////////
// Exposes the physical address defined in the passed structure

int map_peripheral(struct bcm2835_peripheral *p, int exclusive)
static int map_peripheral(struct bcm2835_peripheral *p, int exclusive)
{
    // Name of GPIO memory-mapped device
    static const char* gpio_mem_dev = "/dev/gpiomem";

    if (access(gpio_mem_dev, F_OK) < 0) {
        // That dev node isn't even present, so it's probably not a Pi
        return -1;
220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
228
229
230
231
232
233
234

235
236
237
238
239
240
241
242







-
+







    }
}


//// bcm_host_get_peripheral_address ///////////////////////////////////
// Find Pi's GPIO base address

unsigned bcm_host_get_peripheral_address(void)
static unsigned bcm_host_get_peripheral_address(void)
{
    unsigned address = ~0;
    FILE *fp = fopen("/proc/device-tree/soc/ranges", "rb");
    if (fp)
    {   unsigned char buf[4];
        fseek(fp, 4, SEEK_SET);
        if (fread(buf, 1, sizeof buf, fp) == sizeof buf)
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
569
570
571
572
573
574
575




576







577























578



579

580




581
582
583
584
585
586
587
588
589







-
-
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
-

-
-
-
-
+
+







    }
    for (size_t row = 0; row < NLEDROWS; ++row) {
        INP_GPIO (ledrows[row]);
    }
}


//// gpio_thread ///////////////////////////////////////////////////////
// The GPIO thread entry point: initializes GPIO and then calls
// the gpio_core () implementation linked to this program.

//// init_pidp8i_gpio //////////////////////////////////////////////////
static void *gpio_thread (void *terminate)
{
    // Set thread to real time priority
    struct sched_param sp;
    sp.sched_priority = 4;  // not high, just above the minimum of 1
    int rt = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp) == 0;

// Initialize the GPIO pins to the initial states required by
    // Tell the user about our configuration, succinctly
    const char* pt = pi_type();
    printf(
        "PiDP-8/I @VERSION@ [%s] [%cls] [%spcb] [%sgpio]"
#ifdef DEBUG
        " [debug]"
#endif
        "%s",
        pt,
        ILS_MODE ? 'i' : 'n',
        pt[0] == 'p' ? 
#ifdef PCB_SERIAL_MOD_OV
            "sermod" : 
#elif PCB_SERIAL_MOD_JLW
            "altser" : 
#else
            "std" : 
#endif
            "no",
        (pidp8i_gpio_present ? "" : "no"),
        (rt ? " [rt]" : "")
    );

// gpio_thread().   It's a separate exported function so that scanswitch
    // It's okay for our caller to resume executing, if it's locked
    // waiting for us to finish the initialization bits that can only
    // happen in this thread.
// can also use it.
    pthread_mutex_unlock (&gpio_start_mutex);

    // If we didn't map the GPIO peripheral and get here, it was
    // optional, and we've done all we can without it.
    if (!pidp8i_gpio_present) return (void*)-1;

void init_pidp8i_gpio (void)
{
    // initialise GPIO (all pins used as inputs, with pull-ups enabled on cols)
    int i;
    for (i = 0; i < NLEDROWS; i++) {     // Define ledrows as input
        INP_GPIO(ledrows[i]);
        GPIO_CLR = 1 << ledrows[i];     // so go to Low when switched to output
    }
    for (i = 0; i < NCOLS; i++) {       // Define cols as input
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
-
+
+
-
-
-
+
-
-
+
-
-
+
+
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    usleep(1);  // must wait 150 cycles
    GPIO_PULLCLK0 = 0x070000; // selects GPIO pins 16..18
    usleep(1);
    GPIO_PULL = 0; // reset GPPUD register
    usleep(1);
    GPIO_PULLCLK0 = 0; // remove clock
    usleep(1); // probably unnecessary
}


//// gpio_thread ///////////////////////////////////////////////////////
// The GPIO thread entry point: initializes GPIO and then calls
// the gpio_core () implementation linked to this program.

static void *gpio_thread (void *terminate)
{
    // Set thread to real time priority
    struct sched_param sp;
    sp.sched_priority = 4;  // not high, just above the minimum of 1
    int rt = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp) == 0;

    // Tell the user about our configuration, succinctly
    const char* pt = pi_type();
    printf(
        "PiDP-8/I @VERSION@ [%s] [%cls] [%spcb] [%sgpio]"
#ifdef DEBUG
        " [debug]"
#endif
        "%s",
        pt,
        ILS_MODE ? 'i' : 'n',
        pt[0] == 'p' ? 
#ifdef PCB_SERIAL_MOD_OV
            "sermod" : 
#elif PCB_SERIAL_MOD_JLW
            "altser" : 
#else
            "std" : 
#endif
            "no",
        (pidp8i_gpio_present ? "" : "no"),
        (rt ? " [rt]" : "")
    );

    // It's okay for our caller to resume executing, if it's locked
    // waiting for us to finish the initialization bits that can only
    // happen in this thread.
    pthread_mutex_unlock (&gpio_start_mutex);

    // If we didn't map the GPIO peripheral and get here, it was
    // optional, and we've done all we can without it.
    if (!pidp8i_gpio_present) return (void*)-1;

    // Set GPIO pins to initial states
    init_pidp8i_gpio ();

    // Hand off control to the gpio_core () variant linked to this
    // program: either the new incandescent lamp simulator or the old
    // stock version.
    extern void gpio_core (struct bcm2835_peripheral*, int* terminate);
    gpio_core (&gpio, (int*)terminate);

    // gpio_core () leaves all cols, rows, ledrows are set to input, and
    // it's safe to leave them in that state.  No need to de-init GPIO.
    gss_initted = 0;
    return 0;
}


//// start/stop_pidp8i_gpio_thread /////////////////////////////////////
// Start and stop gpio_thread().  We export these functions rather than
//// map_gpio_for_pidp8i ///////////////////////////////////////////////
// Wrapper around map_peripheral() taking care of some higher level
// export gpio_thread() directly so this module's users don't have to
// know anything about pthreads and such.

// details.  This is the interface we expose to outsiders.
int start_pidp8i_gpio_thread (const char* must_map)
{

    char errs[100];

int map_gpio_for_pidp8i (int must_map)
{
    // Find GPIO address (it varies by Pi model)
    unsigned int gpio_base_addr = bcm_host_get_peripheral_address();
    gpio.addr_p = gpio_base_addr + 0x200000;

    // Attempt to map the GPIO peripheral for our exclusive use.  Some
    // callers care if this fails, and some don't.
    map_peripheral (&gpio, 1);
    if (must_map && !pidp8i_gpio_present) return EFAULT;

#ifdef PCB_SERIAL_MOD_JLW
    // James L-W's alternative serial mods were declared to be done here
    // at configure time, so disable the hysteresis on the GPIO inputs.
    map_peripheral (&gpio2, 0);  // assume success since prior mapping worked
    gpio2.addr_p = gpio_base_addr + 0x100000;
    gpio2.addr[0x0B] = (gpio2.addr[0x0B] & 0xF7) | (0x5A << 24);
#endif

    return 0;
}


//// start/stop_pidp8i_gpio_thread /////////////////////////////////////
// Start and stop gpio_thread().  We export these functions rather than
// export gpio_thread() directly so this module's users don't have to
// know anything about pthreads and such.

int start_pidp8i_gpio_thread (const char* must_map)
{
    char errs[100];
    
    if (map_gpio_for_pidp8i (must_map != 0) != 0) {
        return EFAULT;
    }

    // Until gpio_core () reads the switches for the first time, we need
    // to mark them as all-open, lest we have a race condition with the
    // simulator where it interprets the all-0 initial switchstatus[]
    // value as "all switches closed," which includes the shutdown seq!
    memset (switchstatus, 0xFF, sizeof (switchstatus));

Changes to src/gpio-common.h.
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
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







+
+
+
+
+











+
+
-
-
+
+







extern display* pdis_update, *pdis_paint;

// Compatibility interface for programs like src/test.c that depend on
// being able to modify the LED values directly.
#define ledstatus (pdis_update->curr)
extern int pidp8i_simple_gpio_mode;

// Simplified interface for single-threaded programs that don't use our
// GPIO thread, but do want to share some our implementation.
extern void init_pidp8i_gpio (void);
extern int map_gpio_for_pidp8i (int must_map);

extern uint16_t switchstatus[];
extern uint8_t cols[];
extern uint8_t ledrows[];
extern uint8_t rows[];
extern uint8_t pidp8i_gpio_present;

extern int start_pidp8i_gpio_thread (const char* must_map);
extern void stop_pidp8i_gpio_thread ();
extern void turn_off_pidp8i_leds ();
extern void update_led_states (const us_time_t delay);

// Alternative to start_pidp8i_gpio_thread() for programs that run
// single-threaded and do their own GPIO scanning, like scanswitch.
extern unsigned bcm_host_get_peripheral_address(void);
extern int map_peripheral(struct bcm2835_peripheral *p, int exclusive);
extern void init_pidp8i_gpio (void);

extern void unmap_peripheral(struct bcm2835_peripheral *p);

extern void read_switches (ns_time_t delay);

extern void swap_displays ();

extern void sleep_ns(ns_time_t ns);
Changes to src/gpio-ils.c.
138
139
140
141
142
143
144
145
146
147
148






149

150
151
152
153
154
155
156
138
139
140
141
142
143
144




145
146
147
148
149
150
151
152
153
154
155
156
157
158
159







-
-
-
-
+
+
+
+
+
+

+







                else {
                    *p -= *p * FALLING_FACTOR;
                }
            }
        }

        // Light up LEDs
        extern int swStop, swSingInst;
        if (swStop || swSingInst) {
            // The CPU is in STOP mode, so show the current LED states
            // full-brightness using the same mechanism NLS uses.
        extern int swStop, swSingInst, suppressILS;
        if (swStop || swSingInst || suppressILS) {
            // The CPU is in STOP mode or someone has suppressed the ILS,
            // so show the current LED states full-brightness using the
            // same mechanism NLS uses.  Force a display swap if the next
            // loop iteration won't do it in case this isn't STOP mode.
            update_led_states (intervl * 60);
            if (step != (MAX_BRIGHTNESS - 1)) swap_displays();
        }
        else {
            // Normal case: PWM display using the on-count values
            for (size_t row = 0; row < NLEDROWS; ++row) {
                // Output 0 (CLR) for LEDs in this row which should be on
                size_t *prow = pdis_paint->on[row];
                for (size_t col = 0; col < NCOLS; ++col) {
Added src/palbart/LICENSE.md.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# palbart License

The following was extracted from the top of [`palbart.c`][1] in this
directory:

---------

This is free software.  There is no fee for using it.  You may make any
changes that you wish and also give it away.  If you can make commercial
product out of it, fine, but do not put any limits on the purchaser's
right to do the same.  If you improve it or fix any bugs, it would be
nice if you told me and offered me a copy of the new version.

---------

[1]: https://tangentsoft.com/pidp8i/doc/trunk/palbart/palbart.c
Added src/palbart/palbart.1.








































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
.\"                                      Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH PALBART 1 "January 16, 2000"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh        disable hyphenation
.\" .hy        enable hyphenation
.\" .ad l      left justify
.\" .ad b      justify to both left and right margins
.\" .nf        disable filling
.\" .fi        enable filling
.\" .br        insert line break
.\" .sp <n>    insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
palbart \- BART enhanced PDP8 crossassembler
.SH SYNOPSIS
.B palbart
.RI [options] inputfile
.br
.SH DESCRIPTION
This manual page documents briefly the
.B palbart
command.
It is a cross-assembler to for PDP/8 assembly language programs.
It will produce an output file in bin format, rim format, and using the
appropriate pseudo-ops, a combination of rim and bin formats.
A listing file is always produced and with an optional symbol table
and/or a symbol cross-reference (concordance).  The permanent symbol
table can be output in a form that may be read back in so a customized
permanent symbol table can be produced.  Any detected errors are output
to a separate file giving the filename in which they were detected
along with the line number, column number and error message as well as
marking the error in the listing file.
.PP
The following file name extensions are used:
.PP
 .pal    source code (input)
.PP
 .lst    assembly listing (output)
.PP
 .bin    assembly output in DEC's bin format (output)
.PP
 .rim    assembly output in DEC's rim format (output)
.PP
 .err    assembly errors detected (if any) (output)
.PP
 .prm    permanent symbol table in form suitable for reading after the EXPUNGE pseudo-op.

.PP
.SH OPTIONS
A summary of options is included below.
.TP
.B \-d
Show symbol table at end of assembly
.TP
.B \-h
Display help.
.TP
.B \-l
Allow generation of literals (default is no literal generation)
Show version of program.
.TP
.B \-p
Generate a file with the permanent symbols in it.
(To get the current symbol table, assemble a file than has only
a $ in it.)
.TP
.B \-r
Produce output in rim format (default is bin format)
.TP
.B \-v
Display version information.
.TP
.B \-x
Generate a cross-reference (concordance) of user symbols.

.SH  DIAGNOSTICS
Assembler error diagnostics are output to an error file and inserted
in the listing file.  Each line in the error file has the form
.PP
<filename>(<line>:<col>) : error:  <message> at Loc = <loc>
.PP
An example error message is:
.br
bintst.pal(17:9) : error:  undefined symbol "UNDEF" at Loc = 07616
.PP
The error diagnostics put in the listing start with a two character
error code (if appropriate) and a short message.  A carat '^' is
placed under the item in error if appropriate.
An example error message is:
.PP
      17 07616 3000          DCA     UNDEF
.br
   UD undefined                      ^
.br
      18 07617 1777          TAD  I  DUMMY
.PP
When an indirect is generated, an at character '@' is placed after the
the instruction value in the listing as an indicator as follows:
.PP
      14 03716 1777@         TAD     OFFPAG
.PP
Undefined symbols are marked in the symbol table listing by prepending
a '?' to the symbol.  Redefined symbols are marked in the symbol table
listing by prepending a '#' to the symbol.  Examples are:
.PP
   #REDEF   04567
.br
    SWITCH  07612
.br
   ?UNDEF   00000
.PP
Refer to the code for the diagnostic messages generated.

.SH BUGS
Only a minimal effort has been made to keep the listing format
anything like the PAL-8 listing format.
The operation of the conditional assembly pseudo-ops may not function
exactly as the DEC versions.  I did not have any examples of these so
the implementation is my interpretation of how they should work.
.PP
The RIMPUNch and BINPUNch pseudo-ops do not change the binary output
file type that was specified on startup.  This was intentional and
and allows rim formatted data to be output prior to the actual binary
formatted data.  On UN*X style systems, the same effect can be achieved
ing the "cat" command, but on DOS/Windows systems, doing this was
a major chore.
.PP
The floating point input does not generate values exactly as the DEC
compiler does.  I worked out several examples by hand and believe that
this implementation is slightly more accurate.  If I am mistaken,
let me know and, if possible, a better method of generating the values.
.br

.SH HISTORICAL NOTE
This assembler was written to support the fleet of PDP-8 systems
used by the Bay Area Rapid Transit System.  As of early 1997,
this includes about 40 PDP-8/E systems driving the train destination
signs in passenger stations.

.SH REFERENCES
This assembler is based on the pal assember by:
.br
Douglas Jones <jones@cs.uiowa.edu> and
.br
Rich Coon <coon@convexw.convex.com>

.SH DISCLAIMER
See the symbol table for the set of pseudo-ops supported.
.PP
See the code for pseudo-ops that are not standard for PDP/8 assembly.
.PP
Refer to DEC's "Programming Languages (for the PDP/8)" for complete
documentation of pseudo-ops.
.PP
Refer to DEC's "Introduction to Programming (for the PDP/8)" or a
lower level introduction to the assembly language.

.SH WARRANTY
If you don't like it the way it works or if it doesn't work, that's
tough.  You're welcome to fix it yourself.  That's what you get for
using free software.

.SH COPYRIGHT NOTICE
This is free software.  There is no fee for using it.  You may make
any changes that you wish and also give it away.  If you can make
a commercial product out of it, fine, but do not put any limits on
the purchaser's right to do the same.  If you improve it or fix any
bugs, it would be nice if you told me and offered me a copy of the
new version.
Gary Messenbrink <gam@rahul.net>

.SH VERSIONS
 Version  Date    by   Comments
.br
   v1.0  12Apr96  GAM  Original
.br
   v1.1  18Nov96  GAM  Permanent symbol table initialization error.
.br
   v1.2  20Nov96  GAM  Added BINPUNch and RIMPUNch pseudo-operators.
.br
   v1.3  24Nov96  GAM  Added DUBL pseudo-op (24 bit integer constants).
.br
   v1.4  29Nov96  GAM  Fixed bug in checksum generation.
.br
   v2.1  08Dec96  GAM  Added concordance processing (cross reference).
.br
   v2.2  10Dec96  GAM  Added FLTG psuedo-op (floating point constants).
.br
   v2.3   2Feb97  GAM  Fixed paging problem in cross reference output.
.br
   v2.4  11Apr97  GAM  Fixed problem with some labels being put in cross reference multiple times.

.SH AUTHOR
This manual page was written by Vince Mulhollon <vlm@execpc.com>,
for the Debian GNU/Linux system (but may be used by others).
Added src/palbart/palbart.c.
































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































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
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/******************************************************************************/
/*                                                                            */
/* Program:  PAL (BART version)                                               */
/* File:     pal.c                                                            */
/* Author:   Gary A. Messenbrink                                              */
/*           gam@rahul.net                                                    */
/*                                                                            */
/* Purpose:  A 2 pass PDP-8 pal-like assembler.                               */
/*                                                                            */
/* PAL(1)                                                                     */
/*                                                                            */
/* NAME                                                                       */
/*    pal - a PDP/8 pal-like assembler.                                       */
/*                                                                            */
/* SYNOPSIS:                                                                  */
/*    pal [ -$ -d -h -e -l -p -r -t -v -x ] inputfile                         */
/*                                                                            */
/* DESCRIPTION                                                                */
/*    This is a cross-assembler to for PDP/8 assembly language programs.      */
/*    It will produce an output file in bin format, rim format, and using the */
/*    appropriate pseudo-ops, a combination of rim and bin formats.           */
/*    A listing file is always produced and with an optional symbol table     */
/*    and/or a symbol cross-reference (concordance).  The permanent symbol    */
/*    table can be output in a form that may be read back in so a customized  */
/*    permanent symbol table can be produced.  Any detected errors are output */
/*    to a separate file giving the filename in which they were detected      */
/*    along with the line number, column number and error message as well as  */
/*    marking the error in the listing file.                                  */
/*    The following file name extensions are used:                            */
/*       .pal    source code (input)                                          */
/*       .lst    assembly listing (output)                                    */
/*       .bin    assembly output in DEC's bin format (output)                 */
/*       .rim    assembly output in DEC's rim format (output)                 */
/*       .err    assembly errors detected (if any) (output)                   */
/*       .prm    permanent symbol table in form suitable for reading after    */
/*               the EXPUNGE pseudo-op.                                       */
/*                                                                            */
/* OPTIONS                                                                    */
/*    -$   Allow files to not end with $                                      */
/*    -d   Dump the symbol table at end of assembly                           */
/*    -h   Show help                                                          */
/*    -e   Don't allow generation of links                                    */
/*    -l   Allow generation of links (default is link generation)             */
/*    -n   No redefinition of permanent symbols with labels                   */
/*    -p   Generate a file with the permanent symbols in it.                  */
/*         (To get the current symbol table, assemble a file than has only    */
/*          a $ in it.)                                                       */
/*    -r   Produce output in rim format (default is bin format)               */
/*    -tN  Set tab stops to N spaces (default is 8)                           */
/*    -v   Display program version.                                           */
/*    -x   Generate a cross-reference (concordance) of user symbols.          */
/*                                                                            */
/* DIAGNOSTICS                                                                */
/*    Assembler error diagnostics are output to an error file and inserted    */
/*    in the listing file.  Each line in the error file has the form          */
/*                                                                            */
/*       <filename>(<line>:<col>) : error:  <message> at Loc = <loc>          */
/*                                                                            */
/*    An example error message is:                                            */
/*                                                                            */
/*       bintst.pal(17:9) : error:  undefined symbol "UNDEF" at Loc = 07616   */
/*                                                                            */
/*    The error diagnostics put in the listing start with a two character     */
/*    error code (if appropriate) and a short message.  A carat '^' is        */
/*    placed under the item in error if appropriate.                          */
/*    An example error message is:                                            */
/*                                                                            */
/*          17 07616 3000          DCA     UNDEF                              */
/*       UD undefined                      ^                                  */
/*          18 07617 1777          TAD  I  DUMMY                              */
/*                                                                            */
/*    When an indirect is generated, an at character '@' is placed after the  */
/*    the instruction value in the listing as an indicator as follows:        */
/*                                                                            */
/*          14 03716 1777@         TAD     OFFPAG                             */
/*                                                                            */
/*    Undefined symbols are marked in the symbol table listing by prepending  */
/*    a '?' to the symbol.  Redefined symbols are marked in the symbol table  */
/*    listing by prepending a '#' to the symbol.  Examples are:               */
/*                                                                            */
/*       #REDEF   04567                                                       */
/*        SWITCH  07612                                                       */
/*       ?UNDEF   00000                                                       */
/*                                                                            */
/*    Refer to the code for the diagnostic messages generated.                */
/*                                                                            */
/* BUGS                                                                       */
/*    This program will accept source that real PAL will not. To ensure       */
/*    valid source assemble on real or simulated PDP-8.                       */
/*    Different PAL versions have different permanent symbols defined. This   */
/*    program define more than and PAL version. By default redefining them    */
/*    as a label is not an error. It is for normal PAL. The -n flag will      */
/*    make redefining an error.                                               */
/*                                                                            */
/*    Only a minimal effort has been made to keep the listing format          */
/*    anything like the PAL-8 listing format.                                 */
/*    The operation of the conditional assembly pseudo-ops may not function   */
/*    exactly as the DEC versions.  I did not have any examples of these so   */
/*    the implementation is my interpretation of how they should work.        */
/*                                                                            */
/*    The RIMPUNch and BINPUNch pseudo-ops do not change the binary output    */
/*    file type that was specified on startup.  This was intentional and      */
/*    and allows rim formatted data to be output prior to the actual binary   */
/*    formatted data.  On UN*X style systems, the same effect can be achieved */
/*    by using the "cat" command, but on DOS/Windows systems, doing this was  */
/*    a major chore.                                                          */
/*                                                                            */
/*    The floating point input does not generate values exactly as the DEC    */
/*    compiler does.  I worked out several examples by hand and believe that  */
/*    this implementation is slightly more accurate.  If I am mistaken,       */
/*    let me know and, if possible, a better method of generating the values. */
/*                                                                            */
/*    CDF .-.                                                                 */
/*       Generates 2201 when assembled at 5000. This looks like a bug in OS/8 */
/*       PAL                                                                  */
/*                                                                            */
/* BUILD and INSTALLATION                                                     */
/*    The current version has only been built under Linux.                    */
/*    Earlier versions have been built and successfully executed on:          */
/*      a.  Linux (80486 CPU)using gcc                                        */
/*      b.  RS/6000 (AIX 3.2.5)                                               */
/*      c.  Borland C++ version 3.1  (large memory model)                     */
/*      d.  Borland C++ version 4.52 (large memory model)                     */
/*    with no modifications to the source code.                               */
/*                                                                            */
/*    On UNIX type systems, store the the program as the pal command          */
/*    and on PC type systems, store it as pal.exe                             */
/*                                                                            */
/* HISTORICAL NOTE:                                                           */
/*    This assembler was written to support the fleet of PDP-8 systems        */
/*    used by the Bay Area Rapid Transit System.  As of early 1997,           */
/*    this includes about 40 PDP-8/E systems driving the train destination    */
/*    signs in passenger stations.                                            */
/*                                                                            */
/* REFERENCES:                                                                */
/*    This assembler is based on the pal assembler by:                         */
/*       Douglas Jones <jones@cs.uiowa.edu> and                               */
/*       Rich Coon <coon@convexw.convex.com>                                  */
/*                                                                            */
/* DISCLAIMER:                                                                */
/*    See the symbol table for the set of pseudo-ops supported.               */
/*    See the code for pseudo-ops that are not standard for PDP/8 assembly.   */
/*    Refer to DEC's "Programming Languages (for the PDP/8)" for complete     */
/*    documentation of pseudo-ops.                                            */
/*    Refer to DEC's "Introduction to Programming (for the PDP/8)" or a       */
/*    lower level introduction to the assembly language.                      */
/*                                                                            */
/* WARRANTY:                                                                  */
/*    If you don't like it the way it works or if it doesn't work, that's     */
/*    tough.  You're welcome to fix it yourself.  That's what you get for     */
/*    using free software.                                                    */
/*                                                                            */
/* COPYRIGHT NOTICE:                                                          */
/*    This is free software.  There is no fee for using it.  You may make     */
/*    any changes that you wish and also give it away.  If you can make       */
/*    a commercial product out of it, fine, but do not put any limits on      */
/*    the purchaser's right to do the same.  If you improve it or fix any     */
/*    bugs, it would be nice if you told me and offered me a copy of the      */
/*    new version.                                                            */
/*                                                                            */
/*                                                                            */
/* Amendments Record:                                                         */
/*  Version  Date    by   Comments                                            */
/*  ------- -------  ---  --------------------------------------------------- */
/*    v1.0  12Apr96  GAM  Original                                            */
/*    v1.1  18Nov96  GAM  Permanent symbol table initialization error.        */
/*    v1.2  20Nov96  GAM  Added BINPUNch and RIMPUNch pseudo-operators.       */
/*    v1.3  24Nov96  GAM  Added DUBL pseudo-op (24 bit integer constants).    */
/*    v1.4  29Nov96  GAM  Fixed bug in checksum generation.                   */
/*    v2.1  08Dec96  GAM  Added concordance processing (cross reference).     */
/*    v2.2  10Dec96  GAM  Added FLTG psuedo-op (floating point constants).    */
/*    v2.3   2Feb97  GAM  Fixed paging problem in cross reference output.     */
/* DJG: I started with the 2.5 RK version but found when looking on the net   */
/* later that multiple diverging version existed. I have tried to combine     */
/* the fixed into one version. I took the version info below from the versions*/
/* I pulled from.                                                             */
/* http://dustyoldcomputers.com/pdp-common/reference/host/index.html          */
/* http://www.dunnington.u-net.com/public/PDP-8/palbart.c                     */
/* http://sourcecodebrowser.com/palbart/2.4/palbart-2_84_8c_source.html       */
/* http://packages.qa.debian.org/p/palbart.html                               */
/*    v2.4  11Apr97  GAM  Fixed problem with some labels being put in cross   */
/*                        reference multiple times.                           */
/*                        Started with RK version, Attempted to merge         */
/*                        GAM V2.4 and PNT change DJG                         */
/*    v2.4  29Oct07  RK   Added 4 character tabstop; IOTs for TA8/E.          */
/*    v2.4  19Jan03  PNT  Added ASCII pseudo-op, like TEXT but not packed.    */
/*    v2.5  03Nov07  RK   Fixed buffer overflow problem in readLine and       */
/*                        increased symbol table size                         */
/*    v2.6  14Jul03  PNT  Added missing TTY symbols, and "1st TTY" symbols.   */
/*    v2.7  14Jun13  DJG  David Gesswein djg@pdp8online.com                   */
/*                        Merged other changes found online giving duplicate  */
/*                         Versions in the history                            */
/*                         Didn't copy over deleting -l literal flag          */
/*                        All fixes to make it match OS/8 PAL8 better         */
/*                        Fixed handling of IFDEF type conditionals           */
/*                        Fixed excessive redefined symbol errors             */
/*                         PAL8 uses 12 bit symbols and this program label    */
/*                         symbols are 15 bit.                                */
/*                        Added FILENAME and DEVNAME psuedo ops               */
/*                        Added OPR and KCF instructions. Fixed RMF           */
/*                        Allowed space after =                               */
/*                        Prevented I and D from being deleted by EXPUNGE     */
/*                        Allowed permanent symbols to be redefined with error*/
/*                         PAL8 updates without message. Error is just warning*/
/*                        Fixed certain cases of memory reference generation  */
/*                        Allowed unary +                                     */
/*                        Fixed " character literal at end of line            */
/*                        Fixed errors in reloc handling                      */
/*                        Fixed [CDF CIF type expressions                     */
/*                        Made title default to first line                    */
/*                        Fixed checksum when nopunch used                    */
/*                        Fixed FIXTAB                                        */
/*                        Probably added more subtle bugs                     */
/*    v2.8  15Jun13  DJG  Merged versions found on net. See above             */
/*                        Added * to RELOC addresses in listing               */
/*                        Changed default to literal/links on. Added -e to    */
/*                         turn off                                           */
/*                        Fixed PAGE when RELOC used                          */
/*                        Changed SPF to TFL and SPI to TSK                   */
/*                        Make error when changing permanent symbol to label  */
/*                         if -e flag is used                                 */
/*                        Allow space oring in IFZERO etc                     */
/*                        Fixed handling of page zero overflow                */
/*    v2.9  23Jun13  DJG  Fixed properly all pages literal handling           */
/*                         changing page doesn't cause loss of last literal   */
/*                         location used.                                     */
/*                        Fixed bin generation if no origin set               */
/*    v2.9a 01Jul13  DJG  Fixed Comment. Binaries not updated                 */
/*    v2.10 08Feb14  DJG  Changed trailer to 8 bytes since pip didn't like    */
/*                        trailer of one 0x80                                 */
/*    v2.11 19Apr15  DPI  Fixed incorrect link generation with impled 0200    */
/*                        starting address. Patch from Doug Ingrams           */
/*    v2.12 28Apr15  DJG  Fixed incorrect handling of reloc, expressions with */
/*                        undefined symbols. Fixed conditional assembly with  */
/*                        undefined symbols. Added new flag to allow file to  */
/*                        not end with $                                      */
/*    v2.13 02May15  DPI  Fixed bug in readLine when removing \r from a blank */
/*                        line.  Changed -s to -$ in -h display.  Corrected   */
/*                        version comment.                                    */
/*    v2.13 03May15  DJG  Moved TITLE, BANK to new additional option.         */
/* Change release variable below when you update. Send changes back to        */
/*   David Gesswein, djg@pdp8online.com.                                      */
/******************************************************************************/

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

char *release = "pal-2.13, 03 May 2015";

/* Set to 1 and use -e flag to make ( and [ literals errors */
#define LITERAL_ERROR        0

#define LINELEN              132
#define LIST_LINES_PER_PAGE  55         /* Includes 5 line page header.       */
#define NAMELEN             128
#define SYMBOL_COLUMNS        5
#define SYMLEN                7
#define SYMBOL_TABLE_SIZE  4096
#define TITLELEN             63
#define XREF_COLUMNS          8

#define ADDRESS_FIELD  00177
#define FIELD_FIELD   070000
#define INDIRECT_BIT   00400
#define LAST_PAGE_LOC  00177
#define OP_CODE        07000
#define PAGE_BIT       00200

#ifdef  PAGE_SIZE
#undef  PAGE_SIZE
#endif
#define PAGE_SIZE      00200

#define PAGE_FIELD     07600
#define PAGE_ZERO_END  00200

/* Macro to get the number of elements in an array.                           */
#define DIM(a) (sizeof(a)/sizeof(a[0]))

/* Macro to get the address plus one of the end of an array.                  */
#define BEYOND(a) ((a) + DIM(A))

#define is_blank(c) ((c==' ') || (c=='\t') || (c=='\f') || (c=='>'))
#define isend(c)   ((c=='\0')|| (c=='\n'))
#define isdone(c)  ((c=='/') || (isend(c)) || (c==';'))

/* Macros for testing symbol attributes.  Each macro evaluates to non-zero    */
/* (true) if the stated condition is met.                                      */
/* Use these to test attributes.  The proper bits are extracted and then      */
/* tested.                                                                    */
#define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION)
#define M_DEFINED(s)     ((s & DEFINED) == DEFINED)
#define M_DUPLICATE(s)   ((s & DUPLICATE) == DUPLICATE)
#define M_FIXED(s)       ((s & FIXED) == FIXED)
#define M_LABEL(s)       ((s & LABEL) == LABEL)
#define M_MRI(s)         ((s & MRI) == MRI)
#define M_MRIFIX(s)      ((s & MRIFIX) == MRIFIX)
#define M_PSEUDO(s)      ((s & PSEUDO) == PSEUDO)
#define M_REDEFINED(s)   ((s & REDEFINED) == REDEFINED)
#define M_UNDEFINED(s)   (!M_DEFINED(s))
#define M_PERM_REDEFINED(s)   ((s & PERM_REDEFINED) == PERM_REDEFINED)

/* This macro is used to test symbols by the conditional assembly pseudo-ops. */
#define M_DEF(s) (M_DEFINED(s))
#define M_COND(s) (M_CONDITIONAL(s))
#define M_DEFINED_CONDITIONALLY(t) (M_DEF(t) && ((pass==1) ||!M_COND(t)))

typedef unsigned char BOOL;
typedef unsigned char BYTE;
typedef short    int  WORD16;
typedef long     int  WORD32;

#ifndef FALSE
  #define FALSE 0
  #define TRUE (!FALSE)
#endif

/* Line listing styles.  Used to control listing of lines.                    */
enum linestyle_t
{
  LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL
};
typedef enum linestyle_t LINESTYLE_T;

/* Symbol Types.                                                              */  
/* Note that the names that have FIX as the suffix contain the FIXED bit      */
/* included in the value.                                                     */
/*                                                                            */
/* The CONDITION bit is used when processing the conditional assembly PSEUDO- */
/* OPs (e.g., IFDEF).  During pass 1 of the assembly, the symbol is either    */
/* defined or undefined.  The condition bit is set when the symbol is defined */
/* during pass 1 and reset on pass 2 at the location the symbol was defined   */
/* during pass 1.  When processing conditionals during pass 2, if the symbol  */
/* is defined and the condition bit is set, the symbol is treated as if it    */
/* were undefined.  This gives consistent behavior of the conditional         */
/* pseudo-ops during both pass 1 and pass 2.                                  */
enum symtyp
{
  UNDEFINED = 0000,
  DEFINED   = 0001,
  FIXED     = 0002,
  MRI       = 0004    | DEFINED,
  LABEL     = 0010    | DEFINED,
  REDEFINED = 0020    | DEFINED,
  DUPLICATE = 0040    | DEFINED,
  PSEUDO    = 0100    | FIXED | DEFINED,
  CONDITION = 0200    | DEFINED,
  PERM_REDEFINED = 0400,
  MRIFIX    = MRI     | FIXED | DEFINED,
  DEFFIX    = DEFINED | FIXED
};
typedef enum symtyp SYMTYP;

enum pseudo_t
{
  BANK,    BINPUNCH, DECIMAL, DUBL,    EJECT,    ENPUNCH, EXPUNGE, FIELD,
  FIXMRI,  FIXTAB,   FLTG,    IFDEF,   IFNDEF,   IFNZERO, IFZERO,  NOPUNCH,
  OCTAL,   PAGE,     PAUSE,   RELOC,   RIMPUNCH, SEGMNT,  TEXT,    TITLE,
  XLIST,   ZBLOCK, FILENAME, DEVICE, ASCII
};
typedef enum pseudo_t PSEUDO_T;

struct sym_t
{
  SYMTYP  type;
  char    name[SYMLEN];
  WORD16  val;
  int     xref_index;
  int     xref_count;
};
typedef struct sym_t SYM_T;

struct lpool_t
{
  WORD16  loc;
  WORD16  last_punched;
  WORD16  pool[PAGE_SIZE];
};
typedef struct lpool_t LPOOL_T;

struct emsg_t
{
  char  *list;
  char  *file;
};
typedef struct emsg_t EMSG_T;

struct errsave_t
{
  char  *mesg;
  int    col;
};
typedef struct errsave_t ERRSAVE_T;

struct fltg_
{
  WORD16 exponent;
  WORD32 mantissa;
};
typedef struct fltg_ FLTG_T;

/*----------------------------------------------------------------------------*/

/* Function Prototypes                                                        */

int     binarySearch( char *name, int start, int symbol_count );
int     compareSymbols( const void *a, const void *b );
void    conditionFalse( void );
void    conditionTrue( void );
SYM_T  *defineLexeme( int start, int term, WORD16 val, SYMTYP type );
SYM_T  *defineSymbol( char *name, WORD16 val, SYMTYP type, WORD16 start);
void    endOfBinary( void );
void    errorLexeme( EMSG_T *mesg, int col );
void    errorMessage( EMSG_T *mesg, int col );
void    errorSymbol( EMSG_T *mesg, char *name, int col );
SYM_T  *eval( void );
WORD32  evalDubl( WORD32 initial_value );
FLTG_T *evalFltg( void );
SYM_T  *evalSymbol( void );
void    getArgs( int argc, char *argv[] );
WORD32  getDublExpr( void );
WORD32  getDublExprs( void );
FLTG_T *getFltgExpr( void );
FLTG_T *getFltgExprs( void );
SYM_T  *getExpr( void );
WORD16  getExprs( void );
WORD16  incrementClc( void );
void    inputDubl( void );
void    inputFltg( void );
WORD16  insertLiteral( LPOOL_T *pool, WORD16 value, int fieldpage_index );
char   *lexemeToName( char *name, int from, int term );
void    listLine( void );
SYM_T  *lookup( char *name );
void    moveToEndOfLine( void );
void    nextLexBlank( void );
void    nextLexeme( void );
void    normalizeFltg( FLTG_T *fltg );
void    onePass( void );
void    printCrossReference( void );
void    printErrorMessages( void );
void    printLine(char *line, WORD16 loc, WORD16 val, LINESTYLE_T linestyle);
void    printPageBreak( void );
void    printPermanentSymbolTable( void );
void    printSymbolTable( void );
BOOL    pseudoOperators( PSEUDO_T val );
void    punchChecksum( void );
void    punchLocObject( WORD16 loc, WORD16 val );
void    punchLiteralPool( LPOOL_T *p, BOOL punch_page0 );
void    punchOutObject( WORD16 loc, WORD16 val );
void    punchLeader( int count );
void    punchObject( WORD16 val );
void    punchOrigin( WORD16 loc );
void    readLine( void );
void    saveError( char *mesg, int cc );
BOOL    testForLiteralCollision( WORD16 loc );
void    topOfForm( char *title, char *sub_title );

/*----------------------------------------------------------------------------*/

/* Table of pseudo-ops (directives) which are used to setup the symbol        */
/* table on startup and when the EXPUNGE pseudo-op is executed.               */
SYM_T pseudo[] =
{
  { PSEUDO, "ASCII",  ASCII   },    /* Put 8-bit ASCII into memory (see TEXT) */
  { PSEUDO, "BINPUN", BINPUNCH },   /* Output in Binary Loader format.        */
  { PSEUDO, "DECIMA", DECIMAL },    /* Read literal constants in base 10.     */
  { PSEUDO, "DEVICE", DEVICE },     /* Pack 6 bit device name into memory     */
  { PSEUDO, "DUBL",   DUBL    },    /* Ignored (unsupported).                 */
  { PSEUDO, "EJECT",  EJECT   },    /* Eject a page in the listing.           */
  { PSEUDO, "ENPUNC", ENPUNCH },    /* Turn on object code generation.        */
  { PSEUDO, "EXPUNG", EXPUNGE },    /* Remove all symbols from symbol table.  */
  { PSEUDO, "FIELD",  FIELD   },    /* Set origin to memory field.            */
  { PSEUDO, "FILENA", FILENAME },   /* Pack 6 bit filename into memory.       */
  { PSEUDO, "FIXMRI", FIXMRI  },    /* Like =, but creates mem ref instruction*/
  { PSEUDO, "FIXTAB", FIXTAB  },    /* Mark current symbols as permanent.     */
  { PSEUDO, "FLTG",   FLTG    },    /* Ignored (unsupported).                 */
  { PSEUDO, "IFDEF",  IFDEF   },    /* Assemble if symbol is defined.         */
  { PSEUDO, "IFNDEF", IFNDEF  },    /* Assemble if symbol is not defined.     */
  { PSEUDO, "IFNZER", IFNZERO },    /* Assemble if symbol value is not 0.     */
  { PSEUDO, "IFNZRO", IFNZERO },    /* Assemble if symbol value is not 0.     */
  { PSEUDO, "IFZERO", IFZERO  },    /* Assemble if symbol value is 0.         */
  { PSEUDO, "NOPUNC", NOPUNCH },    /* Turn off object code generation.       */
  { PSEUDO, "OCTAL",  OCTAL   },    /* Read literal constants in base 8.      */
  { PSEUDO, "PAGE",   PAGE    },    /* Set orign to page +1 or page n (0..37).*/
  { PSEUDO, "PAUSE",  PAUSE   },    /* Ignored                                */
  { PSEUDO, "RELOC",  RELOC   },    /* Assemble to run at a different address.*/
  { PSEUDO, "RIMPUN", RIMPUNCH },   /* Output in Read In Mode format.         */
  { PSEUDO, "SEGMNT", SEGMNT  },    /* Like page, but with page size=1K words.*/
  { PSEUDO, "TEXT",   TEXT    },    /* Pack 6 bit trimmed ASCII into memory.  */
  { PSEUDO, "XLIST",  XLIST   },    /* Toggle listing generation.             */
  { PSEUDO, "ZBLOCK", ZBLOCK  },    /* Zero a block of memory.                */
  { PSEUDO, "TITLE",  TITLE   },    /* Use the text string as a listing title.*/
  { PSEUDO, "BANK",   BANK    }     /* Like field, select some 32K out of 128K*/
};
/* Number o extended pseudo operators to ignore unless command option specified
 * to enable */
#define NUMBER_ADDITIONAL_PSEUDO 2

/* Symbol Table                                                               */
/* The table is put in lexical order on startup, so symbols can be            */
/* inserted as desired into the initial table.                                */
/* really_permanent_symbols aren't removed by EXPUNGE                         */
SYM_T really_permanent_symbols[] =
{
  { MRIFIX, "I",      00400   },    /* INDIRECT ADDRESSING                    */
  { MRIFIX, "Z",      00000   }     /* PAGE ZERO ADDRESS                      */
};

SYM_T permanent_symbols[] =
{
  /* Memory Reference Instructions                                            */
  { MRIFIX, "AND",    00000   },    /* LOGICAL AND                            */
  { MRIFIX, "TAD",    01000   },    /* TWO'S COMPLEMENT ADD                   */
  { MRIFIX, "ISZ",    02000   },    /* INCREMENT AND SKIP IF ZERO             */
  { MRIFIX, "DCA",    03000   },    /* DEPOSIT AND CLEAR ACC                  */
  { MRIFIX, "JMP",    05000   },    /* JUMP                                   */
  { MRIFIX, "JMS",    04000   },    /* JUMP TO SUBROUTINE                     */
  /* Floating Point Interpreter Instructions                                  */
  { MRIFIX, "FEXT",   00000   },    /* FLOATING EXIT                          */
  { MRIFIX, "FADD",   01000   },    /* FLOATING ADD                           */
  { MRIFIX, "FSUB",   02000   },    /* FLOATING SUBTRACT                      */
  { MRIFIX, "FMPY",   03000   },    /* FLOATING MULTIPLY                      */
  { MRIFIX, "FDIV",   04000   },    /* FLOATING DIVIDE                        */
  { MRIFIX, "FGET",   05000   },    /* FLOATING GET                           */
  { MRIFIX, "FPUT",   06000   },    /* FLOATING PUT                           */
  { FIXED,  "FNOR",   07000   },    /* FLOATING NORMALIZE                     */
  { FIXED,  "FEXT",   00000   },    /* EXIT FROM FLOATING POINT INTERPRETER   */
  { FIXED,  "SQUARE", 00001   },    /* SQUARE C(FAC)                          */
  { FIXED,  "SQROOT", 00002   },    /* TAKE SQUARE ROOT OF C(FAC)             */
  /* Group 1 Operate Microinstrcutions                                        */
  { FIXED,  "OPR",    07000   },    /* NO OPERATION                           */
  { FIXED,  "NOP",    07000   },    /* NO OPERATION                           */
  { FIXED,  "IAC",    07001   },    /* INCREMENT AC                           */
  { FIXED,  "RAL",    07004   },    /* ROTATE AC AND LINK LEFT ONE            */
  { FIXED,  "RTL",    07006   },    /* ROTATE AC AND LINK LEFT TWO            */
  { FIXED,  "RAR",    07010   },    /* ROTATE AC AND LINK RIGHT ONE           */
  { FIXED,  "RTR",    07012   },    /* ROTATE AC AND LINK RIGHT TWO           */
  { FIXED,  "CML",    07020   },    /* COMPLEMENT LINK                        */
  { FIXED,  "CMA",    07040   },    /* COMPLEMEMNT AC                         */
  { FIXED,  "CLL",    07100   },    /* CLEAR LINK                             */
  { FIXED,  "CLA",    07200   },    /* CLEAR AC                               */
  /* Group 2 Operate Microinstructions                                        */
  { FIXED,  "BSW",    07002   },    /* Swap bytes in AC (PDP/8e)              */
  { FIXED,  "HLT",    07402   },    /* HALT THE COMPUTER                      */
  { FIXED,  "OSR",    07404   },    /* INCLUSIVE OR SR WITH AC                */
  { FIXED,  "SKP",    07410   },    /* SKIP UNCONDITIONALLY                   */
  { FIXED,  "SNL",    07420   },    /* SKIP ON NON-ZERO LINK                  */
  { FIXED,  "SZL",    07430   },    /* SKIP ON ZERO LINK                      */
  { FIXED,  "SZA",    07440   },    /* SKIP ON ZERO AC                        */
  { FIXED,  "SNA",    07450   },    /* SKIP ON NON=ZERO AC                    */
  { FIXED,  "SMA",    07500   },    /* SKIP MINUS AC                          */
  { FIXED,  "SPA",    07510   },    /* SKIP ON POSITIVE AC (ZERO IS POSITIVE) */
  /* Combined Operate Microinstructions                                       */
  { FIXED,  "CIA",    07041   },    /* COMPLEMENT AND INCREMENT AC            */
  { FIXED,  "STL",    07120   },    /* SET LINK TO 1                          */
  { FIXED,  "GLK",    07204   },    /* GET LINK (PUT LINK IN AC BIT 11)       */
  { FIXED,  "STA",    07240   },    /* SET AC TO -1                           */
  { FIXED,  "LAS",    07604   },    /* LOAD ACC WITH SR                       */
  /* MQ Instructions (PDP/8e)                                                 */
  { FIXED,  "MQL",    07421   },    /* Load MQ from AC, then clear AC.        */
  { FIXED,  "MQA",    07501   },    /* Inclusive OR MQ with AC                */
  { FIXED,  "SWP",    07521   },    /* Swap AC and MQ                         */
  { FIXED,  "ACL",    07701   },    /* Load MQ into AC                        */
  /* Program Interrupt                                                        */
  { FIXED,  "IOT",    06000   },
  { FIXED,  "ION",    06001   },    /* TURN INTERRUPT PROCESSOR ON            */
  { FIXED,  "IOF",    06002   },    /* TURN INTERRUPT PROCESSOR OFF           */
  /* Program Interrupt, PDP-8/e                                               */
  { FIXED,  "SKON",   06000   },    /* Skip if interrupt on and turn int off. */
  { FIXED,  "SRQ",    06003   },    /* Skip on interrupt request.             */
  { FIXED,  "GTF",    06004   },    /* Get interrupt flags.                   */
  { FIXED,  "RTF",    06005   },    /* Restore interrupt flags.               */
  { FIXED,  "SGT",    06006   },    /* Skip on greater than flag.             */
  { FIXED,  "CAF",    06007   },    /* Clear all flags.                       */
  /* Keyboard/Reader                                                          */
  { FIXED,  "KCF",    06030   },    /* CLEAR KEYBOAR FLAG                     */
  { FIXED,  "KSF",    06031   },    /* SKIP ON KEYBOARD FLAG                  */
  { FIXED,  "KCC",    06032   },    /* CLEAR KEYBOARD FLAG & READ CHAR        */
  { FIXED,  "KRS",    06034   },    /* READ KEYBOARD BUFFER (STATIC)          */
  { FIXED,  "KIE",    06035   },    /* AC11 TO KEYBD/RDR INT ENABLE F/F       */
  { FIXED,  "KRB",    06036   },    /* READ KEYBOARD BUFFER & CLEAR FLAG      */
  /* Teleprinter/Punch                                                        */
  { FIXED,  "TFL",    06040   },    /* SET TELEPRINTER/PUNCH FLAG             */
  { FIXED,  "TSF",    06041   },    /* SKIP ON TELEPRINTER FLAG               */
  { FIXED,  "TCF",    06042   },    /* CLEAR TELEPRINTER FLAG                 */
  { FIXED,  "TPC",    06044   },    /* LOAD TELEPRINTER & PRINT               */
  { FIXED,  "TSK",    06045   },    /* SKIP IF TELETYPE INTERRUPT             */
  { FIXED,  "TLS",    06046   },    /* LOAD TELPRINTER & CLEAR FLAG           */
  /* High Speed Paper Tape Reader                                             */
  { FIXED,  "RSF",    06011   },    /* SKIP ON READER FLAG                    */
  { FIXED,  "RRB",    06012   },    /* READ READER BUFFER AND CLEAR FLAG      */
  { FIXED,  "RFC",    06014   },    /* READER FETCH CHARACTER                 */
  /* PC8-E High Speed Paper Tape Reader & Punch                               */
  { FIXED,  "RPE",    06010   },    /* Set interrupt enable for reader/punch  */
  { FIXED,  "PCE",    06020   },    /* Clear interrupt enable for rdr/punch   */
  { FIXED,  "RCC",    06016   },    /* Read reader buffer, clear flags & buf, */
                                    /* and fetch character.                   */
  /* High Speed Paper Tape Punch                                              */
  { FIXED,  "PSF",    06021   },    /* SKIP ON PUNCH FLAG                     */
  { FIXED,  "PCF",    06022   },    /* CLEAR ON PUNCH FLAG                    */
  { FIXED,  "PPC",    06024   },    /* LOAD PUNCH BUFFER AND PUNCH CHARACTER* */
  { FIXED,  "PLS",    06026   },    /* LOAD PUNCH BUFFER AND CLEAR FLAG       */

  /* DECassette TU60 (RK 20071008)                                            */
  { FIXED,  "KCLR",   06700   },    /* Clear all (clear A and B)              */
  { FIXED,  "KSDR",   06701   },    /* Skip if data flag set                  */
  { FIXED,  "KSEN",   06702   },    /* Skip if EOT/BOT, not ready, or empty   */
  { FIXED,  "KSBF",   06703   },    /* Skip if ready flag set                 */
  { FIXED,  "KLSA",   06704   },    /* AC4-11 -> A, clear A, -(AC4-11) -> A   */
  { FIXED,  "KSAF",   06705   },    /* Skip on any flag or error              */
  { FIXED,  "KGOA",   06706   },    /* Assert status A and transfer data to AC*/
  { FIXED,  "KRSB",   06707   },    /* Transfer B -> AC4-11                   */

  /* DECtape Transport Type TU55 and DECtape Control Type TC01                */
  { FIXED,  "DTRA",   06761   },    /* Contents of status register is ORed    */
                                    /* into AC bits 0-9                       */
  { FIXED,  "DTCA",   06762   },    /* Clear status register A, all flags     */
                                    /* undisturbed                            */
  { FIXED,  "DTXA",   06764   },    /* Status register A loaded by exclusive  */
                                    /* OR from AC.  If AC bit 10=0, clear     */
                                    /* error flags; if AC bit 11=0, DECtape   */
                                    /* control flag is cleared.               */
  { FIXED,  "DTLA",   06766   },    /* Combination of DTCA and DTXA           */
  { FIXED,  "DTSF",   06771   },    /* Skip if error flag is 1 or if DECtape  */
                                    /* control flag is 1                      */
  { FIXED,  "DTRB",   06772   },    /* Contents of status register B is       */
                                    /* ORed into AC                           */
  { FIXED,  "DTLB",   06774   },    /* Memory field portion of status         */
                                    /* register B loaded from AC bits 6-8     */
  /* Disk File and Control, Type DF32                                         */
  { FIXED,  "DCMA",   06601   },    /* CLEAR DISK MEMORY REQUEST AND          */
                                    /* INTERRUPT FLAGS                        */
  { FIXED,  "DMAR",   06603   },    /* LOAD DISK FROM AC, CLEAR AC READ       */
                                    /* INTO CORE, CLEAR INTERRUPT FLAG        */
  { FIXED,  "DMAW",   06605   },    /* LOAD DISK FROM AC, WRITE ONTO DISK     */
                                    /* FROM CORE, CLEAR INTERRUPT FLAG        */
  { FIXED,  "DCEA",   06611   },    /* CLEAR DISK EXTENDED ADDRESS AND        */
  { FIXED,  "DSAC",   06612   },    /* SKIP IF ADDRESS CONFIRMED FLAG = 1     */
                                    /* MEMORY ADDRESS EXTENSION REGISTER      */
  { FIXED,  "DEAL",   06615   },    /* CLEAR DISK EXTENDED ADDRESS AND        */
                                    /* MEMORY ADDRESS EXTENSION REGISTER      */
                                    /* AND LOAD SAME FROM AC                  */
  { FIXED,  "DEAC",   06616   },    /* CLEAR AC, LOAD AC FROM DISK EXTENDED   */
                                    /* ADDRESS REGISTER, SKIP IF ADDRESS      */
                                    /* CONFIRMED FLAG = 1                     */
  { FIXED,  "DFSE",   06621   },    /* SKIP IF PARITY ERROR, DATA REQUEST     */
                                    /* LATE, OR WRITE LOCK SWITCH FLAG = 0    */
                                    /* (NO ERROR)                             */
  { FIXED,  "DFSC",   06622   },    /* SKIP IF COMPLETION FLAG = 1 (DATA      */
                                    /* TRANSFER COMPLETE)                     */
  { FIXED,  "DMAC",   06626   },    /* CLEAR AC, LOAD AC FROM DISK MEMORY     */
                                    /* ADDRESS REGISTER                       */
  /* Disk File and Control, Type RF08                                         */
  { FIXED,  "DCIM",   06611   },
  { FIXED,  "DIML",   06615   },
  { FIXED,  "DIMA",   06616   },
  { FIXED,  "DISK",   06623   },
  { FIXED,  "DCXA",   06641   },
  { FIXED,  "DXAL",   06643   },
  { FIXED,  "DXAC",   06645   },
  { FIXED,  "DMMT",   06646   },
  /* Memory Extension Control, Type 183                                       */
  { FIXED,  "CDF",    06201   },    /* CHANGE DATA FIELD                      */
  { FIXED,  "CIF",    06202   },    /* CHANGE INSTRUCTION FIELD               */
  { FIXED,  "CDI",    06203   },    /* Change data & instrution field.        */
  { FIXED,  "RDF",    06214   },    /* READ DATA FIELD                        */
  { FIXED,  "RIF",    06224   },    /* READ INSTRUCTION FIELD                 */
  { FIXED,  "RIB",    06234   },    /* READ INTERRUPT BUFFER                  */
  { FIXED,  "RMF",    06244   },    /* RESTORE MEMORY FIELD                   */
  /* Memory Parity, Type MP8/I (MP8/L)                                        */
  { FIXED,  "SMP",    06101   },    /* SKIP IF MEMORY PARITY FLAG = 0         */
  { FIXED,  "CMP",    06104   },    /* CLEAR MEMORY PAIRTY FLAG               */
  /* Memory Parity, Type MP8-E (PDP8/e)                                       */
  { FIXED,  "DPI",    06100   },    /* Disable parity interrupt.              */
  { FIXED,  "SNP",    06101   },    /* Skip if no parity error.               */
  { FIXED,  "EPI",    06103   },    /* Enable parity interrupt.               */
  { FIXED,  "CNP",    06104   },    /* Clear parity error flag.               */
  { FIXED,  "CEP",    06106   },    /* Check for even parity.                 */
  { FIXED,  "SPO",    06107   },    /* Skip on parity option.                 */
  /* Data Communications Systems, Type 680I                                   */
  { FIXED,  "TTINCR", 06401   },    /* The content of the line select         */
                                    /* register is incremented by one.        */
  { FIXED,  "TTI",    06402   },    /* The line status word is read and       */
                                    /* sampled.  If the line is active for    */
                                    /* the fourth time, the line bit is       */
                                    /* shifted into the character assembly    */
                                    /* word.  If the line bit is active for   */
                                    /* a number of times less than four,      */
                                    /* the count is incremented.  If the      */
                                    /* line is not active, the active/inac-   */
                                    /* tive status of the line is recorded    */
  { FIXED,  "TTO",    06404   },    /* The character in the AC is shifted     */
                                    /* right one position, zeros are shifted  */
                                    /* into vacated positions, and the orig-  */
                                    /* inal content of AC11 is transferred    */
                                    /* out of the computer on the TTY line.   */
  { FIXED,  "TTCL",   06411   },    /* The line select register is cleared.   */
  { FIXED,  "TTSL",   06412   },    /* The line select register is loaded by  */
                                    /* an OR transfer from the content of     */
                                    /* of AC5-11, the the AC is cleared.      */
  { FIXED,  "TTRL",   06414   },    /* The content of the line select regis-  */
                                    /* ter is read into AC5-11 by an OR       */
                                    /* transfer.                              */
  { FIXED,  "TTSKP",  06421   },    /* Skip if clock flag is a 1.             */
  { FIXED,  "TTXON",  06424   },    /* Clock 1 is enabled to request a prog-  */
                                    /* ram interrupt and clock 1 flag is      */
                                    /* cleared.                               */
  { FIXED,  "TTXOF",  06422   },    /* Clock 1 is disabled from causing a     */
                                    /* program interrupt and clock 1 flag     */
                                    /* is cleared.                            */
};      /* End-of-Symbols for Permanent Symbol Table                          */

/* Global variables                                                           */
SYM_T *symtab;                  /* Symbol Table                               */
int    symbol_top;              /* Number of entries in symbol table.         */

SYM_T *fixed_symbols;           /* Start of the fixed symbol table entries.   */
int    number_of_fixed_symbols;

/*----------------------------------------------------------------------------*/

WORD16 *xreftab;                /* Start of the concordance table.            */

ERRSAVE_T error_list[20];
int     save_error_count;

#define GET_PAGE_INDEX(x) (((x) & 07600) >> 7)
#define MAX_PAGES 32
LPOOL_T cp[MAX_PAGES];             /* Storage for page constants.       */
int     max_page_used[MAX_PAGES];

char   s_detected[] = "detected";
char   s_error[]    = "error";
char   s_errors[]   = "errors";
char   s_no[]       = "No";
char   s_page[]     = "Page";
char   s_symtable[] = "Symbol Table";
char   s_xref[]     = "Cross Reference";
char   s_generated[] = "generated";
char   s_link[]    = "link";
char   s_links[]   = "links";

/* Assembler diagnostic messages.                                             */
/* Some attempt has been made to keep continuity with the PAL-III and         */
/* MACRO-8 diagnostic messages.  If a diagnostic indicator, (e.g., IC)        */
/* exists, then the indicator is put in the listing as the first two          */
/* characters of the diagnostic message.  The PAL-III indicators where used   */
/* when there was a choice between using MACRO-8 and PAL-III indicators.      */
/* The character pairs and their meanings are:                                */
/*      DT  Duplicate Tag (symbol)                                            */
/*      IC  Illegal Character                                                 */
/*      ID  Illegal Redefinition of a symbol.  An attempt was made to give    */
/*          a symbol a new value not via =.                                   */
/*      IE  Illegal Equals  An equal sign was used in the wrong context,      */
/*          (e.g., A+B=C, or TAD A+=B)                                        */
/*      II  Illegal Indirect  An off page reference was made, but a literal   */
/*          could not be generated because the indirect bit was already set.  */
/*      IR  Illegal Reference (address is not on current page or page zero)   */
/*      ND  No $ (the program terminator) at end of file.                     */
/*      PE  Current, Non-Zero Page Exceeded (literal table flowed into code)  */
/*      RD  ReDefintion of a symbol                                           */
/*      ST  Symbol Table full                                                 */
/*      UA  Undefined Address (undefined symbol)                              */
/*      ZE  Zero Page Exceeded (see above, or out of space)                   */
EMSG_T  duplicate_label     = { "DT duplicate",  "duplicate label" };
EMSG_T  illegal_blank       = { "IC illegal blank", "illegal blank" };
EMSG_T  illegal_character   = { "IC illegal char",  "illegal character" };
EMSG_T  illegal_expression  = { "IC in expression", "illegal expression" };
EMSG_T  label_syntax        = { "IC label syntax",  "label syntax" };
EMSG_T  not_a_number        = { "IC numeric syntax", "numeric syntax of" };
EMSG_T  number_not_radix    = { "IC radix", "number not in current radix"};
EMSG_T  symbol_syntax       = { "IC symbol syntax", "symbol syntax" };
EMSG_T  illegal_equals      = { "IE illegal =",  "illegal equals" };
EMSG_T  illegal_indirect    = { "II off page",   "illegal indirect" };
EMSG_T  illegal_reference   = { "IR off page",   "illegal reference" };
EMSG_T  undefined_symbol    = { "UD undefined",  "undefined symbol" };
EMSG_T  redefined_symbol    = { "RD redefined",  "redefined symbol" };
EMSG_T  illegal_redefine    = { "ID redefined",  "Illegal redefine of symbol" };
EMSG_T  literal_overflow    = { "PE page exceeded",
                                   "current page literal capacity exceeded" };
EMSG_T  pz_literal_overflow = { "ZE page exceeded",
                                   "page zero capacity exceeded" };
EMSG_T  dubl_overflow       = { "dubl overflow",  "DUBL value overflow" };
EMSG_T  fltg_overflow       = { "fltg overflow",  "FLTG value overflow" };
EMSG_T  zblock_too_small    = { "expr too small", "ZBLOCK value too small" };
EMSG_T  zblock_too_large    = { "expr too large", "ZBLOCK value too large" };
EMSG_T  end_of_file         = { "ND no $ at EOF", "No $ at End-of-File" };
EMSG_T  no_pseudo_op        = { "not implemented",
                                   "not implemented pseudo-op" };
EMSG_T  illegal_field_value = { "expr out of range",
                                   "field value not in range of 0 through 7" };
EMSG_T  literal_gen_off     = { "literals off",
                                                "literal generation is off" };
EMSG_T  no_literal_value    = { "no value",  "no literal value" };
EMSG_T  text_string         = { "no delimiter",
                                    "text string delimiters not matched" };
EMSG_T  in_rim_mode         = { "not OK in rim mode"
                                    "FIELD pseudo-op not valid in RIM mode" };
EMSG_T  lt_expected         = { "'<' expected",  "'<' expected" };
EMSG_T  symbol_table_full   = { "ST Symbol Tbl Full",
                                                    "Symbol Table Full" };
/*----------------------------------------------------------------------------*/

FILE   *errorfile;
FILE   *infile;
FILE   *listfile;
FILE   *listsave;
FILE   *objectfile;
FILE   *objectsave;

char    errorpathname[NAMELEN];
char    filename[NAMELEN];
char    listpathname[NAMELEN];
char    objectpathname[NAMELEN];
char   *pathname;
char    permpathname[NAMELEN];

int     tabstops;               /* number of characters to expand a tab to   */
int     list_lineno;
int     list_pageno;
char    list_title[LINELEN];
BOOL    list_title_set;         /* Set if TITLE pseudo-op used.               */
char    line[LINELEN];          /* Input line.                                */
int     lineno;                 /* Current line number.                       */
int     page_lineno;            /* print line number on current page.         */
BOOL    listed;                 /* Listed flag.                               */
BOOL    listedsave;

int     cc;                     /* Column Counter (char position in line).    */
WORD16  checksum;               /* Generated checksum                         */
BOOL    binary_data_output;     /* Set true when data has been output.        */
WORD16  clc;                    /* Location counter                           */
WORD16  cplc;                   /* Current page literal counter.              */
char    delimiter;              /* Character immediately after eval'd term.   */
int     errors;                 /* Number of errors found so far.             */
int     links;                  /* Number of links generated so far.          */
BOOL    error_in_line;          /* TRUE if error on current line.             */
int     errors_pass_1;          /* Number of errors on pass 1.                */
WORD16  field;                  /* Current field                              */
WORD16  fieldlc;                /* location counter without field portion.    */
BOOL    fltg_input;             /* TRUE when doing floating point input.      */
BOOL    indirect_generated;     /* TRUE if an off page address generated.     */
int     last_xref_lexstart;     /* Column where last xref symbol was located. */
int     last_xref_lineno;       /* Line where last xref symbol was located.   */
int     lexstartprev;           /* Where previous lexeme started.             */
int     lextermprev;            /* Where previous lexeme ended.               */
int     lexstart;               /* Index of current lexeme on line.           */
int     lexterm;                /* Index of character after current lexeme.   */
BOOL    literals_ok;            /* Generate literals, ignore ID redefine err  */
BOOL    perm_redef_error;       /* Make redefining perm sym with labels error */
int     maxcc;                  /* Current line length.                       */
BOOL    overflow;               /* Overflow flag for math routines.           */
int     pass;                   /* Number of current pass.                    */
BOOL    print_permanent_symbols;
WORD16  pzlc;                   /* Page Zero literal counter.                 */
WORD16  radix;                  /* Default number radix.                      */
WORD16  reloc;                  /* The relocation distance.                   */
BOOL    rim_mode;               /* Generate rim format, defaults to bin       */
BOOL    dollar_not_required;    /* $ not required at end of file              */
BOOL    additional_enabled;     /* True if extended functions over PAL8       */
                                /* enabled                                    */
BOOL    symtab_print;           /* Print symbol table flag                    */
BOOL    xref;

FLTG_T  fltg_ac;                /* Value holder for evalFltg()                */
SYM_T   sym_eval = { DEFINED, "", 0 };       /* Value holder for eval()       */
SYM_T   sym_getexpr = { DEFINED, "", 0 };    /* Value holder for getexpr()    */
SYM_T   sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator       */


/******************************************************************************/
/*                                                                            */
/*  Function:  main                                                           */
/*                                                                            */
/*  Synopsis:  Starting point.  Controls order of assembly.                   */
/*                                                                            */
/******************************************************************************/
int main( int argc, char *argv[] )
{
  int     ix;
  int     space;

  /* Set the default values for global symbols.                               */
  binary_data_output = FALSE;
  fltg_input = FALSE;
  literals_ok = TRUE;
  perm_redef_error = FALSE;
  print_permanent_symbols = FALSE;
  rim_mode = FALSE;
  dollar_not_required = FALSE;
  additional_enabled = FALSE;
  symtab_print = FALSE;
  xref = FALSE;
  pathname = NULL;

  /* Get the options and pathnames                                            */
  getArgs( argc, argv );

  /* Setup the error file in case symbol table overflows while installing the */
  /* permanent symbols.                                                       */
  errorfile = fopen( errorpathname, "w" );
  if (errorfile == NULL) {
    fprintf( stderr, "Could not open error file %s: %s\n", errorpathname, strerror(errno));
    exit( -1 );

  }
  errors = 0;
  save_error_count = 0;
  pass = 0;             /* This is required for symbol table initialization.  */
  symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE );

  if( symtab == NULL )
  {
    fprintf( stderr, "Could not allocate memory for symbol table.\n");
    exit( -1 );
  }

  /* Place end marker in symbol table.                                        */
  symtab[0] = sym_undefined;
  symbol_top = 0;
  number_of_fixed_symbols = symbol_top;
  fixed_symbols = &symtab[symbol_top - 1];

  /* Enter the pseudo-ops into the symbol table                               */
  for( ix = 0; ix < DIM( pseudo ) - 
        (additional_enabled ? 0 : NUMBER_ADDITIONAL_PSEUDO) ; ix++ )
  {
    defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 );
  }

  /* Enter the predefined symbols into the table.                             */
  /* Also make them part of the permanent symbol table.                       */
  for( ix = 0; ix < DIM( really_permanent_symbols ); ix++ )
  {
    defineSymbol( really_permanent_symbols[ix].name,
                  really_permanent_symbols[ix].val,
                  really_permanent_symbols[ix].type | DEFFIX , 0 );
  }

  /* Enter the predefined symbols into the table.                             */
  /* Also make them part of the permanent symbol table.                       */
  for( ix = 0; ix < DIM( permanent_symbols ); ix++ )
  {
    defineSymbol( permanent_symbols[ix].name,
                  permanent_symbols[ix].val,
                  permanent_symbols[ix].type | DEFFIX , 0 );
  }

  number_of_fixed_symbols = symbol_top;
  fixed_symbols = &symtab[symbol_top - 1];

  /* Do pass one of the assembly                                              */
  checksum = 0;
  pass = 1;
  page_lineno = LIST_LINES_PER_PAGE;
  onePass();
  errors_pass_1 = errors;

  /* Set up for pass two                                                      */
  rewind( infile );
  /*Opened in main errorfile = fopen( errorpathname, "w" );*/
  objectfile = fopen( objectpathname, "wb" );
  if (objectfile == NULL) {
    fprintf( stderr, "Could not open object file %s: %s\n", objectpathname, strerror(errno));
    exit( -1 );

  }
  objectsave = objectfile;

  listfile = fopen( listpathname, "w" );
  if (listfile == NULL) {
    fprintf( stderr, "Could not open list file %s: %s\n", listpathname, strerror(errno));
    exit( -1 );

  }
  listsave = NULL;

  punchLeader( 0 );
  checksum = 0;

  /* Do pass two of the assembly                                              */
  errors = 0;
  save_error_count = 0;
  page_lineno = LIST_LINES_PER_PAGE;

  if( xref )
  {
    /* Get the amount of space that will be required for the concordance.     */
    for( space = 0, ix = 0; ix < symbol_top; ix++ )
    {
      symtab[ix].xref_index = space;    /* Index into concordance table.      */
      space += symtab[ix].xref_count + 1;
      symtab[ix].xref_count = 0;        /* Clear the count for pass 2.        */

    }
    /* Allocate the necessary space.                                          */
    xreftab = (WORD16 *) malloc( sizeof( WORD16 ) * space );

    /* Clear the cross reference space.                                       */
    for( ix = 0; ix < space; ix++ )
    {
      xreftab[ix] = 0;
    }
  }
  pass = 2;
  onePass();

  /* Undo effects of NOPUNCH for any following checksum                       */
  objectfile = objectsave;
  punchChecksum();

  /* Works great for trailer.                                                 */
  punchLeader( 8 );

  /* undo effects of XLIST for any following output to listing file.          */
  if( listfile == NULL )
  {
    listfile = listsave;
  }

  /* Display value of error counter.                                          */
  if( errors == 0 )
  {
    fprintf( listfile, "\n      %s %s %s\n", s_no, s_detected, s_errors );
  }
  else
  {
    fprintf( errorfile, "\n      %d %s %s\n", errors, s_detected,
                                        ( errors == 1 ? s_error : s_errors ));
    fprintf( listfile, "\n      %d %s %s\n", errors, s_detected,
                                        ( errors == 1 ? s_error : s_errors ));
    fprintf( stderr,   "      %d %s %s\n", errors, s_detected,
                                        ( errors == 1 ? s_error : s_errors ));
  }
  /* Display value of link counter.                                          */
  if( links == 0 )
  {
    fprintf( listfile, "      %s %s %s\n", s_no, s_links, s_generated );
  }
  else
  {
    fprintf( errorfile, "      %d %s %s\n", links, 
                                        ( links == 1 ? s_link : s_links ), 
                                        s_generated);
    fprintf( listfile, "      %d %s %s\n", links, 
                                        ( links == 1 ? s_link : s_links ), 
                                        s_generated);
    fprintf( stderr, "      %d %s %s\n", links, 
                                        ( links == 1 ? s_link : s_links ), 
                                        s_generated);
  }

  if( symtab_print )
  {
    printSymbolTable();
  }

  if( print_permanent_symbols )
  {
    printPermanentSymbolTable();
  }

  if( xref )
  {
    printCrossReference();
  }

  fclose( objectfile );
  fclose( listfile );
  fclose( errorfile );
  if( errors == 0 && errors_pass_1 == 0 )
  {
    remove( errorpathname );
  }

  return( errors != 0 );
} /* main()                                                                   */

/******************************************************************************/
/*                                                                            */
/*  Function:  getArgs                                                        */
/*                                                                            */
/*  Synopsis:  Parse command line, set flags accordingly and setup input and  */
/*             output files.                                                  */
/*                                                                            */
/******************************************************************************/
void getArgs( int argc, char *argv[] )
{
  int  len;
  int  ix, jx;

  /* Set the defaults                                                         */
  errorfile = NULL;
  infile = NULL;
  listfile = NULL;
  listsave = NULL;
  objectfile = NULL;
  objectsave = NULL;
  tabstops = 8;

  for( ix = 1; ix < argc; ix++ )
  {
    if( argv[ix][0] == '-' )
    {
      for( jx = 1; argv[ix][jx] != 0; jx++ )
      {
        switch( argv[ix][jx] )
        {
        case '$':
          dollar_not_required = TRUE;
          break;

        case 'd':
          symtab_print = TRUE;
          break;

        case 'a':
          additional_enabled = TRUE;
          break;

        case 'r':
          rim_mode = TRUE;
          break;

        case 'e':
          literals_ok = FALSE;
          break;

        case 'l':
          literals_ok = TRUE;
          break;

        case 'n':
          perm_redef_error = TRUE;
          break;

        case 'p':
          print_permanent_symbols = TRUE;
          break;

        /* Added -tN; RK 20071029 */
        /* Damn, this is ugly, we should use getopt() */
        case 't':
          if (argv [ix][jx + 1]) {
            tabstops = atoi (argv [ix] + (jx + 1));
            /* advance past numbers */
            for (jx++; argv [ix][jx]; jx++) ;
            jx--;
          } else {
            ix++;
            if (ix >= argc) {
              fprintf( stderr, "%s: missing argument for -t, expected number of tabsopts\n", argv[0] );
              exit( -1 );
            }
            for (jx = 0; argv [ix][jx]; jx++) ;
            jx--;
            tabstops = atoi (argv [ix]);
          }
          break;

        case 'x':
          xref = TRUE;
          break;

        case 'v':
          fprintf( stderr, "%s\n", release );
          fflush( stderr );
          exit( -1 );
          break;

        default:
          fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] );
        case 'h':
          fprintf( stderr, " -$ -- allow file to not end with $\n" );
          fprintf( stderr, " -a -- enable additional function not in PAL8\n" );
          fprintf( stderr, " -d -- dump symbol table\n" );
          fprintf( stderr, " -e -- error if link generated\n" );
          fprintf( stderr, " -h -- show this help\n" );
          fprintf( stderr, " -l -- generate literal/link (default)\n" );
          fprintf( stderr, " -n -- no redefining with label permanent symbols\n" );
          fprintf( stderr, " -p -- output permanent symbols to file\n" );
          fprintf( stderr, " -r -- output rim format file\n" );
          fprintf( stderr, " -t N -- set tab stops to N\n" );
          fprintf( stderr, " -v -- display version\n" );
          fprintf( stderr, " -x -- output cross reference to file\n" );
          fflush( stderr );
          exit( -1 );
        } /* end switch                                                       */
      } /* end for                                                            */
    }
    else
    {
      if( pathname != NULL )
      {
        fprintf( stderr, "%s: too many input files\n", argv[0] );
        exit( -1 );
      }
      pathname = &argv[ix][0];
    }
  } /* end for                                                                */

  if( pathname == NULL )
  {
    fprintf( stderr, "%s:  no input file specified\n", argv[0] );
    exit( -1 );
  }

  len = strlen( pathname );
  if( len > NAMELEN - 5 )
  {
    fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname );
    exit( -1 );
  }

  /* Now open the input file.                                                 */
  if(( infile = fopen( pathname, "r" )) == NULL )
  {
    fprintf( stderr, "%s: cannot open \"%s\": %s\n", argv[0], pathname, strerror(errno) );
    exit( -1 );
  }

  /* Now make the pathnames                                                   */
  /* Find last '.', if it exists.                                             */
  jx = len - 1;
  while( pathname[jx] != '.'  && pathname[jx] != '/'
      && pathname[jx] != '\\' && jx >= 0 )
  {
    jx--;
  }

  switch( pathname[jx] )
  {
  case '.':
    break;

  case '/':
  case '\\':
    jx = len;
    break;

  default:
    break;
  }

  /* Add the pathname extensions.                                             */
  strncpy( objectpathname, pathname, jx );
  objectpathname[jx] = '\0';
  strcat( objectpathname, rim_mode ? ".rim" : ".bin" );

  strncpy( listpathname, pathname, jx );
  listpathname[jx] = '\0';
  strcat( listpathname, ".lst" );

  strncpy( errorpathname, pathname, jx );
  errorpathname[jx] = '\0';
  strcat( errorpathname, ".err" );

  strncpy( permpathname, pathname, jx );
  permpathname[jx] = '\0';
  strcat( permpathname, ".prm" );

  /* Extract the filename from the path.                                      */
  if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' )
  {
    pathname[1] = '\\';         /* MS-DOS style pathname                      */
  }

  jx = len - 1;
  while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 )
  {
    jx--;
  }
  strcpy( filename, &pathname[jx + 1] );

} /* getArgs()                                                                */

/******************************************************************************/
/*                                                                            */
/*  Function:  clearLiteralTable                                              */
/*                                                                            */
/*  Synopsis:  Clear the cp and max_page_used data storing literal            */
/*             information.                                                   */
/*                                                                            */
/******************************************************************************/
void clearLiteralTable()
{
  int i;

  for (i = 0; i < DIM(cp); i++)
  {
    cp[i].loc = 0200;             /* Points to end of page for [] operands.  */
    cp[i].last_punched = 0200;    /* Points to end of page for [] operands.  */
  }
  memset(max_page_used, 0, sizeof(max_page_used));
}

/******************************************************************************/
/*                                                                            */
/*  Function:  onePass                                                        */
/*                                                                            */
/*  Synopsis:  Do one assembly pass.                                          */
/*                                                                            */
/******************************************************************************/
void onePass()
{
  char    name[SYMLEN];
  WORD16  newclc;
  BOOL    scanning_line;
  int     start;
  SYM_T  *sym;
  int     term;
  WORD16  val;

  clc = 0200;                   /* Default starting address is 200 octal.     */
  field = 0;
  fieldlc = clc & 07777;
  reloc = 0;
  clearLiteralTable();
  listed = TRUE;
  lineno = 0;
  list_pageno = 0;
  list_lineno = 0;
  last_xref_lexstart = 0;
  last_xref_lineno = 0;
  list_title_set = FALSE;
  radix = 8;                    /* Initial radix is octal (base 8).           */

  if( !rim_mode )
  {
    /* Put out initial origin if not in rim mode.                             */
    punchOrigin( clc );
  }

  while( TRUE )
  {
    readLine();
    nextLexeme();

    scanning_line = TRUE;
    while( scanning_line )
    {
      if( isend( line[lexstart] ))
      {
        scanning_line = FALSE;
      }
      else
      {
        switch( line[lexstart] )
        {
        case '/':
          scanning_line = FALSE;
          break;

        case ';':
          nextLexeme();
          break;

        case '$':
          endOfBinary();
          return;

        case '*':
          nextLexeme();             /* Skip '*', (set origin symbol)          */
          newclc = ((getExpr())->val & 07777 ) | field;
          /* Do not change Current Location Counter if an error occurred.     */
          if( !error_in_line )
          {
            if(( newclc & 07600 ) != ( clc & 07600 ) ) 
            {
              /* Current page has changed.                                    */
              punchLiteralPool( cp, 0 );
            }
            clc = newclc - reloc;
            fieldlc = clc & 07777;

            if( !rim_mode )
            {
              /* Not rim mode, put out origin.                                */
              punchOrigin( clc );
            }
            printLine( line, 0, fieldlc, LINE_VAL );
          }
          break;

        default:
          switch( line[lexterm] )
          {
          case ',':
            if( isalpha( line[lexstart] ))
            {
              /* Use lookup so symbol will not be counted as reference.       */
              sym = lookup( lexemeToName( name, lexstart, lexterm ));
              if( M_DEFINED( sym->type ))
              {
                if( (sym->val & 07777) != ( ( clc+reloc ) & 07777) && pass == 2 )
                {
                  errorSymbol( &duplicate_label, sym->name, lexstart );
                }
                sym->type = sym->type | DUPLICATE;
              }
              /* Must call define on pass 2 to generate concordance.          */
              defineLexeme( lexstart, lexterm, ( clc + reloc ), LABEL );
            }
            else
            {
              errorLexeme( &label_syntax, lexstart );
            }
            nextLexeme();           /* skip label                             */
            nextLexeme();           /* skip comma                             */
            break;

          case '=':
            if( isalpha( line[lexstart] ))
            {
              start = lexstart;
              term = lexterm;
              delimiter = line[lexterm];
              nextLexBlank();       /* skip symbol                            */
              nextLexeme();         /* skip trailing =, allow blank           */
              delimiter = line[lexterm];
              val = getExprs();
              defineLexeme( start, term, val, DEFINED );
              printLine( line, 0, val, LINE_VAL );
            }
            else
            {
              errorLexeme( &symbol_syntax, lexstartprev );
              nextLexeme();         /* skip symbol                            */
              nextLexeme();         /* skip trailing =                        */
              getExprs();           /* skip expression                        */
            }
            break;

          default:
            if( isalpha( line[lexstart] ))
            {
              sym = evalSymbol();
              val = sym->val;
              if( M_PSEUDO( sym->type ))
              {
                nextLexeme();         /* Skip symbol                          */
                scanning_line = pseudoOperators( (PSEUDO_T)val & 07777 );
              }
              else
              {
                /* Identifier is not a pseudo-op, interpret as load value     */
                punchOutObject( clc, getExprs() & 07777 );
                incrementClc();
              }
            }
            else
            {
              /* Identifier is a value, interpret as load value               */
              punchOutObject( clc, getExprs() & 07777 );
              incrementClc();
            }
            break;
          } /* end switch                                                     */
          break;
        } /* end switch                                                       */
      } /* end if                                                             */
    } /* end while( scanning_line )                                           */
  } /* end while( TRUE )                                                      */
} /* onePass()                                                                */


/******************************************************************************/
/*                                                                            */
/*  Function:  fixMRIInstruction                                              */
/*                                                                            */
/*  Synopsis:  Now that we have the final value figure out if page 0, current */
/*             page, or indirect needed and max final instruction             */
/*                                                                            */
/******************************************************************************/
WORD16 fixMRIInstruction(WORD16 instruction, WORD16 value)
{
        /* Now have the address part of the MRI instruction.                  */
        if( value < 00200 )
        {
          instruction |= value;        /* Page zero MRI.                             */
        }
        else if( (( fieldlc + reloc ) & 07600 ) <= value
             && value <= ((( fieldlc + reloc ) & 07600) | 0177 ))
        {
          instruction |= ( PAGE_BIT | (value & ADDRESS_FIELD )); /* Current page MRI */
        }
        else
        {
          if(( instruction & INDIRECT_BIT ) == INDIRECT_BIT )
          {
            /* Already indirect, can't generate                               */
            errorSymbol( &illegal_indirect, NULL, lexstartprev );
          }
          else
          {
            if( literals_ok )
            {
              /* Now fix off page reference.                                  */
              /* Search current page literal pool for needed instruction.           */
              /* Set Indirect Current Page                                    */
              instruction |= ( 00600 | insertLiteral( cp, value, GET_PAGE_INDEX(clc) ));
              indirect_generated = TRUE;
              if (pass == 2)
              {
                links++;
              }
            }
            else
            {
              errorSymbol( &illegal_reference, NULL, lexstartprev );
              instruction |= ( value & 0177 );
            }
          }
        }
   return instruction;
}
/******************************************************************************/
/*                                                                            */
/*  Function:  getExprs                                                       */
/*                                                                            */
/*  Synopsis:  Or together a list of blank separated expressions, from the    */
/*             current lexeme onward.  Leave the current lexeme as            */
/*             the last one in the list.                                      */
/*                                                                            */
/******************************************************************************/
WORD16 getExprs()
{
  SYM_T  *symv;
  SYM_T  *symt;
  WORD16  temp;
  SYMTYP  temp_type;
  WORD16  value;
  SYMTYP  value_type;
  BOOL    MRI_held = FALSE;
  WORD16  held_value = 0;

  symv = getExpr();
  value = symv->val;
  value_type = symv->type;

  while( TRUE )
  {
    if( isdone( line[lexstart] ))
    {
      if (MRI_held)
      {
        value = fixMRIInstruction(value, held_value);
      }
      return( value );
    }
    switch( line[lexstart] )
    {
    case ')':
    case ']':
    case '<':
      if (MRI_held)
      {
        value = fixMRIInstruction(value, held_value);
      }
      return( value );

    default:
      break;
    }

    /* Interpret space as logical or                                          */
    symt = getExpr();
    temp = symt->val & 07777;
    temp_type = symt->type;

    switch( value_type & (MRI | MRIFIX))
    {
    case MRI:
    case MRIFIX:
      /* Previous symbol was a Memory Reference Instruction.                  */
      switch( temp_type & (MRI | MRIFIX) )
      {
      case MRI:
      case MRIFIX:
        /* If we have held value don't or in more MRI's to instuction, they */
        /* are now instuction value */
        if (MRI_held)
        {
          held_value |= temp;
        }
        else
        { 
          /* Current symbol is also a Memory Reference Instruction.           */
          value |= temp;          /* Just OR the MRI instructions.            */
        }
        break;

      default:
        held_value |= temp;
        MRI_held = TRUE;
        break;
      }
      break;

    default:
        if (value_type == UNDEFINED || temp_type == UNDEFINED) {
            value = 0;
        } else {
            value |= temp;          /* Normal 12 bit value.                       */
         }
        break;
    }
  } /* end while                                                              */
} /* getExprs()                                                               */


/******************************************************************************/
/*                                                                            */
/*  Function:  getExpr                                                        */
/*                                                                            */
/*  Synopsis:  Get an expression, from the current lexeme onward, leave the   */
/*             current lexeme as the one after the expression.  Expressions   */
/*             contain terminal symbols (identifiers) separated by operators. */
/*                                                                            */
/******************************************************************************/
SYM_T *getExpr()
{
  SYM_T *sym;
  delimiter = line[lexterm];


  if( line[lexstart] == '-' )
  {
    nextLexBlank();
    sym_getexpr = *(eval());
    sym_getexpr.val = ( - sym_getexpr.val ) & 07777;
  }
  else
  {
    if( line[lexstart] == '+' )
    {
      nextLexBlank();
    }
    sym_getexpr = *(eval());
    sym_getexpr.val = sym_getexpr.val & 07777;
  }

  if( is_blank( delimiter ))
  {
    return( &sym_getexpr );
  }

  /* Here we assume the current lexeme is the operator separating the         */
  /* previous operator from the next, if any.                                 */
  while( TRUE )
  {
    /* assert line[lexstart] == delimiter                                     */
    if( is_blank( delimiter ))
    {
      return( &sym_getexpr );
    }

    switch( line[lexstart] )
    {
    case '+':                   /* add                                        */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val += sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '-':                   /* subtract                                   */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val -= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '^':                   /* multiply                                   */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val *= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '%':                   /* divide                                     */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val /= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '&':                   /* and                                        */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val &= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    case '!':                   /* or                                         */
      nextLexBlank();           /* skip over the operator                     */
      sym = eval();
      sym_getexpr.val |= sym->val;
      if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) {
         sym_getexpr.val = 0;
         sym_getexpr.type = UNDEFINED;
      }
      break;

    default:
      if( isend( line[lexstart] ))
      {
        return( &sym_getexpr );
      }

      switch( line[lexstart] )
      {
      case '/':
      case ';':
      case ')':
      case ']':
      case '<':
        break;

      case '=':
        errorMessage( &illegal_equals, lexstart );
        moveToEndOfLine();
        sym_getexpr.val = 0;
        break;

      default:
        errorMessage( &illegal_expression, lexstart );
        moveToEndOfLine();
        sym_getexpr.val = 0;
        break;
      }
      return( &sym_getexpr );
    }
  } /* end while                                                              */
} /* getExpr()                                                                */


/******************************************************************************/
/*                                                                            */
/*  Function:  eval                                                           */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme, set delimiter and advance.*/
/*                                                                            */
/******************************************************************************/
SYM_T *eval()
{
  WORD16  digit;
  int     from;
  WORD16  loc;
  SYM_T  *sym;
  WORD16  val;

  val = 0;

  delimiter = line[lexterm];
  if( isalpha( line[lexstart] ))
  {
    sym = evalSymbol();
    if( M_UNDEFINED( sym->type ) && pass == 2 )
    {
      errorSymbol( &undefined_symbol, sym->name, lexstart );
      nextLexeme();
      return( sym );
    }
    else
    {
      nextLexeme();
      return( sym );
    }
  }
  else if( isdigit( line[lexstart] ))
  {
    from = lexstart;
    val = 0;
    while( from < lexterm )
    {
      if( isdigit( line[from] ))
      {
        digit = (WORD16) line[from++] - (WORD16) '0';
        if( digit < radix )
        {
          val = val * radix + digit;
        }
        else
        {
          errorLexeme( &number_not_radix, from - 1 );
          val = 0;
          from = lexterm;
        }
      }
      else
      {
        errorLexeme( &not_a_number, lexstart );
        val = 0;
        from = lexterm;
      }
    }
    nextLexeme();
    sym_eval.val = val;
    return( &sym_eval );
  }
  else
  {
    switch( line[lexstart] )
    {
    case '"':                   /* Character literal                          */
      if( cc + 1 <= maxcc )
      {
        val = line[lexstart + 1] | 0200;
        delimiter = line[lexstart + 2];
        cc = lexstart + 2;
      }
      else
      {
        errorMessage( &no_literal_value, lexstart );
      }
      nextLexeme();
      break;

    case '.':                   /* Value of Current Location Counter          */
      val = (clc & 07777) + reloc;
      nextLexeme();
      break;

    case '[':                   /* Generate literal on page zero.             */
      if( !literals_ok && LITERAL_ERROR)
      {
        errorMessage( &literal_gen_off, lexstart );
      }
      nextLexBlank();           /* Skip bracket                               */
      val = getExprs() & 07777;

      if( line[lexstart] == ']' )
      {
        nextLexBlank();         /* Skip end bracket                           */
      }

      sym_eval.val = (literals_ok || !LITERAL_ERROR) ? insertLiteral( cp , val,
        GET_PAGE_INDEX(field)) : 0;
      return( &sym_eval );

    case '(':                   /* Generate literal on current page.          */
      if( !literals_ok && LITERAL_ERROR)
      {
        errorMessage( &literal_gen_off, lexstart );
      }

      nextLexBlank();           /* Skip paren                                 */
      val = getExprs() & 07777;

      if( line[lexstart] == ')' )
      {
        nextLexBlank();         /* Skip end paren                             */
      }

      loc = (literals_ok || !LITERAL_ERROR) ? insertLiteral( cp, val, GET_PAGE_INDEX(clc) ) : 0;
      sym_eval.val = loc + (( clc + reloc ) & 077600 );
      return( &sym_eval );

    default:
      switch( line[lexstart] )
      {
      case '=':
        errorMessage( &illegal_equals, lexstart );
        moveToEndOfLine();
        break;

      default:
        errorMessage( &illegal_character, lexstart );
        break;
      }
      val = 0;                  /* On error, set value to zero.               */
      nextLexBlank();           /* Go past illegal character.                 */
    }
  }
  sym_eval.val = val;
  return( &sym_eval );
} /* eval()                                                                   */


/******************************************************************************/
/*                                                                            */
/*  Function:  inputDubl                                                      */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a double word.          */
/*                                                                            */
/******************************************************************************/
void inputDubl()
{
  WORD32 dublvalue;
  BOOL   scanning_line;

  scanning_line = TRUE;
  do
  {
    while( scanning_line )
    {
      if( isend( line[lexstart] ))
      {
        scanning_line = FALSE;
      }
      else
      {
        switch( line[lexstart] )
        {
        case '/':
          scanning_line = FALSE;
          break;

        case ';':
          nextLexeme();
          break;

        case '+':
          delimiter = line[lexterm];
          nextLexBlank();
        case '-':
        default:
          if( isdigit( line[lexstart] ) || line[lexstart] == '-' )
          {
            dublvalue = getDublExprs();
            punchOutObject( clc, (WORD16)(( dublvalue >> 12 ) & 07777 ));
            incrementClc();
            punchOutObject( clc, (WORD16)( dublvalue & 07777 ));
            incrementClc();
          }
          else
          {
            return;             /* Non-numeric input, back to assembly.       */
          }
          break;
        } /* end switch                                                       */
      } /* end if                                                             */

      if( error_in_line )
      {
        return;                 /* Error occurred, exit DUBL input mode.      */
      }
    } /* end while( scanning_line )                                           */
    readLine();
    nextLexeme();
    scanning_line = TRUE;
  }
  while( TRUE );
} /* inputDubl()                                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  getDublExprs                                                   */
/*                                                                            */
/*  Synopsis:  Get a DUBL expression.                                         */
/*                                                                            */
/******************************************************************************/
WORD32 getDublExprs()
{
  WORD32 dublvalue;

  dublvalue = getDublExpr();

  while( TRUE )
  {
    if( isdone( line[lexstart] ))
    {
      return( dublvalue );
    }
    else
    {
      errorMessage( &illegal_expression, lexstart - 1 );
      return( 0 );
    }
  } /* end while                                                              */
} /* getDublExprs()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  getDublExpr                                                    */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a double word.  The     */
/*             number is always considered to have a decimal radix.           */
/*                                                                            */
/******************************************************************************/
WORD32 getDublExpr()
{
  WORD32 dublvalue;

  delimiter = line[lexterm];
  if( line[lexstart] == '-' )
  {
    nextLexBlank();
    dublvalue = evalDubl( 0 );
    nextLexeme();
    /* Test for any value greater than 23 bits in length.                     */
    if( (unsigned long int)dublvalue > 040000000L )
    {
      errorMessage( &dubl_overflow, lexstart );
      dublvalue = 0;
    }
    dublvalue = -dublvalue;
  }
  else
  {
    dublvalue = evalDubl( 0 );
    nextLexeme();
    /* Test for any value greater than 23 bits in length.                     */
    if( (unsigned long int)dublvalue > 037777777L )
    {
      errorMessage( &dubl_overflow, lexstart );
      dublvalue = 0;
    }
  }

  if( is_blank( delimiter ))
  {
    return( dublvalue );
  }

  /* Here we assume the current lexeme is the operator separating the         */
  /* previous operator from the next, if any.                                 */
  while( TRUE )
  {
    /* assert line[lexstart] == delimiter                                     */
    if( is_blank( delimiter ))
    {
      errorMessage( &illegal_expression, lexstart );
      moveToEndOfLine();
      dublvalue = 0;
      return( dublvalue );
    }

    switch( line[lexstart] )
    {
    case '+':                   /* add                                        */
    case '-':                   /* subtract                                   */
    case '^':                   /* multiply                                   */
    case '%':                   /* divide                                     */
    case '&':                   /* and                                        */
    case '!':                   /* or                                         */
      errorMessage( &illegal_expression, lexstart );
      moveToEndOfLine();
      dublvalue = 0;
      break;

    default:
      if( isend( line[lexstart] ))
      {
        return( dublvalue );
      }

      switch( line[lexstart] )
      {
      case '/':
      case ';':
        break;

      default:
        errorMessage( &illegal_expression, lexstart );
        moveToEndOfLine();
        dublvalue = 0;
        break;
      }
      return( dublvalue );
    }
  } /* end while                                                              */
} /* getDublExpr()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  evalDubl                                                       */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a double word.  The     */
/*             number is always considered to have a decimal radix.           */
/*                                                                            */
/******************************************************************************/
WORD32 evalDubl( WORD32 initial_value )
{
  WORD32  digit;
  int     from;
  WORD32  dublvalue;
  WORD32  olddublvalue;

  overflow = FALSE;
  delimiter = line[lexterm];
  from = lexstart;
  dublvalue = initial_value;

  while( from < lexterm )
  {
    if( isdigit( line[from] ))
    {
      olddublvalue = dublvalue;
      digit = (WORD32)( line[from++] - '0' );
      dublvalue = dublvalue * 10 + digit;
      if( dublvalue < olddublvalue )
      {
        overflow = TRUE;
      }
    }
    else
    {
      errorLexeme( &not_a_number, from );
      dublvalue = 0;
      from = lexterm;
    }
  }
  return( dublvalue );
} /* evalDubl()                                                               */


/******************************************************************************/
/*                                                                            */
/*  Function:  inputFltg                                                      */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a Floating Point const. */
/*                                                                            */
/******************************************************************************/
void inputFltg()
{
  FLTG_T *fltg;
  BOOL    scanning_line;

  fltg_input = TRUE;            /* Set lexeme scanner for floating point.     */
  scanning_line = TRUE;
  while( TRUE )
  {
    while( scanning_line )
    {
      if( isend( line[lexstart] ))
      {
        scanning_line = FALSE;
      }
      else
      {
        switch( line[lexstart] )
        {
        case '/':
          scanning_line = FALSE;
          break;

        case ';':
          nextLexeme();
          break;

        case '+':
          delimiter = line[lexterm];
          nextLexBlank();
        case '-':
        default:
          if( isdigit( line[lexstart] ) || line[lexstart] == '-' )
          {
            fltg = getFltgExprs();
            punchOutObject( clc, ( fltg->exponent & 07777 ));
            incrementClc();
            punchOutObject( clc, (WORD16)(( fltg->mantissa >> 12 ) & 07777 ));
            incrementClc();
            punchOutObject( clc, (WORD16)( fltg->mantissa & 07777 ));
            incrementClc();
          }
          else
          {
            fltg_input = FALSE; /* Reset lexeme scanner.                      */
            return;             /* Non-numeric input, back to assembly.       */
          }
          break;
        } /* end switch                                                       */
      } /* end if                                                             */

      if( error_in_line )
      {
        fltg_input = FALSE;     /* Reset lexeme scanner.                      */
        return;                 /* Error occurred, exit FLTG input mode.      */
      }
    } /* end while( scanning_line )                                           */
    readLine();
    nextLexeme();
    scanning_line = TRUE;
  }
} /* inputFltg()                                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  getFltgExprs                                                   */
/*                                                                            */
/*  Synopsis:  Get a FLTG expression.                                         */
/*                                                                            */
/******************************************************************************/
FLTG_T *getFltgExprs()
{
  FLTG_T *fltg;

  fltg = getFltgExpr();

  while( TRUE )
  {
    if( isdone( line[lexstart] ))
    {
      return( fltg );
    }
    else
    {
      errorMessage( &illegal_expression, lexstart - 1 );
      return( 0 );
    }
  } /* end while                                                              */
} /* getFltgExprs()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  getFltgExpr                                                    */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a double word.  The     */
/*             number is always considered to have a decimal radix.           */
/*                                                                            */
/******************************************************************************/
FLTG_T *getFltgExpr()
{
  FLTG_T *fltg;

  delimiter = line[lexterm];
  fltg = evalFltg();
  /* Test for any value greater than 23 bits in length.                     */
  if( (unsigned long int)fltg->mantissa> 077777777L )
  {
    errorMessage( &fltg_overflow, lexstart );
  }

  if( is_blank( delimiter ))
  {
    return( fltg );
  }

  /* Here we assume the current lexeme is the operator separating the         */
  /* previous operator from the next, if any.                                 */
  while( TRUE )
  {
    /* assert line[lexstart] == delimiter                                     */
    if( is_blank( delimiter ))
    {
      errorMessage( &illegal_expression, lexstart );
      moveToEndOfLine();
      fltg = 0;
      return( fltg );
    }

    switch( line[lexstart] )
    {
    case '+':                   /* add                                        */
    case '-':                   /* subtract                                   */
    case '^':                   /* multiply                                   */
    case '%':                   /* divide                                     */
    case '&':                   /* and                                        */
    case '!':                   /* or                                         */
      errorMessage( &illegal_expression, lexstart );
      moveToEndOfLine();
      fltg = NULL;
      break;

    default:
      if( isend( line[lexstart] ))
      {
        return( fltg );
      }

      switch( line[lexstart] )
      {
      case '/':
      case ';':
        break;

      default:
        errorMessage( &illegal_expression, lexstart );
        moveToEndOfLine();
        fltg = NULL;
        break;
      }
      return( fltg );
    }
  } /* end while                                                              */
} /* getFltgExpr()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  evalFltg                                                       */
/*                                                                            */
/*  Synopsis:  Get the value of the current lexeme as a floating point value. */
/*             Floating point input is alwasy considered decimal.             */
/*             The general format of a floating point number is:              */
/*                +-ddd.dddE+-dd  where each d is a decimal digit.            */
/*                                                                            */
/******************************************************************************/
FLTG_T *evalFltg()
{
  int      current_state;
  int      current_col;
  WORD16   exponent;
  FLTG_T  *fltg;
  WORD32   input_value;
  BOOL     negate;
  BOOL     negate_exponent;
  int      next_state;
  int      right_digits;

  /* This uses a lexical analyzer to parse the floating point format.         */
  static BYTE state_table[][10] =
  {
    /*  0   1   2   3   4   5   6  Oolumn index                               */
    /*  +   -   d   .   E  sp   p      State  Comment                         */
     {  2,  1,  3,  4, 10, 10, 10  },  /*  0  Initial state.                  */
     { 11, 11,  3,  4, 11, 11, 11  },  /*  1  -                               */
     { 11, 11,  3,  4, 11, 11, 11  },  /*  2  +                               */
     { 10, 10, 10,  4,  6, 10, 10  },  /*  3  # (+-ddd)                       */
     { 11, 11,  5, 11, 11, 10, 10  },  /*  4  . (+-ddd.)                      */
     { 11, 11, 11, 11,  6, 10, 11  },  /*  5  # (+-ddd.ddd)                   */
     {  8,  7,  9, 11, 11, 11, 11  },  /*  6  E (+-ddd.dddE)                  */
     { 11, 11,  9, 11, 11, 11, 11  },  /*  7  - (+-ddd.dddE-                  */
     { 11, 11,  9, 11, 11, 11, 11  },  /*  8  + (+-ddd.dddE+                  */
     { 11, 11, 11, 11, 11, 10, 11  }   /*  9  # (+-ddd.dddE+-dd               */
                                       /* 10  Completion state                */
                                       /* 11  Error state.                    */
  };

  delimiter = line[lexterm];
  fltg = &fltg_ac;
  fltg->exponent = 0;
  fltg->mantissa = 0;
  input_value = 0;
  negate = FALSE;
  negate_exponent = FALSE;
  next_state = 0;
  exponent = 0;
  right_digits = 0;
  current_state = 0;

  while( TRUE )
  {
    /* Classify character.  This is the column index.                         */
    switch( line[lexstart] )
    {
    case '+':
      current_col = 0;
      break;

    case '-':
      current_col = 1;
      break;

    case '.':
      current_col = 3;
      break;

    case 'E':  case 'e':
      current_col = 4;
      break;

    default:
      if( isdigit( line[lexstart] ))
      {
        current_col = 2;
      }
      else if( isdone( line[lexstart] ))
      {
        current_col = 5;
      }
      else
      {
        current_col = 6;
      }
      break;
    }

    next_state = state_table[current_state][current_col];

    switch( next_state )
    {
    case 1:                             /*  -                                 */
      negate = TRUE;
    case 2:                             /*  +                                 */
      delimiter = line[lexterm];        /* Move past the + or - character.    */
      nextLexBlank();
      break;

    case 3:                             /* Number  (+-ddd)                    */
      input_value = evalDubl( 0 );      /* Integer part of the number.        */
      nextLexeme();                     /* Move past previous lexeme.         */
      break;

    case 4:
      delimiter = line[lexterm];
      nextLexBlank();                   /* Move past the . character.         */
      break;

    case 5:                             /* .  (+-ddd.ddd)                     */
                                        /* Fractional part of the number.     */
      input_value = evalDubl( input_value );
      right_digits = lexterm - lexstart;/* Digit count to right of decimal.   */
      nextLexeme();                     /* Move past previous lexeme.         */
      break;

    case 6:                             /* E  (+-ddd.dddE)                    */
      delimiter = line[lexterm];        /* Move past the E.                   */
      nextLexBlank();
      break;

    case 7:                             /* -  (+-ddd.dddE-)                   */
      negate_exponent = TRUE;
    case 8:                             /* +  (+-ddd.dddE+)                   */
      delimiter = line[lexterm];        /* Move past the + or - character.    */
      nextLexBlank();
      break;

    case 9:                             /* #  (+-ddd.dddE+-dd)                */
      exponent = (int)evalDubl( 0 );    /* Exponent of floating point number. */
      if( negate_exponent ) { exponent = - exponent; }
      nextLexeme();                     /* Move past previous lexeme.         */
      break;

    case 10:                            /* Floating number parsed, convert    */
                                        /* the number.                        */
      /* Get the exponent for the number as input.                            */
      exponent -= right_digits;

      /* Remove trailing zeros and adjust the exponent accordingly.           */
      while(( input_value % 10 ) == 0 )
      {
        input_value /= 10;
        exponent++;
      }

      /* Convert the number to floating point.  The number is calculated with */
      /* a 27 bit mantissa to improve precision.  The extra 3 bits are        */
      /* discarded after the result has been calculated.                      */
      fltg->exponent = 26;
      fltg->mantissa = input_value << 3;
      normalizeFltg( fltg );


      while( exponent != 0 )
      {
        if( exponent < 0 )
        {
          /* Decimal point is to the left. */
          fltg->mantissa /= 10;
          normalizeFltg( fltg );
          exponent++;
        }
        else if( exponent > 0 )
        {
          /* Decimal point is to the right. */
          fltg->mantissa *= 10;
          normalizeFltg( fltg );
          exponent--;
        }
      }

      /* Discard the extra precsion used for calculating the number.          */
      fltg->mantissa >>= 3;
      fltg->exponent -= 3;
      if( negate )
      {
        fltg->mantissa = (- fltg->mantissa ) & 077777777L;
      }
      return( fltg );

    case 11:                            /* Error in format.                   */
      /* Not a properly constructued floating point number.                   */
      return( fltg );
    default:
      break;
    }
    /* Set state for next pass through the loop.                              */
    current_state = next_state;
  }
} /* evalFltg()                                                               */



/******************************************************************************/
/*                                                                            */
/*  Function:  normalizeFltg                                                  */
/*                                                                            */
/*  Synopsis:  Normalize a PDP-8 double precision floating point number.      */
/*                                                                            */
/******************************************************************************/
void normalizeFltg( FLTG_T *fltg )
{
  /* Normalize the floating point number.                                     */
  if( fltg->mantissa != 0 )
  {
    if(( fltg->mantissa & ~0x3FFFFFFL ) == 0 )
    {
      while(( fltg->mantissa & ~0x1FFFFFFL ) == 0 )

      {
        fltg->mantissa <<= 1;
        fltg->exponent--;
      }
    }
    else
    {
      while(( fltg->mantissa & ~0x3FFFFFFL ) != 0 )
      {
        fltg->mantissa >>= 1;
        fltg->exponent++;
      }
    }
  }
  else
  {
    fltg->exponent = 0;
  }
  return;
}


/******************************************************************************/
/*                                                                            */
/*  Function:  incrementClc                                                   */
/*                                                                            */
/*  Synopsis:  Set the next assembly location.  Test for collision with       */
/*             the literal tables.                                            */
/*                                                                            */
/******************************************************************************/
WORD16 incrementClc()
{
  testForLiteralCollision( clc );

  /* Incrementing the location counter is not to change field setting.        */
  clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 );
  fieldlc = clc & 07777;
  return( clc );
} /* incrementClc()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  testForLiteralCollision                                        */
/*                                                                            */
/*  Synopsis:  Test the given location for collision with the literal tables. */
/*                                                                            */
/******************************************************************************/
BOOL testForLiteralCollision( WORD16 loc )
{
  WORD16  pagelc;
  BOOL    result = FALSE;
  WORD16  tmppage;
  int     tmpfield;

  tmpfield = GET_PAGE_INDEX(loc);
  tmppage = loc & 07600;
  pagelc  = loc & 00177;

  if ( pagelc > max_page_used[tmpfield] ) 
  {
     max_page_used[tmpfield] = pagelc;
  }
  if ( pagelc >= cp[tmpfield].loc )
  {
    if ( tmppage == 0 )
    {
      errorMessage( &pz_literal_overflow, -1 );
    }
    else
    {
      errorMessage( &literal_overflow, -1 );
    }
    result = TRUE;
  }

  return( result );
} /* testForLiteralCollision()                                                */


/******************************************************************************/
/*                                                                            */
/*  Function:  readLine                                                       */
/*                                                                            */
/*  Synopsis:  Get next line of input.  Print previous line if needed.        */
/*                                                                            */
/******************************************************************************/
void readLine()
{
  WORD16  ix;
  WORD16  iy;
  char   inpline[LINELEN];

  listLine();                   /* List previous line if needed.              */
  lineno++;                     /* Count lines read.                          */
  indirect_generated = FALSE;   /* Mark no indirect address generated.        */
  listed = FALSE;               /* Mark as not listed.                        */
  cc = 0;                       /* Initialize column counter.                 */
  lexstartprev = 0;

  error_in_line = FALSE;
  if(( fgets( inpline, LINELEN - 1, infile )) == NULL )
  {
    inpline[0] = '$';
    inpline[1] = '\n';
    inpline[2] = '\0';
    if (!dollar_not_required) {
       error_in_line = TRUE;
    }
  }

  /* Remove any tabs from the input line by inserting the required number     */
  /* of spaces to simulate N character tab stops, where N defaults to 8 and   */
  /* is set by the command line option -t. (RK 20071029)                      */
  /* Ignore \r if there is one.  (DPI 20150501)                               */
  for( ix = 0, iy = 0; inpline[ix] != '\0' && iy < (LINELEN - 2); ix++ )
  {
    switch( inpline[ix] )
    {
    case '\t':
      do
      {
        line[iy] = ' ';
        iy++;
      }
      while(( iy % tabstops ) != 0 && iy < (LINELEN - 2));
      break;

    case '\r':        /* dont copy the carriage return */
       break;

    default:
      line[iy] = inpline[ix];
      iy++;
      break;
    }
  }
  if (iy >= (LINELEN - 2)) {
  	line [iy] = '\n';
  	iy++;
  } 
  line[iy] = '\0';

  maxcc = iy;                   /* Save the current line length.              */
  /* Save the first line for possible use as the listing title.               */
  if( lineno == 1 )
  {
    strcpy( list_title, line );
  }
} /* readLine()                                                               */


/******************************************************************************/
/*                                                                            */
/*  Function:  listLine                                                       */
/*                                                                            */
/*  Synopsis:  Output a line to the listing file.                             */
/*                                                                            */
/******************************************************************************/
void listLine()
/* generate a line of listing if not already done!                            */
{
  if( listfile != NULL && listed == FALSE )
  {
    printLine( line, 0, 0, LINE );
  }
} /* listLine()                                                               */


/******************************************************************************/
/*                                                                            */
/*  Function:  printPageBreak                                                 */
/*                                                                            */
/*  Synopsis:  Output a Top of Form and listing header if new page necessary. */
/*                                                                            */
/******************************************************************************/
void printPageBreak()
{
  if( page_lineno >= LIST_LINES_PER_PAGE )
         /*  ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */
  {
    if( !list_title_set )
    {
      /* strcpy( list_title, line ); */
      if( list_title[strlen(list_title) - 1] == '\n' )
      {
        list_title[strlen(list_title) - 1] = '\0';
      }
      if( strlen( list_title ) > TITLELEN )
      {
        list_title[TITLELEN] = '\0';
      }
      list_title_set = TRUE;
    }
    topOfForm( list_title, NULL );

  }
} /* printPageBreak()                                                         */


/******************************************************************************/
/*                                                                            */
/*  Function:  printLine                                                      */
/*                                                                            */
/*  Synopsis:  Output a line to the listing file with new page if necessary.  */
/*                                                                            */
/******************************************************************************/
void printLine( char *line, WORD16 loc, WORD16 val, LINESTYLE_T linestyle )
{
  char rlc;

  if( listfile == NULL )
  {
    save_error_count = 0;
    return;
  }

  printPageBreak();

  list_lineno++;
  page_lineno++;

  if (reloc == 0)
  {
    rlc = ' ';
  }
  else
  {
    rlc = '*';
  }

  switch( linestyle )
  {
  default:
  case LINE:
    fprintf(listfile, "%5d              ", lineno );
    fputs( line, listfile );
    listed = TRUE;
    break;

  case LINE_VAL:
    fprintf(listfile, "%5d        %4.4o  ", lineno, val );
    fputs( line, listfile );
    listed = TRUE;
    break;

  case LINE_LOC_VAL:
    if( !listed )
    {
      if( indirect_generated )
      {
        fprintf( listfile, "%5d %5.5o%c %4.4o@ ", lineno, loc, rlc, val );
      }
      else
      {
        fprintf( listfile, "%5d %5.5o%c %4.4o  ", lineno, loc, rlc, val );
      }
      fputs( line, listfile );
      listed = TRUE;
    }
    else
    {
      fprintf( listfile, "      %5.5o%c %4.4o\n", loc, rlc, val );
    }
    break;

  case LOC_VAL:
    fprintf( listfile, "      %5.5o%c %4.4o\n", loc, rlc, val );
    break;
  }
  printErrorMessages();
} /* printLine()                                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  printErrorMessages                                             */
/*                                                                            */
/*  Synopsis:  Output any error messages from the current list of errors.     */
/*                                                                            */
/******************************************************************************/
void printErrorMessages()
{
  WORD16  ix;
  WORD16  iy;

  if( listfile != NULL )
  {
    /* If any errors, display them now.                                       */
    for( iy = 0; iy < save_error_count; iy++ )
    {
      printPageBreak();
      fprintf( listfile, "%-18.18s", error_list[iy].mesg );
      if( error_list[iy].col >= 0 )
      {
        for( ix = 0; ix < error_list[iy].col; ix++ )
        {
          if( line[ix] == '\t' )
          {
            putc( '\t', listfile );
          }
          else
          {
            putc( ' ', listfile );
          }
        }
        fputs( "^", listfile );
        list_lineno++;
        page_lineno++;
      }
      fputs( "\n", listfile );
    }
  }
  save_error_count = 0;
} /* printErrorMessages()                                                     */


/******************************************************************************/
/*                                                                            */
/*  Function:  endOfBinary                                                    */
/*                                                                            */
/*  Synopsis:  Outputs both literal tables at the end of a binary segment.    */
/*                                                                            */
/******************************************************************************/
void endOfBinary()
{
  /* Punch page 0 also.                                   */
  punchLiteralPool( cp, 1 );
  if( error_in_line )
  {
    listed = TRUE;
    clc = ( clc & 070000 ) + (( clc - 1 ) & 07777 );
    errorMessage( &end_of_file, -1 );
    clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 );
  }
  else
  {
    listLine();             /* List line if not done yet.                     */
  }
  return;
} /* endOfBinary()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchChecksum                                                  */
/*                                                                            */
/*  Synopsis:  Output a checksum if the current mode requires it and an       */
/*             object file exists.                                            */
/*                                                                            */
/******************************************************************************/
void punchChecksum()
{
  /* If the assembler has output any BIN data output the checksum.            */
  if( binary_data_output && !rim_mode )
  {
    punchLocObject( 0, checksum );
  }
  binary_data_output = FALSE;
  checksum = 0;
} /* punchChecksum()                                                          */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchLeader                                                    */
/*                                                                            */
/*  Synopsis:  Generate 2 feet of leader on object file, as per DEC           */
/*             documentation.  Paper tape has 10 punches per inch.            */
/*                                                                            */
/******************************************************************************/
void punchLeader( int count )
{
  int ix;

  /* If value is zero, set to the default of 2 feet of leader.                */
  count = ( count == 0 ) ? 240 : count;

  if( objectfile != NULL )
  {
    for( ix = 0; ix < count; ix++ )
    {
      fputc( 0200, objectfile );
    }
  }
} /* punchLeader()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchOrigin                                                    */
/*                                                                            */
/*  Synopsis:  Output an origin to the object file.                           */
/*                                                                            */
/******************************************************************************/
void punchOrigin( WORD16 loc )
{
  punchObject((( loc >> 6 ) & 0077 ) | 0100 );
  punchObject( loc & 0077 );
} /* punchOrigin()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchObject                                                    */
/*                                                                            */
/*  Synopsis:  Put one character to object file and include it in checksum.   */
/*                                                                            */
/******************************************************************************/
void punchObject( WORD16 val )
{
  val &= 0377;
  if( objectfile != NULL )
  {
    fputc( val, objectfile );
    checksum += val;
  }
  binary_data_output = TRUE;
} /* punchObject()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchOutObject                                                 */
/*                                                                            */
/*  Synopsis:  Output the current line and then then punch value to the       */
/*             object file.                                                   */
/*                                                                            */
/******************************************************************************/
void punchOutObject( WORD16 loc, WORD16 val )
{
  /* Adding reloc makes printout agree with PAL8 where is prints the */
  /* relocated address, not the address in the BIN file */
  printLine( line,( ( field | loc ) + reloc ), val, LINE_LOC_VAL );
  punchLocObject( loc, val );
} /* punchOutObject()                                                         */

/******************************************************************************/
/*                                                                            */
/*  Function:  punchLocObject                                                 */
/*                                                                            */
/*  Synopsis:  Output the word (with origin if rim format) to the object file.*/
/*                                                                            */
/******************************************************************************/
void punchLocObject( WORD16 loc, WORD16 val )
{
  if( rim_mode )
  {
    punchOrigin( loc );
  }
  punchObject(( val >> 6 ) & 0077 );
  punchObject( val & 0077 );
} /* punchLocObject()                                                         */


/******************************************************************************/
/*                                                                            */
/*  Function:  punchLiteralPool                                               */
/*                                                                            */
/*  Synopsis:  Output the current page data.                                  */
/*                                                                            */
/******************************************************************************/
void punchLiteralPool( LPOOL_T *p, BOOL punch_page0 )
{
  WORD16  loc;
  WORD16  tmplc;
  int lpool_page = 0; /* Silence false uninitialized error from GCC */
  int i;

  for (i = MAX_PAGES-1; i >= 0;  i--) {
    lpool_page = (i << 7) & 07600;

    if ( p[i].loc != p[i].last_punched && (punch_page0 || lpool_page != 0) )
    {
      if( !rim_mode )
      {
        /* Put out origin if not in rim mode.                                 */
        punchOrigin( p[i].loc | lpool_page );
      }
      /* Put the literals in the object file.                                 */
      for( loc = p[i].loc; loc < p[i].last_punched; loc++ )
      {
        tmplc = loc + lpool_page;
        printLine( line, (field | tmplc), p[i].pool[loc], LOC_VAL );
        punchLocObject( tmplc, p[i].pool[loc] );
      }
      p[i].last_punched = p[i].loc;
    }
  }
} /* punchLiteralPool()                                                       */


/******************************************************************************/
/*                                                                            */
/*  Function:  insertLiteral                                                  */
/*                                                                            */
/*  Synopsis:  Add a value to the given literal pool if not already in pool.  */
/*             Return the location of the value in the pool.                  */
/*                                                                            */
/******************************************************************************/
WORD16 insertLiteral( LPOOL_T *pool, WORD16 value, int fieldpage_index )
{
  WORD16  ix;
  LPOOL_T *p;

  p = &pool[fieldpage_index];

  /* Search the literal pool for any occurence of the needed value.           */
  ix = PAGE_SIZE - 1;
  while( ix >= p->loc && p->pool[ix] != value )
  {
    ix--;
  }

  /* Check if value found in literal pool. If not, then insert value.         */
  if( ix < p->loc )
  {
    (p->loc)--;
    p->pool[p->loc] = value;
    ix = p->loc;
    if( max_page_used[fieldpage_index] >= p->loc ) {
      if ( (fieldpage_index & 017) == 0 )
      {
        errorMessage( &pz_literal_overflow, -1 );
      }
      else
      {
        errorMessage( &literal_overflow, -1 );
      }
    }
  }
  return( ix );
} /* insertLiteral()                                                          */


/******************************************************************************/
/*                                                                            */
/*  Function:  printSymbolTable                                               */
/*                                                                            */
/*  Synopsis:  Output the symbol table.                                       */
/*                                                                            */
/******************************************************************************/
void printSymbolTable()
{
  int    col;
  int    cx;
  char  *fmt;
  int    ix;
  char   mark;
  int    page;
  int    row;
  int    symbol_base;
  int    symbol_lines;

  symbol_base = number_of_fixed_symbols;

  for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ )
  {
    topOfForm( list_title, s_symtable );
    symbol_lines = LIST_LINES_PER_PAGE - page_lineno;

    for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++)
    {
      list_lineno++;
      page_lineno++;
      fprintf( listfile, "%5d", list_lineno );

      for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ )
      {
        /* Get index of symbol for the current line and column                         */
        cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row;
        cx += symbol_base;

        /* Make sure that there is a symbol to be printed.                    */
        if( number_of_fixed_symbols <= cx && cx < symbol_top )
        {
          switch( symtab[cx].type & LABEL )
          {
          case LABEL:
            fmt = " %c%-6.6s %5.5o ";
            break;

          default:
            fmt = " %c%-6.6s  %4.4o ";
            break;
          }

          switch( symtab[cx].type & ( DEFINED | REDEFINED ))
          {
          case UNDEFINED:
            mark = '?';
            break;

          case REDEFINED:
            mark = '#';
            break;

          default:
            mark = ' ';
            break;
          }
          fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val );
          ix++;
        }
      }
      fprintf( listfile, "\n" );
    }
  }
} /* printSymbolTable()                                                       */


/******************************************************************************/
/*                                                                            */
/*  Function:  printPermanentSymbolTable                                      */
/*                                                                            */
/*  Synopsis:  Output the permanent symbol table to a file suitable for       */
/*             being input after the EXPUNGE pseudo-op.                       */
/*                                                                            */
/******************************************************************************/
void printPermanentSymbolTable()
{
  int     ix;
  FILE   *permfile;
  char  *s_type;

  if(( permfile = fopen( permpathname, "w" )) == NULL )
  {
    exit( 2 );
  }

  fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" );
  fprintf( permfile, "        EXPUNGE\n/\n" );
  /* Print the memory reference instructions first.                           */
  s_type = "FIXMRI";
  for( ix = 0; ix < symbol_top; ix++ )
  {
    if( M_MRI( symtab[ix].type ))
    {
      fprintf( permfile, "%-7s %s=%4.4o\n",
                                    s_type, symtab[ix].name, symtab[ix].val );
    }
  }

  s_type = " ";
  for( ix = 0; ix < symbol_top; ix++ )
  {
    if( M_FIXED( symtab[ix].type ))
    {
      if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type ))
      {
        fprintf( permfile, "%-7s %s=%4.4o\n",
                                    s_type, symtab[ix].name, symtab[ix].val );
      }
    }
  }
  fprintf( permfile, "/\n        FIXTAB\n" );
  fclose( permfile );
} /* printPermanentSymbolTable()                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  printCrossReference                                            */
/*                                                                            */
/*  Synopsis:  Output a cross reference (concordance) for the file being      */
/*             assembled.                                                     */
/*                                                                            */
/******************************************************************************/
void printCrossReference()
{
  int    ix;
  int    symbol_base;
  int    xc;
  int    xc_index;
  int    xc_refcount;
  int    xc_cols;

  /* Force top of form for first page.                                        */
  page_lineno = LIST_LINES_PER_PAGE;

  list_lineno = 0;
  symbol_base = number_of_fixed_symbols;

  for( ix = symbol_base; ix < symbol_top; ix++ )
  {
    list_lineno++;
    page_lineno++;
    if( page_lineno >= LIST_LINES_PER_PAGE )
    {
      topOfForm( list_title, s_xref );
    }

    fprintf( listfile, "%5d", list_lineno );

    /* Get reference count & index into concordance table for this symbol.    */
    xc_refcount = symtab[ix].xref_count;
    xc_index = symtab[ix].xref_index;
    /* Determine how to label symbol on concordance.                          */
    switch( symtab[ix].type & ( DEFINED | REDEFINED ))
    {
    case UNDEFINED:
      fprintf( listfile, " U         ");
      break;

    case REDEFINED:
      fprintf( listfile, " M  %5d  ", xreftab[xc_index] );
      break;

    default:
      fprintf( listfile, " A  %5d  ", xreftab[xc_index] );
      break;
    }
    fprintf( listfile, "%-6.6s  ", symtab[ix].name );

    /* Output the references, 8 numbers per line after symbol name.           */
    for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ )
    {
      if( xc_cols >= XREF_COLUMNS )
      {
        xc_cols = 0;
        page_lineno++;
        if( page_lineno >= LIST_LINES_PER_PAGE )
        {
          topOfForm( list_title, s_xref);
        }
        list_lineno++;
        fprintf( listfile, "\n%5d%-19s", list_lineno, " " );
      }
      fprintf( listfile, "  %5d", xreftab[xc_index + xc] );
    }
    fprintf( listfile, "\n" );
  }
} /* printCrossReference()                                                    */


/******************************************************************************/
/*                                                                            */
/*  Function:  topOfForm                                                      */
/*                                                                            */
/*  Synopsis:  Prints title and sub-title on top of next page of listing.     */
/*                                                                            */
/******************************************************************************/
void topOfForm( char *title, char *sub_title )
{
  char temp[10];

  list_pageno++;
  strcpy( temp, s_page );
  sprintf( temp, "%s %d", s_page, list_pageno );

  /* Output a top of form if not the first page of the listing.               */
  if( list_pageno > 1 )
  {
    fprintf( listfile, "\f" );
  }
  fprintf( listfile, "\n\n\n      %-63s %10s\n", title, temp );

  /* Reset the current page line counter.                                     */
  page_lineno = 3;
  if( sub_title != NULL )
  {
    fprintf( listfile, "%80s\n", sub_title );
    page_lineno++;
  }
  else
  {
    fprintf( listfile, "\n" );
    page_lineno++;
  }
  fprintf( listfile, "\n" );
  page_lineno++;
} /* topOfForm()                                                              */


/******************************************************************************/
/*                                                                            */
/*  Function:  lexemeToName                                                   */
/*                                                                            */
/*  Synopsis:  Convert the current lexeme into a string.                      */
/*                                                                            */
/******************************************************************************/
char *lexemeToName( char *name, int from, int term )
{
  int  to;

  to = 0;

  while( from < term && to < ( SYMLEN - 1 ))
  {
    name[to++] = toupper( line[from++] );
  }

  while( to < SYMLEN )
  {
    name[to++] = '\0';
  }
  return( name );
} /* lexemeToName()                                                           */

/******************************************************************************/
/*                                                                            */
/*  Function:  defineLexeme                                                   */
/*                                                                            */
/*  Synopsis:  Put lexeme into symbol table with a value.                     */
/*                                                                            */
/******************************************************************************/
SYM_T *defineLexeme( int     start,     /* start of lexeme being defined.     */
                     int     term,      /* end+1 of lexeme being defined.     */
                     WORD16  val,       /* value of lexeme being defined.     */
                     SYMTYP  type )     /* how symbol is being defined.       */
{
  char  name[SYMLEN];

  lexemeToName( name, start, term);
  return( defineSymbol( name, val, type, start ));
} /* defineLexeme()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  defineSymbol                                                   */
/*                                                                            */
/*  Synopsis:  Define a symbol in the symbol table, enter symbol name if not  */
/*             not already in table.                                          */
/*                                                                            */
/******************************************************************************/
SYM_T *defineSymbol( char *name, WORD16 val, SYMTYP type, WORD16 start )
{
  SYM_T  *sym;
  int     xref_count;

  if( strlen( name ) < 1 )
  {
    return( &sym_undefined );   /* Protect against non-existent names.        */
  }
  sym = lookup( name );
  /* OS/8 PAL8 seems to allow permanent symbold to be redefined without error */
  if( ( M_FIXED( sym->type ) && pass == 1 && perm_redef_error ) || 
     (M_PERM_REDEFINED( sym->type ) && (sym->val != val)) )
  {
     type |= PERM_REDEFINED;
  }

  xref_count = 0;               /* Set concordance for normal defintion.      */

  if( M_DEFINED( sym->type ))
  {
    if( pass == 2 && ( (sym->val & 07777) != (val & 07777) || M_PERM_REDEFINED(sym->type)) )
    {
      /* Generate diagnostic if redefining a symbol.                          */
      if( M_PERM_REDEFINED( sym->type ) && (M_LABEL(sym->type) || M_LABEL(type)) )
      {
        errorSymbol( &illegal_redefine, sym->name, start );
      } else 
      {
        /* Generate diagnostic if redefining a symbol.                          */
        if( M_REDEFINED( sym->type ) && (M_LABEL(sym->type) || M_LABEL(type)) )
        {
          errorSymbol( &redefined_symbol, sym->name, start );
        }
      }
      type = type | REDEFINED;
      sym->xref_count++;      /* Referenced suymbol, count it.                */
      xref_count = sym->xref_count;
    }
  }

  if( pass == 2 && xref )
  {
    /* Put the definition line number in the concordance table.               */
    /* Defined symbols are not counted as references.                         */
    xreftab[sym->xref_index] = lineno;
    /* Put the line number in the concordance table.                          */
    xreftab[sym->xref_index + xref_count] = lineno;
  }

  /* Now set the value and the type.                                          */
  sym->val = ( M_LABEL(type) ) ? val : val & 07777;
  sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type;
  return( sym );
} /* defineSymbol()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  lookup                                                         */
/*                                                                            */
/*  Synopsis:  Find a symbol in table.  If not in table, enter symbol in      */
/*             table as undefined.  Return address of symbol in table.        */
/*                                                                            */
/******************************************************************************/
SYM_T *lookup( char *name )
{
  int     ix;                   /* Insertion index                            */
  int     lx;                   /* Left index                                 */
  int     rx;                   /* Right index                                */

  /* First search the permanent symbols.                                      */
  lx = 0;
  ix = binarySearch( name, lx, number_of_fixed_symbols );

  /* If symbol not in permanent symbol table.                                 */
  if( ix < 0 )
  {
    /* Now try the user symbol table.                                         */
    ix = binarySearch( name, number_of_fixed_symbols, symbol_top );

    /* If symbol not in user symbol table.                                    */
    if( ix < 0 )
    {
      /* Must put symbol in table if index is negative.                       */
      ix = ~ix;
      if( symbol_top + 1 >= SYMBOL_TABLE_SIZE )
      {
        errorSymbol( &symbol_table_full, name, lexstart );
        exit( 1 );
      }

      for( rx = symbol_top; rx >= ix; rx-- )
      {
        symtab[rx + 1] = symtab[rx];
      }
      symbol_top++;

      /* Enter the symbol as UNDEFINED with a value of zero.                  */
      strcpy( symtab[ix].name, name );
      symtab[ix].type = UNDEFINED;
      symtab[ix].val  = 0;
      symtab[ix].xref_count = 0;
      if( xref && pass == 2 )
      {
        xreftab[symtab[ix].xref_index] = 0;
      }
    }
  }

  return( &symtab[ix] );        /* Return the location of the symbol.         */
} /* lookup()                                                                 */


/******************************************************************************/
/*                                                                            */
/*  Function:  binarySearch                                                   */
/*                                                                            */
/*  Synopsis:  Searches the symbol table within the limits given.  If the     */
/*             symbol is not in the table, it returns the insertion point.    */
/*                                                                            */
/******************************************************************************/
int binarySearch( char *name, int start, int symbol_count )
{
  int     lx;                   /* Left index                                 */
  int     mx;                   /* Middle index                               */
  int     rx;                   /* Right index                                */
  int     compare;              /* Results of comparison                      */

  lx = start;
  rx = symbol_count - 1;
  while( lx <= rx )
  {
    mx = ( lx + rx ) / 2;   /* Find center of search area.                    */

    compare = strcmp( name, symtab[mx].name );

    if( compare < 0 )
    {
      rx = mx - 1;
    }
    else if( compare > 0 )
    {
      lx = mx + 1;
    }
    else
    {
      return( mx );         /* Found a match in symbol table.                 */
    }
  } /* end while                                                              */
  return( ~lx );            /* Return insertion point.                        */
} /* binarySearch()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  compareSymbols                                                 */
/*                                                                            */
/*  Synopsis:  Used to presort the symbol table when starting assembler.      */
/*                                                                            */
/******************************************************************************/
int compareSymbols( const void *a, const void *b )
{
  return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name ));
} /* compareSymbols()                                                         */


/******************************************************************************/
/*                                                                            */
/*  Function:  evalSymbol                                                     */
/*                                                                            */
/*  Synopsis:  Get the pointer for the symbol table entry if exists.          */
/*             If symbol doesn't exist, return a pointer to the undefined sym */
/*                                                                            */
/******************************************************************************/
SYM_T *evalSymbol()
{
  char   name[SYMLEN];
  SYM_T *sym;

  sym = lookup( lexemeToName( name, lexstart, lexterm ));

  /* The symbol goes in the concordance iff it is in a different position in  */
  /* the assembler source file.                                               */
  if( lexstart != last_xref_lexstart ||  lineno != last_xref_lineno )
  {
    sym->xref_count++;          /* Count the number of references to symbol.  */
    last_xref_lexstart = lexstart;
    last_xref_lineno = lineno;

    /* Put the line number in the concordance table.                          */
    if( xref && pass == 2 )
    {
      xreftab[sym->xref_index + sym->xref_count] = lineno;
    }
  }

  return( sym );
} /* evalSymbol()                                                             */


/******************************************************************************/
/*                                                                            */
/*  Function:  moveToEndOfLine                                                */
/*                                                                            */
/*  Synopsis:  Move the parser input to the end of the current input line.    */
/*                                                                            */
/******************************************************************************/
void moveToEndOfLine()
{
  while( !isend( line[cc] )) cc++;
  lexstart = cc;
  lexterm = cc;
  lexstartprev = lexstart;
} /* moveToEndOfLine()                                                        */

/******************************************************************************/
/*                                                                            */
/*  Function:  nextLexeme                                                     */
/*                                                                            */
/*  Synopsis:  Get the next lexical element from input line.                  */
/*                                                                            */
/******************************************************************************/
void nextLexeme()
{
  /* Save start column of previous lexeme for diagnostic messages.            */
  lexstartprev = lexstart;
  lextermprev = lexterm;

  while( is_blank( line[cc] )) { cc++; }
  lexstart = cc;

  if( isalnum( line[cc] ))
  {
    while( isalnum( line[cc] )) { cc++; }
  }
  else if( isend( line[cc] ))
  {
    /* End-of-Line, don't advance cc!                                         */
  }
  else
  {
    switch( line[cc] )
    {
    case '"':     /* Quoted letter                                            */
      if( cc + 2 < maxcc )
      {
        cc++;
        cc++;
      }
      else
      {
        errorMessage( &no_literal_value, lexstart );
        cc++;
      }
      break;

    case '/':     /* Comment, don't advance cc!                               */
      break;

    default:      /* All other punctuation.                                   */
      cc++;
      break;
    }
  }
  lexterm = cc;
} /* nextLexeme()                                                             */


/******************************************************************************/
/*                                                                            */
/*  Function:  nextLexBlank                                                   */
/*                                                                            */
/*  Synopsis:  Used to prevent illegal blanks in expressions.                 */
/*                                                                            */
/******************************************************************************/
void nextLexBlank()
{
  nextLexeme();
  if( is_blank( delimiter ))
  {
    errorMessage( &illegal_blank, lexstart - 1 );
  }
  delimiter = line[lexterm];
} /* nextLexBlank()                                                           */


/******************************************************************************/
/*                                                                            */
/*  Function:  pseudoOperators                                                */
/*                                                                            */
/*  Synopsis:  Process pseudo-ops (directives).                               */
/*                                                                            */
/******************************************************************************/
BOOL pseudoOperators( PSEUDO_T val )
{
  int     count, count2;
  int     delim;
  int     index;
  int     ix;
  int     lexstartsave;
  WORD16  newfield;
  WORD16  oldclc;
  int     pack;
  BOOL    status;
  SYM_T  *sym;
  FILE   *temp;
  int     term;
  WORD16  value;
  char os8_name[8];
  int     reloc_clc;

  status = TRUE;
  switch( (PSEUDO_T) val )
  {
  case ASCII:
    /* added 18-Jan-2003 PNT  -- derived from TEXT */
    delim = line[lexstart];
    index = lexstart + 1;
    while( line[index] != delim && !isend( line[index] ))
    {
      punchOutObject( clc, (line[index] & 127) | 128 );
      incrementClc();
      index++;
    }
    if( isend( line[index] ))
    {
      cc = index;
      lexterm = cc;
      errorMessage( &text_string, cc );
    }
    else
    {
      cc = index + 1;
      lexterm = cc;
    }
    nextLexeme();
    break;
  
  case BANK:
    errorSymbol( &no_pseudo_op, "BANK", lexstartprev );
    /* should select a different 32K out of 128K                              */
    break;

  case BINPUNCH:
    /* If there has been data output and this is a mode switch, set up to     */
    /* output data in BIN mode.                                               */
    if( binary_data_output && rim_mode )
    {
      clearLiteralTable();
      punchLeader( 8 );         /* Generate a short leader/trailer.           */
      checksum = 0;
      binary_data_output = FALSE;
    }
    rim_mode = FALSE;
    break;

  case DECIMAL:
    radix = 10;
    break;

  case DUBL:
    inputDubl();
    break;

  case EJECT:
    page_lineno = LIST_LINES_PER_PAGE;  /* This will force a page break.      */
    status = FALSE;             /* This will force reading of next line       */
    break;

  case ENPUNCH:
    if( pass == 2 )
    {
      objectfile = objectsave;
    }
    break;

  case EXPUNGE:                 /* Erase symbol table                         */
    if( pass == 1 )
    {
      symtab[0] = sym_undefined;
      symbol_top = 0;
      number_of_fixed_symbols = symbol_top;
      fixed_symbols = &symtab[symbol_top - 1];

      /* Enter the pseudo-ops into the symbol table.                          */
      for( ix = 0; ix < DIM( pseudo ); ix++ )
      {
        defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 );
      }
      /* Enter the really permanent symbols into the table.                   */
      /* Also make them part of the permanent symbol table.                   */
      for( ix = 0; ix < DIM( really_permanent_symbols ); ix++ )
      {
        defineSymbol( really_permanent_symbols[ix].name,
                      really_permanent_symbols[ix].val,
                      really_permanent_symbols[ix].type | DEFFIX , 0 );
      }
      number_of_fixed_symbols = symbol_top;
      fixed_symbols = &symtab[symbol_top - 1];

    }
    break;

  case FIELD:
    /* Punch page 0 also */
    punchLiteralPool( cp, 1 );
    newfield = field >> 12;
    lexstartsave = lexstartprev;
    if( isdone( line[lexstart] ))
    {
      newfield += 1;            /* Blank FIELD directive.                     */
    }
    else
    {
      newfield = (getExpr())->val;      /* FIELD with argument.               */
    }

    if( rim_mode )
    {
      errorMessage( &in_rim_mode, lexstartsave ); /* Can't change fields.     */
    }
    else if( newfield > 7 || newfield < 0 )
    {
      errorMessage( &illegal_field_value, lexstartprev );
    }
    else
    {
      value = (( newfield & 0007 ) << 3 ) | 00300;
      punchObject( value );
      if( objectfile != NULL )    /* Only fix checksum if punching */
      {
        checksum -= value;        /* Field punches are not added to checksum.   */
      }
      field = newfield << 12;
    }

    clc = 0200 | field;
    fieldlc = clc & 07777;

    if( !rim_mode )
    {
      punchOrigin( clc );
    }

    clearLiteralTable();

    break;

  case FIXMRI:
    if( line[lexterm] == '=' && isalpha( line[lexstart] ))
    {
      lexstartsave = lexstart;
      term = lexterm;
      nextLexeme();           /* Skip symbol.                                 */
      nextLexeme();           /* Skip trailing =                              */
      defineLexeme( lexstartsave, term, getExprs(), MRI );
    }
    else
    {
      errorLexeme( &symbol_syntax, lexstart );
      nextLexeme();           /* Skip symbol.                                 */
      nextLexeme();           /* Skip trailing =                              */
      (void) getExprs();      /* Skip expression.                             */
    }
    break;

  case FIXTAB:
    if (pass == 1) /* Only fix on first pass, on second all are defined */
    {
      /* Mark all current symbols as permanent symbols.                         */
      for( ix = 0; ix < symbol_top; ix++ )
      {
        symtab[ix].type = symtab[ix].type | FIXED;
      }
      number_of_fixed_symbols = symbol_top;
      fixed_symbols = &symtab[symbol_top - 1];

      /* Re-sort the symbol table                                               */
      qsort( symtab, symbol_top, sizeof(symtab[0]), compareSymbols );
    }
    break;

  case FLTG:
    inputFltg();
    /* errorSymbol( &no_pseudo_op, "FLTG", lexstartprev ); */
    break;

  case IFDEF:
    if( isalpha( line[lexstart] ))
    {
      sym = evalSymbol();
      nextLexeme();
      if( M_DEFINED_CONDITIONALLY( sym->type ))
      {
        conditionTrue();
      }
      else
      {
        conditionFalse();
      }
    }
    else
    {
      errorLexeme( &label_syntax, lexstart );
    }
    break;

  case IFNDEF:
    if( isalpha( line[lexstart] ))
    {
      sym = evalSymbol();
      nextLexeme();
      if( M_DEFINED_CONDITIONALLY( sym->type ))
      {
        conditionFalse();
      }
      else
      {
        conditionTrue();
      }
    }
    else
    {
      errorLexeme( &label_syntax, lexstart );
    }
    break;

  case IFNZERO:
    if( getExprs() == 0 )
    {
      conditionFalse();
    }
    else
    {
      conditionTrue();
    }
    break;

  case IFZERO:
    if( getExprs() == 0 )
    {
      conditionTrue();
    }
    else
    {
      conditionFalse();
    }
    break;

  case NOPUNCH:
    if( pass == 2 )
    {
      objectfile = NULL;
    }
    break;

  case OCTAL:
    radix = 8;
    break;

  case PAGE:
    reloc_clc = clc + reloc;
    punchLiteralPool( cp, 0 );
    oldclc = clc;
    if( isdone( line[lexstart] ))
    {
      clc = (( reloc_clc + 0177 ) & 077600) - reloc;  /* No argumnet.               */
      fieldlc = clc & 07777;
    }
    else
    {
      value = (getExpr())->val;
      clc = field + (( value & 037 ) << 7 ) - reloc;
      fieldlc = clc & 07777;
    }
    testForLiteralCollision( clc + reloc );

    if( !rim_mode && clc != oldclc )
    {
      punchOrigin( clc );
    }
    break;

  case PAUSE:
    break;

  case RELOC:
    if( isdone( line[lexstart] ))
    {
       reloc = 0;               /* Blank RELOC directive.                     */
    }
    else
    {
      value = (getExpr())->val; /* RELOC with argument.                       */
      reloc = (value & 07777) - ( clc  & 07777);
    }
    break;

  case RIMPUNCH:
    /* If the assembler has output any BIN data, output the literal tables    */
    /* and the checksum for what has been assembled and setup for RIM mode.   */
    if( binary_data_output && !rim_mode )
    {
      endOfBinary();
      clearLiteralTable();
      punchChecksum();
      punchLeader( 8 );         /* Generate a short leader/trailer.           */
    }
    rim_mode = TRUE;
    break;

  case SEGMNT:
    punchLiteralPool( cp, 0 );
    if( isdone( line[lexstart] ))
    {                           /* No argument.                               */
      clc = ( clc & 06000 ) + 02000;
      fieldlc = clc & 07777;
    }
    else
    {
      getExpr();
      clc = ( val & 003 ) << 10;
      fieldlc = clc & 07777;
    }
    if( !rim_mode )
    {
      punchOrigin( clc );
    }
    testForLiteralCollision( clc );
    break;

  case TEXT:
    delim = line[lexstart];
    pack = 0;
    count = 0;
    index = lexstart + 1;
    while( line[index] != delim && !isend( line[index] ))
    {
      pack = ( pack << 6 ) | ( line[index] & 077 );
      count++;
      if( count > 1 )
      {
        punchOutObject( clc, pack );
        incrementClc();
        count = 0;
        pack = 0;
      }
      index++;
    }

    if( count != 0 )
    {
      punchOutObject( clc, pack << 6 );
      incrementClc();
    }
    else
    {
      punchOutObject( clc, 0 );
      incrementClc();
    }

    if( isend( line[index] ))
    {
      cc = index;
      lexterm = cc;
      errorMessage( &text_string, cc );
    }
    else
    {
      cc = index + 1;
      lexterm = cc;
    }
    nextLexeme();
    break;

  case FILENAME:
    memset(os8_name, 0, sizeof(os8_name));
    delimiter=line[lexstart];
    if (delimiter != '.')
    {
      for (index = lexstart, count = 0; index < lexterm && count < 6; index++)
      {
        os8_name[count++] = line[index];
      }
      delimiter=line[lexterm];
      if (delimiter == '.')
      {
         nextLexeme();     /* Skip . */
      }
    }
    nextLexeme();
    if (delimiter == '.')
    {
      for (index = lexstart, count = 6; index < lexterm && count < 8; index++)
      {
        os8_name[count++] = line[index];
      }
    }

    pack = 0;
    count = 0;
    for (count2 = 0; count2 < 8; count2++) 
    {
      pack = ( pack << 6 ) | ( os8_name[count2] & 077 );
      count++;
      if( count > 1 )
      {
        punchOutObject( clc, pack );
        incrementClc();
        count = 0;
        pack = 0;
      }
    }
    nextLexeme();
    break;

  case DEVICE:
    memset(os8_name, 0, sizeof(os8_name));
    for (index = lexstart, count = 0; index < lexterm && count < 4; index++)
    {
      os8_name[count++] = line[index];
    }

    pack = 0;
    count = 0;
    for (count2 = 0; count2 < 4; count2++) 
    {
      pack = ( pack << 6 ) | ( os8_name[count2] & 077 );
      count++;
      if( count > 1 )
      {
        punchOutObject( clc, pack );
        incrementClc();
        count = 0;
        pack = 0;
      }
    }

    nextLexeme();
    break;

  case TITLE:
    delim = line[lexstart];
    ix = lexstart + 1;
    /* Find string delimiter.                                                 */
    do
    {
      if( list_title[ix] == delim && list_title[ix + 1] == delim )
      {
        ix++;
      }
      ix++;
    } while( line[ix] != delim && !isend(line[ix]) );

    if( line[ix] == delim )
    {
      count = 0;
      ix = lexstart + 1;
      do
      {
        if( list_title[ix] == delim && list_title[ix + 1] == delim )
        {
          ix++;
        }
        list_title[count] = line[ix];
        count++;
        ix++;
        list_title[count] = '\0';
      } while( line[ix] != delim && !isend(line[ix]) );

      if( strlen( list_title ) > TITLELEN )
      {
        list_title[TITLELEN] = '\0';
      }

      cc = ix + 1;
      lexterm = cc;
      page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles.  */
      list_title_set = TRUE;
    }
    else
    {
      cc = ix;
      lexterm = cc;
      errorMessage( &text_string, cc );
    }

    nextLexeme();
    break;

  case XLIST:
    if( isdone( line[lexstart] ))
    {
      temp = listfile;          /* Blank XLIST directive.                     */
      listfile = listsave;
      listsave = temp;
    }
    else
    {
      if( (getExpr())->val == 0 )
      {
        if( listfile == NULL )
        {
          listfile = listsave;
          listsave = NULL;
        }
      }
      else
      {
        if( listfile != NULL )
        {
          listsave = listfile;
          listfile = NULL;
        }
      }
    }
    break;

  case ZBLOCK:
    value = (getExpr())->val;
    if( value < 0 )
    {
      errorMessage( &zblock_too_small, lexstartprev );
    }
    else if( value + ( clc & 07777 ) - 1 > 07777 )
    {
      errorMessage( &zblock_too_large, lexstartprev );
    }
    else
    {
      for( ; value > 0; value-- )
      {
        punchOutObject( clc, 0 );
        incrementClc();
      }
    }

    break;

  default:
    break;
  } /* end switch for pseudo-ops                                              */
  return( status );
} /* pseudoOperators()                                                        */


/******************************************************************************/
/*                                                                            */
/*  Function:  conditionFalse                                                 */
/*                                                                            */
/*  Synopsis:  Called when a false conditional has been evaluated.            */
/*             Lex should be the opening <; ignore all text until             */
/*             the closing >.                                                 */
/*                                                                            */
/******************************************************************************/
void conditionFalse()
{
  int     level;

  if( line[lexstart] == '<' )
  {
    /* Invariant: line[cc] is the next unexamined character.                  */
    level = 1;
    while( level > 0 )
    {
      if( isend( line[cc] ))
      {
        readLine();
      }
      else
      {
        switch( line[cc] )
        {
        case '>':
          level--;
          cc++;
          break;

        case '<':
          level++;
          cc++;
          break;

        case '$':
          level = 0;
          cc++;
          break;

        default:
          cc++;
          break;
        } /* end switch                                                       */
      } /* end if                                                             */
    } /* end while                                                            */
    nextLexeme();
  }
  else
  {
    errorMessage( &lt_expected, lexstart );
  }
} /* conditionFalse()                                                         */

/******************************************************************************/
/*                                                                            */
/*  Function:  conditionTrue                                                  */
/*                                                                            */
/*  Synopsis:  Called when a true conditional has been evaluated.             */
/*             Lex should be the opening <; skip it and setup for             */
/*             normal assembly.                                               */
/*                                                                            */
/******************************************************************************/
void conditionTrue()
{
  if( line[lexstart] == '<' )
  {
    nextLexeme();               /* Skip the opening '<'                       */
  }
  else
  {
    errorMessage( &lt_expected, lexstart );
  }
} /* conditionTrue()                                                          */


/******************************************************************************/
/*                                                                            */
/*  Function:  errorLexeme                                                    */
/*                                                                            */
/*  Synopsis:  Display an error message using the current lexical element.    */
/*                                                                            */
/******************************************************************************/
void errorLexeme( EMSG_T *mesg, int col )
{
  char   name[SYMLEN];

  errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col );
} /* errorLexeme()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  errorSymbol                                                    */
/*                                                                            */
/*  Synopsis:  Display an error message with a given string.                  */
/*                                                                            */
/******************************************************************************/
void errorSymbol( EMSG_T *mesg, char *name, int col )
{
  char   linecol[12];
  char  *s;

  if( pass == 2 )
  {
    s = ( name == NULL ) ? "" : name ;
    errors++;
    sprintf( linecol, "(%d:%d)", lineno, col + 1 );
    fprintf( errorfile, "%s%-9s : error:  %s \"%s\" at Loc = %5.5o\n",
                                      filename, linecol, mesg->file, s, clc );
    saveError( mesg->list, col );
  }
  error_in_line = TRUE;
} /* errorSymbol()                                                            */


/******************************************************************************/
/*                                                                            */
/*  Function:  errorMessage                                                   */
/*                                                                            */
/*  Synopsis:  Display an error message without a name argument.              */
/*                                                                            */
/******************************************************************************/
void errorMessage( EMSG_T *mesg, int col )
{
  char   linecol[12];

  if( pass == 2 )
  {
    errors++;
    sprintf( linecol, "(%d:%d)", lineno, col + 1 );
    fprintf( errorfile, "%s%-9s : error:  %s at Loc = %5.5o\n",
                                         filename, linecol, mesg->file, clc );
    saveError( mesg->list, col );
  }
  error_in_line = TRUE;
} /* errorMessage()                                                           */

/******************************************************************************/
/*                                                                            */
/*  Function:  saveError                                                      */
/*                                                                            */
/*  Synopsis:  Save the current error in a list so it may displayed after the */
/*             the current line is printed.                                   */
/*                                                                            */
/******************************************************************************/
void saveError( char *mesg, int col )
{
  if( save_error_count < DIM( error_list ))
  {
    error_list[save_error_count].mesg = mesg;
    error_list[save_error_count].col = col;
    save_error_count++;
  }
  error_in_line = TRUE;

  if( listed )
  {
    printErrorMessages();
  }
} /* saveError()                                                              */
/* End-of-File                                                                */
Added src/ptp2txt.c.



















































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* 
 * Program to convert between POSIX ASCII text files
 * and the output of OS/8 PIP to the Paper Tape Punch.

 * The OS/8 paper tape punch format is:
 *
 *     leader: a bunch of ASCII NUL chars to be ignored.
 *     ASCII with the 8th bit set, CR+LF line endings.
 *     trailer: a bunch of ASCII NUL chars to be ignored.

 * This program can be used as a filter from stdin to stdout or
 * it will create a new file with name ending in .txt if going to
 * POSIX text or .ptp if going to OS/8 PIP Paper Tape format.

 * If the program is called with the name "txt2ptp" then
 * LTCOUNT (default 100) bytes of leader is prepended to the
 * output file and LTCOUNT bytes of leader are appended.
 * The 8th bit of every output character is set, and LF-only
 * input is turned into CR+LF output.  CR+LF input is passed
 * as-is.
 
 * If called by any other name, the ASCII NUL character is
 * ignored anywhere in the file, and the 8th bit is cleared.
 * Line endings are untouched in this case.

 * This program helps work around the issue that the 
 * OS/8 Paper Tape reader handler assumes the last
 * character in the buffer is junk, so that when you send
 * a plain text file into PDP-8 SIMH OS/8 with the
 * ptr device, the last character is lost.
 */

/*
 * Author: Bill Cattey
 * License:  The SIMH License:

 * Copyright © 2015-2017
 * by Bill Cattey et. ux. William Cattey et. ux. Poetnerd

 * 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 include
 * 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.
 */

#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>

#define BLOCK_SIZE 256
#define TO_PTP 1
#define TO_TXT 2
#define LTCHAR '\0'
#define LTCOUNT 100

/* global variable: ltbuf */
int global_ltbuf[LTCOUNT];


void make_txt (FILE *fpin, FILE *fpout)
{
    int inchar, outchar;
    int read_ct, n;
    char *obuffp;
    char ibuff[BLOCK_SIZE], obuff[BLOCK_SIZE];

    while ((read_ct = fread (ibuff, sizeof(char), BLOCK_SIZE, fpin))) {
        obuffp = obuff;
        for (n = 0; n < read_ct; n++) {
            inchar = *(ibuff + n);
            if (inchar == LTCHAR) continue;
            *obuffp++ = inchar & 0177;
        }
        fwrite (obuff, sizeof(char), obuffp - obuff, fpout);
    }
}

/* We could just create an empty buffer and output it,
   but this is better if for some reason LTCHAR changes. */ 
void init_ltbuf ()
{
    int n;

    for (n = 0; n < LTCOUNT; n++) {
        global_ltbuf[n] = LTCHAR;
    }
}

void make_lt (FILE *fpout)
{
    fwrite (global_ltbuf, sizeof(char), LTCOUNT, fpout);
}

void make_ptp (FILE *fpin, FILE *fpout)
{
    int inchar, outchar, prior = '\0';
    int read_ct, n;
    char *obuffp;
    char ibuff[BLOCK_SIZE];
    /* Every \n might add a \r to the output. 
       Worst case is obuff doubles in size. */
    char obuff[2*BLOCK_SIZE];

    make_lt (fpout);

    while ((read_ct = fread (ibuff, sizeof(char), BLOCK_SIZE, fpin))) {
        obuffp = obuff;
        for (n = 0; n < read_ct; n++) {
            inchar = *(ibuff + n);
            if (inchar == '\n' && prior != '\r') {
                *obuffp++ = (char)('\r' | 0200);
            }
            *obuffp++ = inchar | 0200;
            prior = inchar;
        }
        fwrite (obuff, sizeof(char), obuffp - obuff, fpout);
    }
    /* If we don't already have an EOF, add one. */
    if (inchar != '\032') {
      fwrite ("\232", sizeof(char), 1, fpout);
    }
    make_lt (fpout);
}


void process_file (char *fname, int flag)
{
    FILE *fpin, *fpout;

    char *ofname;
    char *fend;

    if (flag == TO_PTP) fend = ".ptp";
    else fend = ".txt";

    ofname = malloc (((strlen (fname) + strlen(fend)) * sizeof (char)) + 1);
    strcpy (ofname, fname);
    strcat (ofname, fend);

    /* printf ("Filename is: %s.\n", ofname); */

    if ((fpin = fopen (fname, "r")) == NULL) {
        printf ("Open of input file %s failed with status %d.  Skipping.\n",
                fname, errno);
        return;
    }
    if ((fpout = fopen (ofname, "w")) == NULL) {
        printf ("Open of output file %s failed with status %d.  Skipping.\n",
                ofname, errno);
        return;
    }
    if (flag == TO_PTP)
        make_ptp (fpin, fpout);
    else
        make_txt (fpin, fpout);

    fclose (fpin);
    fclose (fpout);
    free (ofname);
}

int main (int argc, char *argv[])
{
    int i, flag;
    char *ltbuf;

    if (strcmp (basename (argv[0]), "txt2ptp") == 0) {
        /* printf ("Flag is TO_PTP"); */
        flag = TO_PTP;
        init_ltbuf ();
    }
    else {
        flag = TO_TXT;
    }

    if (argc == 1) {
        if (flag == TO_PTP) make_ptp (stdin, stdout);
        else make_txt (stdin, stdout);
    }
    else {
        for (i = 1; i < argc; i++) {
            process_file (argv[i], flag);
        }
    }
}

Changes to src/scanswitch.c.
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
36
37
38
39
40
41
42


43


44


45





46
47
48

49







50










51
























52
53
54
55
56
57
58







-
-
+
-
-
+
-
-
+
-
-
-
-
-
+


-
+
-
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-








#define pgpio (&gpio)


int main()
{
	int i,j,k,switchscan[2], tmp;
	struct bcm2835_peripheral gpio;
	

	// ------------ Find gpio address (different for Pi 2) -------------

	extern struct bcm2835_peripheral gpio;
	gpio.addr_p = bcm_host_get_peripheral_address() + 0x200000;

    if (map_gpio_for_pidp8i (1) != 0)
	if (gpio.addr_p== 0x20200000) printf("scanswitch - RPi Plus\n");
	else printf("scanswitch - RPi 2\n");

	if (map_peripheral(&gpio, 0) != 0)
	{	printf("Failed to map the physical GPIO registers into the virtual memory space.\n");
	{	printf("Failed to map the GPIO SoC peripheral into our VM space.\n");
		return -1;
	}

    init_pidp8i_gpio();
	// initialise GPIO (all pins used as inputs, with pull-ups enabled on cols)
	for (i=0;i<NLEDROWS;i++)	// Define ledrows as input
		INP_GPIO(ledrows[i]);
	for (i=0;i<NCOLS;i++)		// Define cols as input
		INP_GPIO(cols[i]);
	for (i=0;i<NROWS;i++)		// Define rows as input
		INP_GPIO(rows[i]);

	// BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs
	GPIO_PULL = 2;				// pull-up
	short_wait();       		// must wait 150 cycles
	GPIO_PULLCLK0 = 0x0fff0; 	// selects GPIO pins 4..15 (assumes we avoid pins 2 and 3!)
	short_wait();
	GPIO_PULL = 0;  			// reset GPPUD register
	short_wait();
	GPIO_PULLCLK0 = 0; 			// remove clock
	short_wait(); 	        	// probably unnecessary

	// Read the switches
	// BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs
	GPIO_PULL = 0;				// no pull-up no pull-down just float
	short_wait();	        	// must wait 150 cycles
	GPIO_PULLCLK0 = 0x0ff00000;	// selects GPIO pins 20..27
	short_wait();
	GPIO_PULL = 0; 				// reset GPPUD register
	short_wait();
	GPIO_PULLCLK0 = 0; 			// remove clock
	short_wait();       		// probably unnecessary

	// BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs
	GPIO_PULL = 0;				// no pull-up no pull down just float
	short_wait();	        	// must wait 150 cycles
	GPIO_PULLCLK0 = 0x070000; 	// selects GPIO pins 16..18
	short_wait();
	GPIO_PULL = 0; 				// reset GPPUD register
	short_wait();
	GPIO_PULLCLK0 = 0; 			// remove clock
	short_wait();       		// probably unnecessary
	// --------------------------------------------------


	// prepare for reading switches		

	for (uint8_t row=1;row<=2;row++)		// do rows 2 (for IF switches) and 3 (for STOP switch)
	{		
		INP_GPIO(rows[row]);
		OUT_GPIO(rows[row]);			// turn on one switch row
		GPIO_CLR = 1 << rows[row];		// and output 0V to overrule built-in pull-up from column input pin
	
		sleep_us(10);                   // unnecessarily long?
Changes to src/scp.c.
239
240
241
242
243
244
245
246

247
248
249
250
251

252
253
254
255
256
257
258
239
240
241
242
243
244
245

246



247

248
249
250
251
252
253
254
255







-
+
-
-
-

-
+







   11-Dec-95    RMS     Fixed ordering bug in save/restore
   22-May-95    RMS     Added symbolic input
   13-Apr-95    RMS     Added symbolic printouts
*/

/* Macros and data structures */

#ifdef PIDP8I
#define NOT_MUX_USING_CODE  /* sim_tmxr library provider or agnostic */
#include "gpio-common.h"        // for start/stop_pidp8i_gpio_thread()
#include "PDP8/pidp8i.h"        // for build_pidp8i_scp_cmd()
#endif

#define NOT_MUX_USING_CODE /* sim_tmxr library provider or agnostic */
#define IN_SCP_C 1          /* Include from scp.c */

#include "sim_defs.h"
#include "sim_rev.h"
#include "sim_disk.h"
#include "sim_tape.h"
#include "sim_ether.h"
#include "sim_serial.h"
272
273
274
275
276
277
278





279
280
281
282
283
284
285
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287







+
+
+
+
+







#endif
#include <sys/stat.h>
#include <setjmp.h>

#if defined(HAVE_DLOPEN)                                /* Dynamic Readline support */
#include <dlfcn.h>
#endif

#ifdef PIDP8I
#include "gpio-common.h"        // for start/stop_pidp8i_gpio_thread()
#include "PDP8/pidp8i.h"        // for build_pidp8i_scp_cmd()
#endif

#ifndef MAX
#define MAX(a,b)  (((a) >= (b)) ? (a) : (b))
#endif

/* search logical and boolean ops */

344
345
346
347
348
349
350

351
352
353
354
355
356
357
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360







+







#endif
#define GET_SWITCHES(cp) \
    if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
#define GET_RADIX(val,dft) \
    if (sim_switches & SWMASK ('O')) val = 8; \
    else if (sim_switches & SWMASK ('D')) val = 10; \
    else if (sim_switches & SWMASK ('H')) val = 16; \
    else if ((sim_switch_number >= 2) && (sim_switch_number <= 36)) val = sim_switch_number; \
    else val = dft;

/* Asynch I/O support */
#if defined (SIM_ASYNCH_IO)
pthread_mutex_t sim_asynch_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t sim_asynch_wake = PTHREAD_COND_INITIALIZER;

495
496
497
498
499
500
501





502

503
504
505
506
507
508
509
498
499
500
501
502
503
504
505
506
507
508
509

510
511
512
513
514
515
516
517







+
+
+
+
+
-
+








/* Command support routines */

SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
int32 test_search (t_value *val, SCHTAB *schptr);
static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char);
typedef enum {
    SW_ERROR,           /* Parse Error */
    SW_BITMASK,         /* Bitmask Value or Not a switch */
    SW_NUMBER           /* Numeric Value */
    } SWITCH_PARSE;
int32 get_switches (const char *cptr);
SWITCH_PARSE get_switches (const char *cptr, int32 *sw_val, int32 *sw_number);
CONST char *get_sim_sw (CONST char *cptr);
t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
t_value get_rval (REG *rptr, uint32 idx);
void put_rval (REG *rptr, uint32 idx, t_value val);
void fprint_help (FILE *st);
void fprint_stopped (FILE *st, t_stat r);
void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
552
553
554
555
556
557
558

559
560
561
562
563
564
565
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574







+








/* Global data */

DEVICE *sim_dflt_dev = NULL;
UNIT *sim_clock_queue = QUEUE_LIST_END;
int32 sim_interval = 0;
int32 sim_switches = 0;
int32 sim_switch_number = 0;
FILE *sim_ofile = NULL;
TMLN *sim_oline = NULL;
MEMFILE *sim_mfile = NULL;
SCHTAB *sim_schrptr = FALSE;
SCHTAB *sim_schaptr = FALSE;
DEVICE *sim_dfdev = NULL;
UNIT *sim_dfunit = NULL;
578
579
580
581
582
583
584




585
586
587
588
589
590
591
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604







+
+
+
+







char *sim_brk_act_buf[MAX_DO_NEST_LVL];
BRKTAB **sim_brk_tab = NULL;
int32 sim_brk_ent = 0;
int32 sim_brk_lnt = 0;
int32 sim_brk_ins = 0;
int32 sim_quiet = 0;
int32 sim_step = 0;
char *sim_sub_instr = NULL;
char *sim_sub_instr_buf = NULL;
size_t sim_sub_instr_size = 0;
size_t *sim_sub_instr_off = NULL;
static double sim_time;
static uint32 sim_rtime;
static int32 noqueue_time;
volatile int32 stop_cpu = 0;
static char **sim_argv;
t_value *sim_eval = NULL;
static t_value sim_last_val;
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
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







+











+
+
+
+
+
+
+







char *sim_prompt = NULL;                                /* prompt string */
static FILE *sim_gotofile;                              /* the currently open do file */
static int32 sim_goto_line[MAX_DO_NEST_LVL+1];          /* the current line number in the currently open do file */
static int32 sim_do_echo = 0;                           /* the echo status of the currently open do file */
static int32 sim_show_message = 1;                      /* the message display status of the currently open do file */
static int32 sim_on_inherit = 0;                        /* the inherit status of on state and conditions when executing do files */
static int32 sim_do_depth = 0;
static t_bool sim_cmd_echoed = FALSE;                   /* Command was emitted already prior to message output */

static int32 sim_on_check[MAX_DO_NEST_LVL+1];
static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1];
static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE];
static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1];
static const char *sim_do_label[MAX_DO_NEST_LVL+1];

t_stat sim_last_cmd_stat;                               /* Command Status */

static SCHTAB sim_stabr;                                /* Register search specifier */
static SCHTAB sim_staba;                                /* Memory search specifier */

static DEBTAB sim_dflt_debug[] = {
    {"EVENT",       SIM_DBG_EVENT,      "Event Dispatching"},
    {"ACTIVATE",    SIM_DBG_ACTIVATE,   "Event Scheduling"},
    {"AIO_QUEUE",   SIM_DBG_AIO_QUEUE,  "Asynchronous Event Queueing"},
  {0}
};

static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0)  };
static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0)  };
#if defined USE_INT64
static const char *sim_si64 = "64b data";
#else
static const char *sim_si64 = "32b data";
802
803
804
805
806
807
808
809
810
811




812
813
814
815
816
817
818
823
824
825
826
827
828
829



830
831
832
833
834
835
836
837
838
839
840







-
-
-
+
+
+
+







      "                         interpreted as an address\n"
      "3Switches\n"
      " Switches can be used to control the format of display information:\n\n"
       /***************** 80 character line width template *************************/
      "++-a                 display as ASCII\n"
      "++-c                 display as character string\n"
      "++-m                 display as instruction mnemonics\n"
      "++-o                 display as octal\n"
      "++-d                 display as decimal\n"
      "++-h                 display as hexadecimal\n\n"
      "++-o or -8           display as octal\n"
      "++-d or -10          display as decimal\n"
      "++-h or -16          display as hexadecimal\n"
      "++-2                 display as binary\n\n"
      " The simulators typically accept symbolic input (see documentation with each\n"
      " simulator).\n\n"
      "3Examples\n"
      " Examples:\n\n"
      "++ex 1000-1100           examine 1000 to 1100\n"
      "++de PC 1040             set PC to 1040\n"
      "++ie 40-50               interactively examine 40:50\n"
994
995
996
997
998
999
1000
1001

1002
1003
1004
1005
1006
1007
1008
1016
1017
1018
1019
1020
1021
1022

1023
1024
1025
1026
1027
1028
1029
1030







-
+







      " to open the file read only.  If the file does not exist, or the unit does\n"
      " not support read only operation, an error occurs.  Input-only devices, such\n"
      " as paper-tape readers, and devices with write lock switches, such as disks\n"
      " and tapes, support read only operation; other devices do not.  If a file is\n"
      " attached read only, its contents can be examined but not modified.\n"
      "5-q\n"
      " If the -q switch is specified when creating a new file (-n) or opening one\n"
      " read only (-r), the message announcing this fact is suppressed.\n"
      " read only (-r), any messages announcing these facts will be suppressed.\n"
      "5-f\n"
      " For simulated magnetic tapes, the ATTACH command can specify the format of\n"
      " the attached tape image file:\n\n"
      "++ATTACH -f <tape_unit> <format> <filename>\n\n"
      " The currently supported tape image file formats are:\n\n"
      "++SIMH                   SIMH simulator format\n"
      "++E11                    E11 simulator format\n"
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
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







-
+


-
+
+


-
+


-
+
+
+
+
+
+
+
+






-
-
-
-
+
+
+
+
+







      "++DIR {path}                list directory files\n"
#define HLP_LS          "*Commands Listing_Files LS"
      "3LS\n"
      "++LS {path}                 list directory files\n"
      "2Displaying Files\n"
#define HLP_TYPE         "*Commands Displaying_Files TYPE"
      "3TYPE\n"
      "++TYPE {file}               display a file contents\n"
      "++TYPE file                 display a file contents\n"
#define HLP_CAT          "*Commands Displaying_Files CAT"
      "3CAT\n"
      "++CAT {file}                display a file contents\n"
      "++CAT file                  display a file contents\n"
      "2Removing Files\n"
#define HLP_DELETE       "*Commands Removing_Files DEL"
      "3DELETE\n"
      "++DEL{ete} {file}           deletes a file\n"
      "++DEL{ete} file             deletes a file\n"
#define HLP_RM          "*Commands Removing_Files RM"
      "3RM\n"
      "++RM {file}                 deletes a file\n"
      "++RM file                   deletes a file\n"
      "2Copying Files\n"
#define HLP_COPY        "*Commands Copying_Files COPY"
      "3COPY\n"
      "++COPY sfile dfile          copies a file\n"
#define HLP_CP          "*Commands Copying_Files CP"
      "3CP\n"
      "++CP sfile dfile            copies a file\n"
#define HLP_SET         "*Commands SET"
      "2SET\n"
       /***************** 80 character line width template *************************/
#define HLP_SET_CONSOLE "*Commands SET CONSOLE"
      "3Console\n"
      "+set console arg{,arg...}    set console options\n"
      "+set console WRU             specify console drop to simh character\n"
      "+set console BRK             specify console Break character\n"
      "+set console DEL             specify console delete character\n"
      "+set console PCHAR           specify console printable characters\n"
      "+set console WRU=value       specify console drop to simh character\n"
      "+set console BRK=value       specify console Break character\n"
      "+set console DEL=value       specify console delete character\n"
      "+set console PCHAR=bitmask   bit mask of printable characters in\n"
      "++++++++                     range [31,0]\n"
      "+set console SPEED=speed{*factor}\n"
      "++++++++                     specify console input data rate\n"
      "+set console TELNET=port     specify console telnet port\n"
      "+set console TELNET=LOG=log_file\n"
      "++++++++                     specify console telnet logging to the\n"
      "++++++++                     specified destination {LOG,STDOUT,STDERR,\n"
      "++++++++                     DEBUG or filename)\n"
1179
1180
1181
1182
1183
1184
1185



1186
1187
1188
1189
1190

1191

1192
1193








1194
1195
1196
1197
1198
1199
1200
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







+
+
+




-
+

+


+
+
+
+
+
+
+
+







#if defined (SIM_ASYNCH_CLOCKS)
      "+set clock asynch            enable asynchronous clocks\n"
      "+set clock noasynch          disable asynchronous clocks\n"
#endif
      "+set clock nocatchup         disable catchup clock ticks\n"
      "+set clock catchup           enable catchup clock ticks\n"
      "+set clock calib=n%%          specify idle calibration skip %%\n"
      "+set clock stop=n            stop execution after n instructions\n\n"
      " The set clock stop command allows execution to have a bound when\n"
      " execution starts with a BOOT, NEXT or CONTINUE command.\n"
#define HLP_SET_ASYNCH "*Commands SET Asynch"
      "3Asynch\n"
      "+set asynch                  enable asynchronous I/O\n"
      "+set noasynch                disable asynchronous I/O\n"
#define HLP_SET_ENVIRON "*Commands SET Asynch"
#define HLP_SET_ENVIRON "*Commands SET Environment"
      "3Environment\n"
      "4Explicitily Changing A Variable\n"
      "+set environment name=val    set environment variable\n"
      "+set environment name        clear environment variable\n"
      "4Gathering Input From A User\n"
      " Input from a user can be obtained by:\n\n"
      "+set environment -p \"Prompt String\" name=default\n\n"
      " The -p switch indicates that the user should be prompted\n"
      " with the indicated prompt string and the input provided\n"
      " will be saved in the environment variable 'name'.  If no\n"
      " input is provided, the value specified as 'default' will be\n"
      " used.\n"
#define HLP_SET_ON      "*Commands SET Command_Status_Trap_Dispatching"
      "3Command Status Trap Dispatching\n"
      "+set on                      enables error checking after command\n"
      "++++++++                     execution\n"
      "+set noon                    disables error checking after command\n"
      "++++++++                     execution\n"
      "+set on inherit              enables inheritance of ON state and\n"
1224
1225
1226
1227
1228
1229
1230
1231

1232
1233
1234
1235
1236
1237
1238
1267
1268
1269
1270
1271
1272
1273

1274
1275
1276
1277
1278
1279
1280
1281







-
+







      "++++++++                     messages\n"
      "+set noquiet                 re-enables suppression of some output and\n"
      "++++++++                     messages\n"
#define HLP_SET_PROMPT "*Commands SET Command_Prompt"
      "3Command Prompt\n"
      "+set prompt \"string\"        sets an alternate simulator prompt string\n"
      "3Device and Unit\n"
      "+set <dev> OCT|DEC|HEX       set device display radix\n"
      "+set <dev> OCT|DEC|HEX|BIN   set device display radix\n"
      "+set <dev> ENABLED           enable device\n"
      "+set <dev> DISABLED          disable device\n"
      "+set <dev> DEBUG{=arg}       set device debug flags\n"
      "+set <dev> NODEBUG={arg}     clear device debug flags\n"
      "+set <dev> arg{,arg...}      set device parameters (see show modifiers)\n"
      "+set <unit> ENABLED          enable unit\n"
      "+set <unit> DISABLED         disable unit\n"
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
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







+


















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







#define HLP_DO          "*Commands Executing_Command_Files"
      "2Executing Command Files\n"
      " The simulator can execute command files with the DO command:\n\n"
      "++DO <filename> {arguments...}       execute commands in file\n\n"
      " The DO command allows command files to contain substitutable arguments.\n"
      " The string %%n, where n is between 1 and 9, is replaced with argument n\n"
      " from the DO command line. The string %%0 is replaced with <filename>.\n"
      " The string %%* is replaced by the whole set of arguments (%%1 ... %%9).\n"
      " The sequences \\%% and \\\\ are replaced with the literal characters %% and \\,\n"
      " respectively.  Arguments with spaces can be enclosed in matching single\n"
      " or double quotation marks.\n\n"
      " DO commands may be nested up to ten invocations deep.\n\n"
      "3Switches\n"
      " If the switch -v is specified, the commands in the file are echoed before\n"
      " they are executed.\n\n"
      " If the switch -e is specified, command processing (including nested command\n"
      " invocations) will be aborted if a command error is encountered.\n"
      " (Simulation stop never abort processing; use ASSERT to catch unexpected\n"
      " stops.)  Without the switch, all errors except ASSERT failures will be\n"
      " ignored, and command processing will continue.\n\n"
      " If the switch -o is specified, the on conditions and actions from the\n"
      " calling command file will be inherited in the command file being invoked.\n"
      " If the switch -q is specified, the quiet mode will be explicitly enabled\n"
      " for the called command file, otherwise quiet mode is inherited from the\n"
      " calling context.\n"
       /***************** 80 character line width template *************************/
      "3Variable_Insertion\n"
      " Built In variables %%DATE%%, %%TIME%%, %%DATETIME%%, %%LDATE%%, %%LTIME%%,\n"
      " %%CTIME%%, %%DATE_YYYY%%, %%DATE_YY%%, %%DATE_YC%%, %%DATE_MM%%, %%DATE_MMM%%,\n"
      " %%DATE_MONTH%%, %%DATE_DD%%, %%DATE_D%%, %%DATE_WYYYY%%, %%DATE_WW%%,\n"
      " %%TIME_HH%%, %%TIME_MM%%, %%TIME_SS%%, %%STATUS%%, %%TSTATUS%%, %%SIM_VERIFY%%,\n"
      " %%SIM_QUIET%%, %%SIM_MESSAGE%% %%SIM_MESSAGE%%\n"
      " %%SIM_NAME%%, %%SIM_BIN_NAME%%, %%SIM_BIN_PATH%%m %%SIM_OSTYPE%%\n\n"
      "+Token %%0 expands to the command file name.\n"
      "+Token %%n (n being a single digit) expands to the n'th argument\n"
      "+Token %%* expands to the whole set of arguments (%%1 ... %%9)\n\n"
      "+The input sequence \"%%%%\" represents a literal \"%%\".  All other\n"
      "+character combinations are rendered literally.\n\n"
      "+Omitted parameters result in null-string substitutions.\n\n"
      "+Tokens preceeded and followed by %% characters are expanded as environment\n"
      "+variables, and if an environment variable isn't found then it can be one of\n"
      "+several special variables:\n\n"
      "++%%DATE%%              yyyy-mm-dd\n"
      "++%%TIME%%              hh:mm:ss\n"
      "++%%DATETIME%%          yyyy-mm-ddThh:mm:ss\n"
      "++%%LDATE%%             mm/dd/yy (Locale Formatted)\n"
      "++%%LTIME%%             hh:mm:ss am/pm (Locale Formatted)\n"
      "++%%CTIME%%             Www Mmm dd hh:mm:ss yyyy (Locale Formatted)\n"
      "++%%UTIME%%             nnnn (Unix time - seconds since 1/1/1970)\n"
      "++%%DATE_YYYY%%         yyyy        (0000-9999)\n"
      "++%%DATE_YY%%           yy          (00-99)\n"
      "++%%DATE_MM%%           mm          (01-12)\n"
      "++%%DATE_MMM%%          mmm         (JAN-DEC)\n"
      "++%%DATE_MONTH%%        month       (January-December)\n"
      "++%%DATE_DD%%           dd          (01-31)\n"
      "++%%DATE_WW%%           ww          (01-53)     ISO 8601 week number\n"
      "++%%DATE_WYYYY%%        yyyy        (0000-9999) ISO 8601 week year number\n"
      "++%%DATE_D%%            d           (1-7)       ISO 8601 day of week\n"
      "++%%DATE_JJJ%%          jjj         (001-366) day of year\n"
      "++%%DATE_19XX_YY%%      yy          A year prior to 2000 with the same\n"
      "++++++++++   calendar days as the current year\n"
      "++%%DATE_19XX_YYYY%%    yyyy        A year prior to 2000 with the same\n"
      "++++++++++   calendar days as the current year\n"
      "++%%TIME_HH%%           hh          (00-23)\n"
      "++%%TIME_MM%%           mm          (00-59)\n"
      "++%%TIME_SS%%           ss          (00-59)\n"
      "++%%STATUS%%            Status value from the last command executed\n"
      "++%%TSTATUS%%           The text form of the last status value\n"
      "++%%SIM_VERIFY%%        The Verify/Verbose mode of the current Do command file\n"
      "++%%SIM_VERBOSE%%       The Verify/Verbose mode of the current Do command file\n"
      "++%%SIM_QUIET%%         The Quiet mode of the current Do command file\n"
      "++%%SIM_MESSAGE%%       The message display status of the current Do command file\n"
      "++%%SIM_NAME%%          The name of the current simulator\n"
      "++%%SIM_BIN_NAME%%      The program name of the current simulator\n"
      "++%%SIM_BIN_PATH%%      The program path that invoked the current simulator\n"
      "++%%SIM_OSTYPE%%        The Operating System running the current simulator\n\n"
      "+Environment variable lookups are done first with the precise name between\n"
      "+the %% characters and if that fails, then the name between the %% characters\n"
      "+is upcased and a lookup of that valus is attempted.\n\n"
      "+The first Space delimited token on the line is extracted in uppercase and\n"
      "+then looked up as an environment variable.  If found it the value is\n"
      "+supstituted for the original string before expanding everything else.  If\n"
      "+it is not found, then the original beginning token on the line is left\n"
      "+untouched.\n"
#define HLP_GOTO        "*Commands Executing_Command_Files GOTO"
      "3GOTO\n"
      " Commands in a command file execute in sequence until either an error\n"
      " trap occurs (when a command completes with an error status), or when an\n"
      " explict request is made to start command execution elsewhere with the\n"
      " GOTO command:\n\n"
      "++GOTO <label>\n\n"
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
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







+
+





-
+
+
+
+
+
+
+

-
-
+
+
-
-
+

+
+
+
-
+


-
+

-
-
-
+

+
-
-
+
+
+







       /***************** 80 character line width template *************************/
#define HLP_SEND        "*Commands Executing_Command_Files Injecting_Console_Input"
       /***************** 80 character line width template *************************/
      "3Injecting Console Input\n"
      " The SEND command provides a way to insert input into the console device of\n"
      " a simulated system as if it was entered by a user.\n\n"
      "++SEND {-t} {after=nn,}{delay=nn,}\"<string>\"\n\n"
      "++NOSEND\n\n"
      "++SHOW SEND\n\n"
      " The string argument must be delimited by quote characters.  Quotes may\n"
      " be either single or double but the opening and closing quote characters\n"
      " must match.  Data in the string may contain escaped character strings.\n\n"
      " The SEND command can also insert input into any serial device on a\n"
      " simulated system as if it was entered by a user.\n\n"
      "++SEND {-t} <dev>:line {after=nn,}{delay=nn,}\"<string>\"\n\n"
      "++SEND {-t} {<dev>:line} {after=nn,}{delay=nn,}\"<string>\"\n\n"
      "++NOSEND {<dev>:line}\n\n"
      "++SHOW SEND {<dev>:line}\n\n"
      " The NOSEND command removes any undelivered input data which may be\n"
      " pending on the CONSOLE or a specific multiplexer line.\n\n"
      " The SHOW SEND command displays any pending SEND activity for the\n"
      " CONSOLE or a specific multiplexer line.\n"
      "4Delay\n"
      " Specifies a positive integer representing a minimal instruction delay\n"
      " between characters being sent.  The value specified in a delay\n"
      " Specifies an integer (>=0) representing a minimal instruction delay\n"
      " between characters being sent.  The delay parameter can be set by\n"
      " argument persists across SEND commands to the same device (console or\n"
      " serial device).  The delay parameter can be set by itself with:\n\n"
      " itself with:\n\n"
      "++SEND DELAY=n\n\n"
      " which will set the default delay value for subsequent SEND commands\n"
      " which don't specify an explicit DELAY parameter along with a string\n"
      " If a SEND command is processed and no DELAY value has been specified,\n"
      " The default value of the delay parameter is 1000.\n"
      " the default value of the delay parameter is 1000.\n"
       /***************** 80 character line width template *************************/
      "4After\n"
      " Specifies a positive integer representing a minimal number of instructions\n"
      " Specifies an integer (>=0) representing a minimal number of instructions\n"
      " which must execute before the first character in the string is sent.\n"
      " The value specified as the after parameter persists across SEND commands\n"
      " to the same device (console or serial device).   The after parameter value\n"
      " can be set by itself with:\n\n"
      " The after parameter value can be set by itself with:\n\n"
      "++SEND AFTER=n\n\n"
      " which will set the default after value for subsequent SEND commands\n"
      " If the after parameter isn't explicitly set, it defaults to the value of\n"
      " the delay parameter.\n"
      " which don't specify an explicit AFTER parameter along with a string\n"
      " If a SEND command is processed and no AFTER value has been specified,\n"
      " the default value of the delay parameter is the DELAY parameter value.\n"
      "4Escaping String Data\n"
      " The following character escapes are explicitly supported:\n"
      "++\\r  Sends the ASCII Carriage Return character (Decimal value 13)\n"
      "++\\n  Sends the ASCII Linefeed character (Decimal value 10)\n"
      "++\\f  Sends the ASCII Formfeed character (Decimal value 12)\n"
      "++\\t  Sends the ASCII Horizontal Tab character (Decimal value 9)\n"
      "++\\v  Sends the ASCII Vertical Tab character (Decimal value 11)\n"
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
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







+

















-
+
+
+
+







#define HLP_EXPECT      "*Commands Executing_Command_Files Reacting_To_Console_Output"
       /***************** 80 character line width template *************************/
      "3Reacting To Console Output\n"
      " The EXPECT command provides a way to stop execution and take actions\n"
      " when specific output has been generated by the simulated system.\n\n"
      "++EXPECT {dev:line} {[count]} {HALTAFTER=n,}\"<string>\" {actioncommand {; actioncommand}...}\n\n"
      "++NOEXPECT {dev:line} \"<string>\"\n\n"
      "++SHOW EXPECT {dev:line}\n\n"
      " The string argument must be delimited by quote characters.  Quotes may\n"
      " be either single or double but the opening and closing quote characters\n"
      " must match.  Data in the string may contain escaped character strings.\n"
      " If a [count] is specified, the rule will match after the match string\n"
      " has matched count times.\n\n"
      " When multiple expect rules are defined with the same match string, they\n"
      " will match in the same order they were defined in.\n\n"
      " When expect rules are defined, they are evaluated agains recently\n"
      " produced output as each character is output to the device.  Since this\n"
      " evaluation processing is done on each output character, rule matching\n"
      " is not specifically line oriented.  If line oriented matching is desired\n"
      " then rules should be defined which contain the simulated system's line\n"
      " ending character sequence (i.e. \"\\r\\n\").\n"
      " Once data has matched any expect rule, that data is no longer eligible\n"
      " to match other expect rules which may already be defined.\n"
      " Data which is output prior to the definition of an expect rule is not\n"
      " eligible to be matched against.\n\n"
      " The NOEXPECT command removes a previously defined EXPECT command.\n"
      " The NOEXPECT command removes a previously defined EXPECT command for the\n"
      " console or a specific multiplexer line.\n\n"
      " The SHOW EXPECT command displays all of the pending EXPECT state for\n"
      " the console or a specific multiplexer line.\n"
       /***************** 80 character line width template *************************/
      "4Switches\n"
      " Switches can be used to influence the behavior of EXPECT rules\n\n"
      "5-p\n"
      " EXPECT rules default to be one shot activities.  That is a rule is\n"
      " automatically removed when it matches unless it is designated as a\n"
      " persistent rule by using a -p switch when the rule is defined.\n"
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704



1705


1706
1707
1708
1709
1710










1711
1712
1713
1714
1715
1716
1717
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







-
-
-
+
+
+

+
+





+
+
+
+
+
+
+
+
+
+







      "++\\n{n{n}} where each n is an octal digit (0-7)\n"
      " and hext character values of the form:\n"
      "++\\xh{h} where each h is a hex digit (0-9A-Fa-f)\n"
      "4HaltAfter\n"
      " Specifies the number of instructions which should be executed before\n"
      " simulator instruction execution should stop.  The default is to stop\n"
      " executing instructions immediately (i.e. HALTAFTER=0).\n"
      " The HaltAfter delay, once set, persists for all expect behaviors for\n"
      " that device.\n"
      " The HaltAfter parameter value can be set by itself with:\n\n"
      " The default HaltAfter delay, once set, persists for all expect behaviors\n"
      " for that device.\n"
      " The default HaltAfter parameter value can be set by itself with:\n\n"
      "++EXPECT HALTAFTER=n\n\n"
      " A unique HaltAfter value can be specified with each expect matching rule\n"
      " which if it is not specified then the default value will be used.\n"
      " To avoid potentially unpredictable system hehavior that will happen\n"
      " if multiple expect rules are in effect and a haltafter value is large\n"
      " enough for more than one expect rule to match before an earlier haltafter\n"
      " delay has expired, only a single EXPECT rule can be defined if a non-zero\n"
      " HaltAfter parameter has been set.\n"
      /***************** 80 character line width template *************************/
#define HLP_SLEEP       "*Commands Executing_Command_Files Pausing_Command_Execution"
      "3Pausing Command Execution\n"
      " A simulator command file may wait for a specific period of time with the\n\n"
      "++SLEEP NUMBER[SUFFIX]...\n\n"
      " Pause for NUMBER seconds.  SUFFIX may be 's' for seconds (the default),\n"
      " 'm' for minutes, 'h' for hours or 'd' for days.  NUMBER may be an\n"
      " arbitrary floating point number.  Given two or more arguments, pause\n"
      " for the amount of time specified by the sum of their values.\n"
      " NOTE: A SLEEP command is interruptable with SIGINT (^C).\n\n"
      /***************** 80 character line width template *************************/
#define HLP_ASSERT      "*Commands Executing_Command_Files Testing_Simulator_State"
#define HLP_IF          "*Commands Executing_Command_Files Testing_Simulator_State"
      "3Testing Simulator State\n"
      " There are two ways for a command file to examine simulator state and\n"
      " then take action based on that state:\n"
      "4ASSERT\n"
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
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







+
+















-
+







+
+














-
+







      "5Examples:\n"
      " A command file might be used to bootstrap an operating system that\n"
      " halts after the initial load from disk.  The ASSERT command is then\n"
      " used to confirm that the load completed successfully by examining the\n"
      " CPU's \"A\" register for the expected value:\n\n"
      "++; OS bootstrap command file\n"
      "++;\n"
      "++IF EXIST \"os.disk\" echo os.disk exists\n"
      "++IF NOT EXIST os.disk echo os.disk not existing\n"
      "++ATTACH DS0 os.disk\n"
      "++BOOT DS\n"
      "++; A register contains error code; 0 = good boot\n"
      "++ASSERT A=0\n"
      "++ATTACH MT0 sys.tape\n"
      "++ATTACH MT1 user.tape\n"
      "++RUN\n\n"
       /***************** 80 character line width template *************************/
      " In the example, if the A register is not 0, the \"ASSERT A=0\" command will\n"
      " be echoed, the command file will be aborted with an \"Assertion failed\"\n"
      " message.  Otherwise, the command file will continue to bring up the\n"
      " operating system.\n"
      "4IF\n"
      " The IF command tests a simulator state condition and executes additional\n"
      " commands if the condition is true:\n\n"
      "++IF <Simulator State Expressions> commandtoprocess{; additionalcommandtoprocess}...\n\n"
      "++IF <Conditional Expressions> commandtoprocess{; additionalcommandtoprocess}...\n\n"
      "5Examples:\n"
      " A command file might be used to bootstrap an operating system that\n"
      " halts after the initial load from disk.  The ASSERT command is then\n"
      " used to confirm that the load completed successfully by examining the\n"
      " CPU's \"A\" register for the expected value:\n\n"
      "++; OS bootstrap command file\n"
      "++;\n"
      "++IF EXIST \"os.disk\" echo os.disk exists\n"
      "++IF NOT EXIST os.disk echo os.disk not existing\n"
      "++ATTACH DS0 os.disk\n"
      "++BOOT DS\n"
      "++; A register contains error code; 0 = good boot\n"
      "++IF NOT A=0 echo Boot failed - Failure Code; EX A; exit AFAIL\n"
      "++ATTACH MT0 sys.tape\n"
      "++ATTACH MT1 user.tape\n"
      "++RUN\n\n"
       /***************** 80 character line width template *************************/
      " In the example, if the A register is not 0, the message \"Boot failed -\n"
      " Failure Code:\" command will be displayed, the contents of the A register\n"
      " will be displayed and the command file will be aborted with an \"Assertion\n"
      " failed\" message.  Otherwise, the command file will continue to bring up\n"
      " the operating system.\n"
      "4Conditional Expressions\n"
      " The IF and ASSERT commands evaluate two different forms of conditional\n"
      " The IF and ASSERT commands evaluate three different forms of conditional\n"
      " expressions.:\n\n"
      "5Simulator State Expressions\n"
      " The values of simulator registers can be evaluated with:\n\n"
      "++{NOT} {<dev>} <reg>|<addr>{<logical-op><value>}<conditional-op><value>\n\n"
      " If <dev> is not specified, CPU is assumed.  <reg> is a register (scalar\n"
      " or subscripted) belonging to the indicated device.  <addr> is an address\n"
      " in the address space of the indicated device.  The <conditional-op>\n"
1805
1806
1807
1808
1809
1810
1811





1812
1813
1814
1815
1816
1817
1818
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955







+
+
+
+
+







      "++GTR - greater than\n"
      "++>=  - greater than or equal\n"
      "++GEQ - greater than or equal\n\n"
      " Comparisons are generic.  This means that if both string1 and string2 are\n"
      " comprised of all numeric digits, then the strings are converted to numbers\n"
      " and a numeric comparison is performed. For example: \"+1\" EQU \"1\" will be\n"
      " true.\n"
      "5File Existence Expressions\n"
      " File existence can be determined with:\n"
      "++{NOT} EXIST \"<filespec>\"\n\n"
      "++{NOT} EXIST <filespec>\n\n"
      " Specifies a true (false {NOT}) condition if the file exists.\n"
       /***************** 80 character line width template *************************/
#define HLP_EXIT        "*Commands Exiting_The_Simulator"
      "2Exiting The Simulator\n"
      " EXIT (synonyms QUIT and BYE) returns control to the operating system.\n"
       /***************** 80 character line width template *************************/
#define HLP_SCREENSHOT  "*Commands Screenshot_Video_Window"
      "2Screenshot Video Window\n"
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
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







+
+













-
+
+


+







    { "PWD",        &pwd_cmd,       0,          HLP_PWD },
    { "DIR",        &dir_cmd,       0,          HLP_DIR },
    { "LS",         &dir_cmd,       0,          HLP_LS },
    { "TYPE",       &type_cmd,      0,          HLP_TYPE },
    { "CAT",        &type_cmd,      0,          HLP_CAT },
    { "DELETE",     &delete_cmd,    0,          HLP_DELETE },
    { "RM",         &delete_cmd,    0,          HLP_RM },
    { "COPY",       &copy_cmd,      0,          HLP_COPY },
    { "CP",         &copy_cmd,      0,          HLP_CP },
    { "SET",        &set_cmd,       0,          HLP_SET },
    { "SHOW",       &show_cmd,      0,          HLP_SHOW },
    { "DO",         &do_cmd,        1,          HLP_DO },
    { "GOTO",       &goto_cmd,      1,          HLP_GOTO },
    { "RETURN",     &return_cmd,    0,          HLP_RETURN },
    { "SHIFT",      &shift_cmd,     0,          HLP_SHIFT },
    { "CALL",       &call_cmd,      0,          HLP_CALL },
    { "ON",         &on_cmd,        0,          HLP_ON },
    { "IF",         &assert_cmd,    0,          HLP_IF },
    { "PROCEED",    &noop_cmd,      0,          HLP_PROCEED },
    { "IGNORE",     &noop_cmd,      0,          HLP_IGNORE },
    { "ECHO",       &echo_cmd,      0,          HLP_ECHO },
    { "ASSERT",     &assert_cmd,    1,          HLP_ASSERT },
    { "SEND",       &send_cmd,      0,          HLP_SEND },
    { "SEND",       &send_cmd,      1,          HLP_SEND },
    { "NOSEND",     &send_cmd,      0,          HLP_SEND },
    { "EXPECT",     &expect_cmd,    1,          HLP_EXPECT },
    { "NOEXPECT",   &expect_cmd,    0,          HLP_EXPECT },
    { "SLEEP",      &sleep_cmd,     0,          HLP_SLEEP },
    { "!",          &spawn_cmd,     0,          HLP_SPAWN },
    { "HELP",       &help_cmd,      0,          HLP_HELP },
#if defined(USE_SIM_VIDEO)
    { "SCREENSHOT", &screenshot_cmd,0,          HLP_SCREENSHOT },
#endif
    { NULL, NULL, 0 }
    };
1929
1930
1931
1932
1933
1934
1935

1936
1937
1938
1939
1940
1941
1942
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084







+







    { NULL,         NULL,                       0 }
    };

static C1TAB set_dev_tab[] = {
    { "OCTAL",      &set_dev_radix,     8 },
    { "DECIMAL",    &set_dev_radix,     10 },
    { "HEX",        &set_dev_radix,     16 },
    { "BINARY",     &set_dev_radix,     2 },
    { "ENABLED",    &set_dev_enbdis,    1 },
    { "DISABLED",   &set_dev_enbdis,    0 },
    { "DEBUG",      &set_dev_debug,     1 },
    { "NODEBUG",    &set_dev_debug,     0 },
    { NULL,         NULL,               0 }
    };

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







-
+











-
+






+







sim_switches = 0;                                       /* init switches */
lookswitch = TRUE;
stdnul = fopen(NULL_DEVICE,"wb");
for (i = 1; i < argc; i++) {                            /* loop thru args */
    if (argv[i] == NULL)                                /* paranoia */
        continue;
    if ((*argv[i] == '-') && lookswitch) {              /* switch? */
        if ((sw = get_switches (argv[i])) < 0) {
        if (get_switches (argv[i], &sw, NULL) == SW_ERROR) {
            fprintf (stderr, "Invalid switch %s\n", argv[i]);
            return 0;
            }
        sim_switches = sim_switches | sw;
        }
    else {
        if ((strlen (argv[i]) + strlen (cbuf) + 3) >= sizeof(cbuf)) {
            fprintf (stderr, "Argument string too long\n");
            return 0;
            }
        if (*cbuf)                                      /* concat args */
            sim_strlcat (cbuf, " ", sizeof(cbuf)); 
            strlcat (cbuf, " ", sizeof (cbuf)); 
        sprintf(&cbuf[strlen(cbuf)], "%s%s%s", strchr(argv[i], ' ') ? "\"" : "", argv[i], strchr(argv[i], ' ') ? "\"" : "");
        lookswitch = FALSE;                             /* no more switches */
        }
    }                                                   /* end for */
sim_quiet = sim_switches & SWMASK ('Q');                /* -q means quiet */
sim_on_inherit = sim_switches & SWMASK ('O');           /* -o means inherit on state */


sim_init_sock ();                                       /* init socket capabilities */
AIO_INIT;                                               /* init Asynch I/O */
if (sim_vm_init != NULL)                                /* call once only */
    (*sim_vm_init)();
sim_finit ();                                           /* init fio package */
setenv ("SIM_NAME", sim_name, 1);                       /* Publish simulator name */
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
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







+


+
+
+
+
+













+







        sim_error_text (stat));
    return 0;
    }
if (!sim_quiet) {
    printf ("\n");
    show_version (stdout, NULL, NULL, 0, NULL);
    }
show_version (stdnul, NULL, NULL, 1, NULL);             /* Quietly set SIM_OSTYPE */
if (sim_dflt_dev == NULL)                               /* if no default */
    sim_dflt_dev = sim_devices[0];
if (((sim_dflt_dev->flags & DEV_DEBUG) == 0) &&         /* default device without debug? */
    (sim_dflt_dev->debflags == NULL)) {
    sim_dflt_dev->flags |= DEV_DEBUG;                   /* connect default event debugging */
    sim_dflt_dev->debflags = sim_dflt_debug;
    }
if (*argv[0]) {                                         /* sim name arg? */
    char *np;                                           /* "path.ini" */

    strncpy (nbuf, argv[0], PATH_MAX + 1);              /* copy sim name */
    if ((np = (char *)match_ext (nbuf, "EXE")))         /* remove .exe */
        *np = 0;
    np = strrchr (nbuf, '/');                           /* stript path and try again in cwd */
    if (np == NULL)
        np = strrchr (nbuf, '\\');                      /* windows path separator */
    if (np == NULL)
        np = strrchr (nbuf, ']');                       /* VMS path separator */
    if (np != NULL)
        setenv ("SIM_BIN_NAME", np+1, 1);               /* Publish simulator binary name */
    setenv ("SIM_BIN_PATH", argv[0], 1);
    }
sim_argv = argv;
cptr = getenv("HOME");
if (cptr == NULL) {
    cptr = getenv("HOMEPATH");
    cptr2 = getenv("HOMEDRIVE");
    }
2147
2148
2149
2150
2151
2152
2153
2154

2155
2156
2157
2158
2159
2160
2161
2297
2298
2299
2300
2301
2302
2303

2304
2305
2306
2307
2308
2309
2310
2311







-
+







    stat = do_cmd (0, cbuf);                            /* proc cmd file */
else if (*argv[0]) {                                    /* sim name arg? */
    char *np;                                           /* "path.ini" */
    nbuf[0] = '"';                                      /* starting " */
    strncpy (nbuf + 1, argv[0], PATH_MAX + 1);          /* copy sim name */
    if ((np = (char *)match_ext (nbuf, "EXE")))         /* remove .exe */
        *np = 0;
    sim_strlcat (nbuf, ".ini\"", sizeof(nbuf));         /* add .ini" */
    strlcat (nbuf, ".ini\"", sizeof (nbuf));            /* add .ini" */
    stat = do_cmd (-1, nbuf) & ~SCPE_NOMESSAGE;         /* proc default cmd file */
    if (stat == SCPE_OPENERR) {                         /* didn't exist/can't open? */
        np = strrchr (nbuf, '/');                       /* stript path and try again in cwd */
        if (np == NULL)
            np = strrchr (nbuf, '\\');                  /* windows path separator */
        if (np == NULL)
            np = strrchr (nbuf, ']');                   /* VMS path separator */
2209
2210
2211
2212
2213
2214
2215

2216
2217
2218
2219
2220
2221
2222
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373







+







    else cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prmopt*/
    if (cptr == NULL) {                                 /* EOF? */
        if (sim_ttisatty()) continue;                   /* ignore tty EOF */
        else break;                                     /* otherwise exit */
        }
    if (*cptr == 0)                                     /* ignore blank */
        continue;
    sim_cmd_echoed = TRUE;
    sim_sub_args (cbuf, sizeof(cbuf), argv);
    if (sim_log)                                        /* log cmd */
        fprintf (sim_log, "%s%s\n", sim_prompt, cptr);
    if (sim_deb && (sim_deb != sim_log) && (sim_deb != stdout))
        fprintf (sim_deb, "%s%s\n", sim_prompt, cptr);
    cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
    sim_switches = 0;                                   /* init switches */
2249
2250
2251
2252
2253
2254
2255
2256

2257
2258
2259
2260
2261
2262
2263
2400
2401
2402
2403
2404
2405
2406

2407
2408
2409
2410
2411
2412
2413
2414







-
+








if ((!cptr) || (*cptr == '\0'))
    return SCPE_ARG;

cptr = get_glyph_nc (cptr, gbuf, '"');                  /* get quote delimited token */
if (gbuf[0] == '\0') {                                  /* Token started with quote */
    gbuf[sizeof (gbuf)-1] = '\0';
    strncpy (gbuf, cptr, sizeof (gbuf)-1);
    strlcpy (gbuf, cptr, sizeof (gbuf));
    gptr = strchr (gbuf, '"');
    if (gptr)
        *gptr = '\0';
    }
sim_prompt = (char *)realloc (sim_prompt, strlen (gbuf) + 2);   /* nul terminator and trailing blank */
sprintf (sim_prompt, "%s ", gbuf);
return SCPE_OK;
2475
2476
2477
2478
2479
2480
2481
2482

2483
2484
2485
2486
2487
2488
2489
2626
2627
2628
2629
2630
2631
2632

2633
2634
2635
2636
2637
2638
2639
2640







-
+







if (dptr->flags & DEV_DISABLE) {
    fprint_header (st, &found, header);
    sprintf (buf, "set %s ENABLE", sim_dname (dptr));
    fprintf (st,  "%-30s\tEnables device %s\n", buf, sim_dname (dptr));
    sprintf (buf, "set %s DISABLE", sim_dname (dptr));
    fprintf (st,  "%-30s\tDisables device %s\n", buf, sim_dname (dptr));
    }
if (dptr->flags & DEV_DEBUG) {
if ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) {
    fprint_header (st, &found, header);
    sprintf (buf, "set %s DEBUG", sim_dname (dptr));
    fprintf (st,  "%-30s\tEnables debugging for device %s\n", buf, sim_dname (dptr));
    sprintf (buf, "set %s NODEBUG", sim_dname (dptr));
    fprintf (st,  "%-30s\tDisables debugging for device %s\n", buf, sim_dname (dptr));
    if (dptr->debflags) {
        t_bool desc_available = FALSE;
2553
2554
2555
2556
2557
2558
2559
2560

2561
2562
2563
2564
2565
2566
2567
2704
2705
2706
2707
2708
2709
2710

2711
2712
2713
2714
2715
2716
2717
2718







-
+







        if ((!mptr->disp) || (!mptr->pstring) || !(*mptr->pstring))
            continue;
        fprint_header (st, &found, header);
        sprintf (buf, "show %s %s%s", sim_dname (dptr), mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "=arg" : "");
        fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
        }
    }
if (dptr->flags & DEV_DEBUG) {
if ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) {
    fprint_header (st, &found, header);
    sprintf (buf, "show %s DEBUG", sim_dname (dptr));
    fprintf (st, "%-30s\tDisplays debugging status for device %s\n", buf, sim_dname (dptr));
    }
if ((dptr->modifiers) && (dptr->units) && (dptr->numunits != 1)) {
    for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
        if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2927
2928
2929
2930
2931
2932
2933
2934

2935
2936
2937
2938
2939
2940
2941
3078
3079
3080
3081
3082
3083
3084

3085
3086
3087
3088
3089
3090
3091
3092







-
+







echo = (sim_switches & SWMASK ('V')) || sim_do_echo;    /* -v means echo */
sim_quiet = (sim_switches & SWMASK ('Q')) || sim_quiet; /* -q means quiet */
sim_on_inherit =(sim_switches & SWMASK ('O')) || sim_on_inherit; /* -o means inherit ON condition actions */

errabort = sim_switches & SWMASK ('E');                 /* -e means abort on error */

abuf[sizeof(abuf)-1] = '\0';
strncpy (abuf, fcptr, sizeof(abuf)-1);
strlcpy (abuf, fcptr, sizeof(abuf));
c = abuf;
do_arg[10] = NULL;                                      /* make sure the argument list always ends with a NULL */
for (nargs = 0; nargs < 10; ) {                         /* extract arguments */
    while (sim_isspace (*c))                                /* skip blanks */
        c++;
    if (*c == 0)                                        /* all done? */
        do_arg [nargs++] = NULL;                        /* null argument */
2950
2951
2952
2953
2954
2955
2956
2957
2958


2959
2960
2961
2962
2963
2964
2965
3101
3102
3103
3104
3105
3106
3107


3108
3109
3110
3111
3112
3113
3114
3115
3116







-
-
+
+







            *c++ = 0;
        }
    }                                                   /* end for */

if (do_arg [0] == NULL)                                 /* need at least 1 */
    return SCPE_2FARG;
if ((fpin = fopen (do_arg[0], "r")) == NULL) {          /* file failed to open? */
    sim_strlcpy (cbuf, do_arg[0], sizeof (cbuf));       /* try again with .sim extension */
    sim_strlcat (cbuf, ".sim", sizeof (cbuf));
    strlcpy (cbuf, do_arg[0], sizeof (cbuf));           /* try again with .sim extension */
    strlcat (cbuf, ".sim", sizeof (cbuf));
    if ((fpin = fopen (cbuf, "r")) == NULL) {           /* failed a second time? */
        if (flag == 0)                                  /* cmd line file? */
             fprintf (stderr, "Can't open file %s\n", do_arg[0]);
        return SCPE_OPENERR;                            /* return failure */
        }
    }
if (flag >= 0) {                                        /* Only bump nesting from command or nested */
2982
2983
2984
2985
2986
2987
2988
2989
2990


2991
2992
2993
2994
2995
2996
2997
3133
3134
3135
3136
3137
3138
3139


3140
3141
3142
3143
3144
3145
3146
3147
3148







-
-
+
+







                    }
                strcpy(sim_on_actions[sim_do_depth][i], sim_on_actions[sim_do_depth-1][i]);
                }
            }
        }
    }

sim_strlcpy( sim_do_filename[sim_do_depth], do_arg[0], 
             sizeof (sim_do_filename[sim_do_depth]));   /* stash away do file name for possible use by 'call' command */
strlcpy( sim_do_filename[sim_do_depth], do_arg[0], 
         sizeof (sim_do_filename[sim_do_depth]));       /* stash away do file name for possible use by 'call' command */
sim_do_label[sim_do_depth] = label;                     /* stash away do label for possible use in messages */
sim_goto_line[sim_do_depth] = 0;
if (label) {
    sim_gotofile = fpin;
    sim_do_echo = echo;
    stat = goto_cmd (0, label);
    if (stat != SCPE_OK) {
3015
3016
3017
3018
3019
3020
3021

3022
3023
3024
3025
3026
3027
3028
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180







+







        stat = SCPE_OK;                                 /* set good return */
        break;
        }
    if (*cptr == 0)                                     /* ignore blank */
        continue;
    if (echo)                                           /* echo if -v */
        sim_printf("%s> %s\n", do_position(), cptr);
    sim_cmd_echoed = echo;
    if (*cptr == ':')                                   /* ignore label */
        continue;
    cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
    sim_switches = 0;                                   /* init switches */
    sim_gotofile = fpin;
    sim_do_echo = echo;
    if ((cmdp = find_cmd (gbuf))) {                     /* lookup command */
3063
3064
3065
3066
3067
3068
3069
3070

3071
3072
3073
3074
3075
3076
3077
3215
3216
3217
3218
3219
3220
3221

3222
3223
3224
3225
3226
3227
3228
3229







-
+







        case SCPE_STEP:
            break;
        default:
            break;
        }
    if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) &&   /* error from cmd? */
        (stat != SCPE_STEP)) {
        if (!echo && !sim_quiet &&                      /* report if not echoing */
        if (!echo &&                                    /* report if not echoing */
            !stat_nomessage &&                          /* and not suppressing messages */
            !(cmdp && cmdp->message)) {                 /* and not handling them specially */
            sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
            }
        }
    if (!stat_nomessage) {                              /* report error if not suppressed */
        if (cmdp && cmdp->message)                      /* special message handler */
3097
3098
3099
3100
3101
3102
3103

3104
3105
3106
3107
3108
3109
3110
3111
3112
3249
3250
3251
3252
3253
3254
3255
3256
3257

3258
3259
3260
3261
3262
3263
3264







+

-







Cleanup_Return:
fclose (fpin);                                          /* close file */
sim_gotofile = NULL;
if (flag >= 0) {
    sim_do_echo = saved_sim_do_echo;                    /* restore echo state we entered with */
    sim_show_message = saved_sim_show_message;          /* restore message display state we entered with */
    sim_on_inherit = saved_sim_on_inherit;              /* restore ON inheritance state we entered with */
    sim_quiet = saved_sim_quiet;                        /* restore quiet mode we entered with */
    }
sim_quiet = saved_sim_quiet;                            /* restore quiet mode we entered with */
if ((flag >= 0) || (!sim_on_inherit)) {
    for (i=0; i<SCPE_MAX_ERR; i++) {                    /* release any on commands */
        free (sim_on_actions[sim_do_depth][i]);
        sim_on_actions[sim_do_depth][i] = NULL;
        }
    sim_on_check[sim_do_depth] = 0;                     /* clear on mode */
    }
3127
3128
3129
3130
3131
3132
3133
3134

3135
3136
3137
3138
3139


3140
3141
3142
3143

3144
3145
3146
3147

3148
3149

3150
3151
3152
3153
3154
3155
3156
3279
3280
3281
3282
3283
3284
3285

3286
3287
3288
3289


3290
3291
3292
3293
3294

3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310







-
+



-
-
+
+



-
+




+


+







                     and other enviroment variables

   Calling sequence
   instr        =       input string
   instr_size   =       sizeof input string buffer
   do_arg[10]   =       arguments

   Token "%0" expands to the command file name. 
   Token %0 expands to the command file name. 
   Token %n (n being a single digit) expands to the n'th argument
   Tonen %* expands to the whole set of arguments (%1 ... %9)

   The input sequence "\%" represents a literal "%", and "\\" represents a
   literal "\".  All other character combinations are rendered literally.
   The input sequence "%%" represents a literal "%".  All other 
   character combinations are rendered literally.

   Omitted parameters result in null-string substitutions.

   A Tokens preceeded and followed by % characters are expanded as environment
   Tokens preceeded and followed by % characters are expanded as environment
   variables, and if one isn't found then can be one of several special 
   variables: 
          %DATE%              yyyy-mm-dd
          %TIME%              hh:mm:ss
          %DATETIME%          yyyy-mm-ddThh:mm:ss
          %STIME%             hh_mm_ss
          %CTIME%             Www Mmm dd hh:mm:ss yyyy
          %UTIME%             nnn (Unix time - seconds since 1/1/1970)
          %STATUS%            Status value from the last command executed
          %TSTATUS%           The text form of the last status value
          %SIM_VERIFY%        The Verify/Verbose mode of the current Do command file
          %SIM_VERBOSE%       The Verify/Verbose mode of the current Do command file
          %SIM_QUIET%         The Quiet mode of the current Do command file
          %SIM_MESSAGE%       The message display status of the current Do command file
   Environment variable lookups are done first with the precise name between 
3167
3168
3169
3170
3171
3172
3173


3174
3175
3176
3177
3178
3179
3180
3181







3182


3183

3184
3185
3186

3187
3188
3189



3190
3191
3192
3193
3194
3195
3196
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344

3345
3346
3347
3348
3349
3350

3351



3352
3353
3354
3355
3356
3357
3358
3359
3360
3361







+
+








+
+
+
+
+
+
+
-
+
+

+


-
+
-
-
-
+
+
+







void sim_sub_args (char *instr, size_t instr_size, char *do_arg[])
{
char gbuf[CBUFSIZE];
char *ip = instr, *op, *oend, *istart, *tmpbuf;
const char *ap;
char rbuf[CBUFSIZE];
int i;
size_t instr_off = 0;
size_t outstr_off = 0;
time_t now;
struct tm *tmnow;

time(&now);
tmnow = localtime(&now);
tmpbuf = (char *)malloc(instr_size);
op = tmpbuf;
oend = tmpbuf + instr_size - 2;
if (instr_size > sim_sub_instr_size) {
    sim_sub_instr = (char *)realloc (sim_sub_instr, instr_size*sizeof(*sim_sub_instr));
    sim_sub_instr_off = (size_t *)realloc (sim_sub_instr_off, instr_size*sizeof(*sim_sub_instr_off));
    sim_sub_instr_size = instr_size;
    }
sim_sub_instr_buf = instr;
strlcpy (sim_sub_instr, instr, instr_size*sizeof(*sim_sub_instr));
while (sim_isspace (*ip))                                   /* skip leading spaces */
while (sim_isspace (*ip)) {                                 /* skip leading spaces */
    sim_sub_instr_off[outstr_off++] = ip - instr;
    *op++ = *ip++;
    }
istart = ip;
for (; *ip && (op < oend); ) {
    if ((ip [0] == '\\') &&                             /* literal escape? */
    if ((ip [0] == '%') && (ip [1] == '%')) {           /* literal % insert? */
        ((ip [1] == '%') || (ip [1] == '\\'))) {        /*   and followed by '%' or '\'? */
        ip++;                                           /* skip '\' */
        *op++ = *ip++;                                  /* copy escaped char */
        sim_sub_instr_off[outstr_off++] = ip - instr;
        ip++;                                           /* skip one */
        *op++ = *ip++;                                  /* copy insert % */
        }
    else 
        if ((*ip == '%') && 
            (sim_isalnum(ip[1]) || (ip[1] == '*') || (ip[1] == '_'))) {/* sub? */
            if ((ip[1] >= '0') && (ip[1] <= ('9'))) {   /* %n = sub */
                ap = do_arg[ip[1] - '0'];
                for (i=0; i<ip[1] - '0'; ++i)           /* make sure we're not past the list end */
3242
3243
3244
3245
3246
3247
3248
3249

3250
3251
3252
3253
3254
3255
3256
3407
3408
3409
3410
3411
3412
3413

3414
3415
3416
3417
3418
3419
3420
3421







-
+







                        ap = rbuf;
                        }
                    else if (!strcmp ("DATETIME", gbuf)) {
                        sprintf (rbuf, "%04d-%02d-%02dT%02d:%02d:%02d", tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday, tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);
                        ap = rbuf;
                        }
                    /* Locale oriented formatted date/time info */
                    if (!strcmp ("LDATE", gbuf)) {
                    else if (!strcmp ("LDATE", gbuf)) {
                        strftime (rbuf, sizeof(rbuf), "%x", tmnow);
                        ap = rbuf;
                        }
                    else if (!strcmp ("LTIME", gbuf)) {
#if defined(HAVE_C99_STRFTIME)
                        strftime (rbuf, sizeof(rbuf), "%r", tmnow);
#else
3266
3267
3268
3269
3270
3271
3272




3273
3274
3275
3276
3277
3278
3279
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448







+
+
+
+







#if defined(HAVE_C99_STRFTIME)
                        strftime (rbuf, sizeof(rbuf), "%c", tmnow);
#else
                        strcpy (rbuf, ctime(&now));
                        rbuf[strlen (rbuf)-1] = '\0';    /* remove trailing \n */
#endif
                        ap = rbuf;
                        }
                    else if (!strcmp ("UTIME", gbuf)) {
                        sprintf (rbuf, "%" LL_FMT "d", (LL_TYPE)now);
                        ap = rbuf;
                        }
                    /* Separate Date/Time info */
                    else if (!strcmp ("DATE_YYYY", gbuf)) {/* Year (0000-9999) */
                        strftime (rbuf, sizeof(rbuf), "%Y", tmnow);
                        ap = rbuf;
                        }
                    else if (!strcmp ("DATE_YY", gbuf)) {/* Year (00-99) */
3381
3382
3383
3384
3385
3386
3387
3388


3389

3390
3391
3392
3393
3394
3395
3396

3397
3398
3399
3400


3401

3402
3403
3404


3405
3406


3407

3408
3409
3410
3411
3412
3413
3414
3550
3551
3552
3553
3554
3555
3556

3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571

3572
3573
3574
3575
3576
3577

3578
3579
3580

3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591







-
+
+

+







+



-
+
+

+


-
+
+

-
+
+

+







                    else if (!strcmp ("SIM_MESSAGE", gbuf)) {
                        sprintf (rbuf, "%s", sim_show_message ? "" : "-Q");
                        ap = rbuf;
                        }
                    }
                }
            if (ap) {                                   /* non-null arg? */
                while (*ap && (op < oend))              /* copy the argument */
                while (*ap && (op < oend)) {            /* copy the argument */
                    sim_sub_instr_off[outstr_off++] = ip - instr;
                    *op++ = *ap++;
                    }
                }
            }
        else
            if (ip == istart) {                         /* at beginning of input? */
                get_glyph (istart, gbuf, 0);            /* substitute initial token */
                ap = getenv(gbuf);                      /* if it is an environment variable name */
                if (!ap) {                              /* nope? */
                    sim_sub_instr_off[outstr_off++] = ip - instr;
                    *op++ = *ip++;                      /* press on with literal character */
                    continue;
                    }
                while (*ap && (op < oend))              /* copy the translation */
                while (*ap && (op < oend)) {            /* copy the translation */
                    sim_sub_instr_off[outstr_off++] = ip - instr;
                    *op++ = *ap++;
                    }
                ip += strlen(gbuf);
                }
            else
            else {
                sim_sub_instr_off[outstr_off++] = ip - instr;
                *op++ = *ip++;                          /* literal character */
    }
                }
    }
*op = 0;                                                /* term buffer */
sim_sub_instr_off[outstr_off] = 0;
strcpy (instr, tmpbuf);
free (tmpbuf);
return;
}

t_stat shift_args (char *do_arg[], size_t arg_count)
{
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481

3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494

3495




3496

3497
3498
3499
3500
3501
3502
3503
3649
3650
3651
3652
3653
3654
3655

3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677

3678
3679
3680
3681
3682
3683
3684
3685







-


+













+

+
+
+
+
-
+







*/
t_stat assert_cmd (int32 flag, CONST char *cptr)
{
char gbuf[CBUFSIZE], gbuf2[CBUFSIZE];
CONST char *tptr, *gptr;
REG *rptr;
uint32 idx;
t_value val;
t_stat r;
t_bool Not = FALSE;
t_bool Exist = FALSE;
t_bool result;
t_addr addr;
t_stat reason;

cptr = (CONST char *)get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, (CONST char *)cptr, &r);  
                                                        /* get sw, default */
sim_stabr.boolop = sim_staba.boolop = -1;               /* no relational op dflt */
if (*cptr == 0)                                         /* must be more */
    return SCPE_2FARG;
tptr = get_glyph (cptr, gbuf, 0);                       /* get token */
if (!strcmp (gbuf, "NOT")) {                            /* Conditional Inversion? */
    Not = TRUE;                                         /* remember that, and */
    cptr = (CONST char *)tptr;
    tptr = get_glyph (cptr, gbuf, 0);                   /* get next token */
    }
if (!strcmp (gbuf, "EXIST")) {                          /* File Exist Test? */
    Exist = TRUE;                                       /* remember that, and */
    cptr = (CONST char *)tptr;
    }
if (*cptr == '"') {                                     /* quoted string comparison? */
if (Exist || (*cptr == '"')) {                          /* quoted string comparison? */
    char op[CBUFSIZE];
    static struct {
        const char *op;
        int aval;
        int bval;
        t_bool invert;
        } *optr, compare_ops[] =
3519
3520
3521
3522
3523
3524
3525

3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548






























3549
3550
3551
3552
3553
3554
3555
3701
3702
3703
3704
3705
3706
3707
3708























3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745







+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    tptr = (CONST char *)get_glyph_gen (cptr, gbuf, '=', (sim_switches & SWMASK ('I')), TRUE, '\\');
                                                    /* get first string */
    if (!*tptr)
        return SCPE_2FARG;
    cptr += strlen (gbuf);
    while (sim_isspace (*cptr))                         /* skip spaces */
        ++cptr;
    if (!Exist) {
    get_glyph (cptr, op, '"');
    for (optr = compare_ops; optr->op; optr++)
        if (0 == strcmp (op, optr->op))
            break;
    if (!optr->op)
        return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op);
    cptr += strlen (op);
    while (sim_isspace (*cptr))                         /* skip spaces */
        ++cptr;
    cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\');
                                                        /* get second string */
    if (*cptr) {                                        /* more? */
        if (flag)                                       /* ASSERT has no more args */
            return SCPE_2MARG;
        }
    else {
        if (!flag)                                      
            return SCPE_2FARG;                          /* IF needs actions! */
        }
    result = sim_cmp_string (gbuf, gbuf2);
    result = ((result == optr->aval) || (result == optr->bval));
    if (optr->invert)
        result = !result;
        get_glyph (cptr, op, '"');
        for (optr = compare_ops; optr->op; optr++)
            if (0 == strcmp (op, optr->op))
                break;
        if (!optr->op)
            return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op);
        cptr += strlen (op);
        while (sim_isspace (*cptr))                         /* skip spaces */
            ++cptr;
        cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\');
                                                            /* get second string */
        if (*cptr) {                                        /* more? */
            if (flag)                                       /* ASSERT has no more args */
                return SCPE_2MARG;
            }
        else {
            if (!flag)                                      
                return SCPE_2FARG;                          /* IF needs actions! */
            }
        result = sim_cmp_string (gbuf, gbuf2);
        result = ((result == optr->aval) || (result == optr->bval));
        if (optr->invert)
            result = !result;
        }
    else {
        FILE *f = fopen (gbuf, "r");
        if (f)
            fclose (f);
        result = (f != NULL);
        }
    }
else {
    cptr = get_glyph (cptr, gbuf, 0);                   /* get register */
    rptr = find_reg (gbuf, &gptr, sim_dfdev);           /* parse register */
    if (rptr) {                                         /* got register? */
        if (*gptr == '[') {                             /* subscript? */
            if (rptr->depth <= 1)                       /* array register? */
3586
3587
3588
3589
3590
3591
3592
3593
3594


3595
3596
3597
3598
3599
3600
3601
3776
3777
3778
3779
3780
3781
3782


3783
3784
3785
3786
3787
3788
3789
3790
3791







-
-
+
+







        if (!flag)                                      
            return SCPE_2FARG;                          /* IF needs actions! */
        }
    if (rptr) {                                         /* Handle register case */
        if (!get_rsearch (gbuf, rptr->radix, &sim_stabr) ||  /* parse condition */
            (sim_stabr.boolop == -1))                   /* relational op reqd */
            return SCPE_MISVAL;
        val = get_rval (rptr, idx);                     /* get register value */
        result = test_search (&val, &sim_stabr);        /* test condition */
        sim_eval[0] = get_rval (rptr, idx);             /* get register value */
        result = test_search (sim_eval, &sim_stabr);    /* test condition */
        }
    else {                                              /* Handle memory case */
        if (!get_asearch (gbuf, sim_dfdev ? sim_dfdev->dradix : sim_dflt_dev->dradix, &sim_staba) ||  /* parse condition */
            (sim_staba.boolop == -1))                    /* relational op reqd */
            return SCPE_MISVAL;
        reason = get_aval (addr, sim_dfdev, sim_dfunit);/* get data */
        if (reason != SCPE_OK)                          /* return if error */
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627














3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647




































3648
3649
3650
3651
3652
3653
3654

3655
3656




3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671





3672
3673
3674
3675
3676
3677
3678



3679
3680
3681
3682
3683
3684
3685
3686

3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698












3699
3700
3701


3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3803
3804
3805
3806
3807
3808
3809








3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887


3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905

3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935






3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948


3949
3950



3951
3952
3953
3954
3955
3956
3957







-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
-
-
+
+
+
+














-
+
+
+
+
+







+
+
+








+






-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
-
-
-







return SCPE_OK;
}

/* Send command

   Syntax: SEND {After=m},{Delay=n},"string-to-send"

   After  - is a positive integer representing a number of instruction delay 
            before the initial characters is sent.  The value specified
            in a after argument persists across SEND commands.  The after
            parameter can be set by itself with SEND AFTER=n
   Delay  - is a positive integer representing a minimal instruction delay 
            before and between characters being sent.  The value specified
            in a delay argument persists across SEND commands.  The delay
            parameter can be set by itself with SEND DELAY=n
   After  - is an integer (>= 0) representing a number of instruction 
            delay before the initial characters is sent.  The after 
            parameter can is set by itself (with SEND AFTER=n). 
            The value specified then persists across SEND commands, 
            and is the default value used in subsequent SEND commands 
            which don't specify an explicit AFTER parameter.  This default
            value is visible externally via an environment variable.
   Delay  - is an integer (>= 0) representing a number of instruction 
            delay before and between characters being sent.  The
            delay parameter can is set by itself (with SEND DELAY=n) 
            The value specified persists across SEND commands, and is
            the default value used in subsequent SEND commands which
            don't specify an explicit DELAY parameter.  This default
            value is visible externally via an environment variable.
   String - must be quoted.  Quotes may be either single or double but the
            opening anc closing quote characters must match.  Within quotes 
            C style character escapes are allowed.  
            The following character escapes are explicitly supported:
        \r  Sends the ASCII Carriage Return character (Decimal value 13)
        \n  Sends the ASCII Linefeed character (Decimal value 10)
        \f  Sends the ASCII Formfeed character (Decimal value 12)
        \t  Sends the ASCII Horizontal Tab character (Decimal value 9)
        \v  Sends the ASCII Vertical Tab character (Decimal value 11)
        \b  Sends the ASCII Backspace character (Decimal value 8)
        \\  Sends the ASCII Backslash character (Decimal value 92)
        \'  Sends the ASCII Single Quote character (Decimal value 39)
        \"  Sends the ASCII Double Quote character (Decimal value 34)
        \?  Sends the ASCII Question Mark character (Decimal value 63)
        \e  Sends the ASCII Escape character (Decimal value 27)
     as well as octal character values of the form:
        \n{n{n}} where each n is an octal digit (0-7)
     and hext character values of the form:
        \xh{h} where each h is a hex digit (0-9A-Fa-f)
   */

static uint32 get_default_env_parameter (const char *dev_name, const char *param_name, uint32 default_value)
{
char varname[CBUFSIZE];
uint32 val;
char *endptr;
const char *colon = strchr (dev_name, ':');

if (colon)
    snprintf (varname, sizeof(varname), "%s_%*.*s_%s", param_name, (int)(colon-dev_name), (int)(colon-dev_name), dev_name, colon + 1);
else
    snprintf (varname, sizeof(varname), "%s_%s", param_name, dev_name);
if (!getenv (varname))
    val = default_value;
else {
    val = strtoul (getenv (varname), &endptr, 0);
    if (*endptr)
        val = default_value;
    }
return val;
}

static void set_default_env_parameter (const char *dev_name, const char *param_name, uint32 value)
{
char varname[CBUFSIZE];
char valbuf[CBUFSIZE];

const char *colon = strchr (dev_name, ':');

if (colon)
    snprintf (varname, sizeof(varname), "%s_%*.*s_%s", param_name, (int)(colon-dev_name), (int)(colon-dev_name), dev_name, colon + 1);
else
    snprintf (varname, sizeof(varname), "%s_%s", param_name, dev_name);
snprintf (valbuf, sizeof(valbuf), "%u", value);
setenv(varname, valbuf, 1);
}

t_stat send_cmd (int32 flag, CONST char *cptr)
{
char gbuf[CBUFSIZE];
CONST char *tptr;
uint8 dbuf[CBUFSIZE];
uint32 dsize = 0;
const char *dev_name;
uint32 delay = 0;
uint32 after = 0;
uint32 delay;
t_bool delay_set = FALSE;
uint32 after;
t_bool after_set = FALSE;
t_stat r;
SEND *snd;

GET_SWITCHES (cptr);                                    /* get switches */
tptr = get_glyph (cptr, gbuf, ',');
if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
    r = tmxr_locate_line_send (gbuf, &snd);
    if (r != SCPE_OK)
        return r;
    cptr = tptr;
    tptr = get_glyph (tptr, gbuf, ',');
    }
else
    snd = sim_cons_get_send ();

dev_name = tmxr_send_line_name (snd);
if (!flag)
    return sim_send_clear (snd);
delay = get_default_env_parameter (dev_name, "SIM_SEND_DELAY", SEND_DEFAULT_DELAY);
after = get_default_env_parameter (dev_name, "SIM_SEND_AFTER", delay);
while (*cptr) {
    if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) {
        delay = (uint32)get_uint (&gbuf[6], 10, 10000000, &r);
        if (r != SCPE_OK)
            return sim_messagef (SCPE_ARG, "Invalid Delay Value\n");
        cptr = tptr;
        tptr = get_glyph (cptr, gbuf, ',');
        delay_set = TRUE;
        if (!after_set)
            after = delay;
        continue;
        }
    if ((!strncmp(gbuf, "AFTER=", 6)) && (gbuf[6])) {
        after = (uint32)get_uint (&gbuf[6], 10, 10000000, &r);
        if (r != SCPE_OK)
            return sim_messagef (SCPE_ARG, "Invalid After Value\n");
        cptr = tptr;
        tptr = get_glyph (cptr, gbuf, ',');
        after_set = TRUE;
        continue;
        }
    if ((*cptr == '"') || (*cptr == '\''))
        break;
    return SCPE_ARG;
    }
if (*cptr) {
    if ((*cptr != '"') && (*cptr != '\''))
        return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
    cptr = get_glyph_quoted (cptr, gbuf, 0);
    if (*cptr != '\0')
        return SCPE_2MARG;                  /* No more arguments */
if (!*cptr) {
    if ((!delay_set) && (!after_set))
        return SCPE_2FARG;
    set_default_env_parameter (dev_name, "SIM_SEND_DELAY", delay);
    set_default_env_parameter (dev_name, "SIM_SEND_AFTER", after);
    return SCPE_OK;
    }
if ((*cptr != '"') && (*cptr != '\''))
    return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
cptr = get_glyph_quoted (cptr, gbuf, 0);
if (*cptr != '\0')
    return SCPE_2MARG;                  /* No more arguments */

    if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize))
        return sim_messagef (SCPE_ARG, "Invalid String\n");
if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize))
    return sim_messagef (SCPE_ARG, "Invalid String\n");
    }
if ((dsize == 0) && (delay == 0) && (after == 0))
    return SCPE_2FARG;
return sim_send_input (snd, dbuf, dsize, after, delay);
}

t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
char gbuf[CBUFSIZE];
CONST char *tptr;
3734
3735
3736
3737
3738
3739
3740
3741

3742
3743
3744
3745
3746
3747
3748
3980
3981
3982
3983
3984
3985
3986

3987
3988
3989
3990
3991
3992
3993
3994







-
+







EXPECT *exp;

GET_SWITCHES (cptr);                                    /* get switches */
tptr = get_glyph (cptr, gbuf, ',');
if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
    r = tmxr_locate_line_expect (gbuf, &exp);
    if (r != SCPE_OK)
        return r;
        return sim_messagef (r, "No such active line: %s\n", gbuf);
    cptr = tptr;
    }
else
    exp = sim_cons_get_expect ();
if (flag)
    return sim_set_expect (exp, cptr);
else
3771
3772
3773
3774
3775
3776
3777



















































3778
3779
3780
3781
3782
3783
3784
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







if (*tptr != '\0')
    return SCPE_2MARG;          /* No more arguments */
if (*cptr && (cptr[strlen(cptr)-1] != '"') && (cptr[strlen(cptr)-1] != '\''))
    return SCPE_ARG;            /* String must be quote delimited */
return sim_exp_show (st, exp, gbuf);
}


/* Sleep command */

t_stat sleep_cmd (int32 flag, CONST char *cptr)
{
char *tptr;
double wait;

stop_cpu = 0;
signal (SIGINT, int_handler);
while (*cptr) {
    wait = strtod (cptr, &tptr);
    switch (*tptr) {
        case ' ':
        case '\t':
        case '\0':
            break;
        case 's':
        case 'S':
            ++tptr;
            break;
        case 'm':
        case 'M':
            ++tptr;
            wait *= 60.0;
            break;
        case 'h':
        case 'H':
            ++tptr;
            wait *= (60.0*60.0);
            break;
        case 'd':
        case 'D':
            ++tptr;
            wait *= (24.0*60.0*60.0);
            break;
        default:
            signal (SIGINT, SIG_DFL);                               /* cancel WRU */
            return sim_messagef (SCPE_ARG, "Invalid Sleep unit '%c'\n", *cptr);
        }
    wait *= 1000.0;                             /* Convert to Milliseconds */
    cptr = tptr;
    while ((wait > 1000.0) && (!stop_cpu))
        wait -= sim_os_ms_sleep (1000);
    if ((wait > 0.0) && (!stop_cpu))
        sim_os_ms_sleep ((unsigned)wait);
    }
signal (SIGINT, SIG_DFL);                               /* cancel WRU */
stop_cpu = 0;
return SCPE_OK;
}

/* Goto command */

t_stat goto_cmd (int32 flag, CONST char *fcptr)
{
char cbuf[CBUFSIZE], gbuf[CBUFSIZE], gbuf1[CBUFSIZE];
const char *cptr;
4030
4031
4032
4033
4034
4035
4036
4037

4038
4039
4040































4041

4042
4043
4044
4045
4046
4047
4048
4327
4328
4329
4330
4331
4332
4333

4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368

4369
4370
4371
4372
4373
4374
4375
4376







-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







return SCPE_OK;
}

/* Set environment routine */

t_stat sim_set_environment (int32 flag, CONST char *cptr)
{
char varname[CBUFSIZE];
char varname[CBUFSIZE], prompt[CBUFSIZE], cbuf[CBUFSIZE];

if ((!cptr) || (*cptr == 0))                            /* now eol? */
    return SCPE_2FARG;
if (sim_switches & SWMASK ('P')) {
    CONST char *deflt = NULL;

    cptr = get_glyph_quoted (cptr, prompt, 0);          /* get prompt */
    if (prompt[0] == '\0')
        return sim_messagef (SCPE_2FARG, "Missing Prompt and Environment Variable Name\n");
    if ((prompt[0] == '"') || (prompt[0] == '\'')) {
        prompt[strlen (prompt) - 1] = '\0';
        memmove (prompt, prompt + 1, strlen (prompt));
        }
    deflt = get_glyph (cptr, varname, '=');             /* get environment variable name */
    if (deflt == NULL)
        deflt = "";
    if (*deflt) {
        strlcat (prompt, " [", sizeof (prompt));
        strlcat (prompt, deflt, sizeof (prompt));
        strlcat (prompt, "] ", sizeof (prompt));
        }
    else
        strlcat (prompt, " ", sizeof (prompt));
    if (sim_rem_cmd_active_line == -1) {
        cptr = read_line_p (prompt, cbuf, sizeof(cbuf), stdin);
        if ((cptr == NULL) || (*cptr == 0))
            cptr = deflt;
        else
            cptr = cbuf;
        }
    else
        cptr = deflt;
    }
else
cptr = get_glyph (cptr, varname, '=');                  /* get environment variable name */
    cptr = get_glyph (cptr, varname, '=');              /* get environment variable name */
setenv(varname, cptr, 1);
return SCPE_OK;
}

/* Set command */

t_stat set_cmd (int32 flag, CONST char *cptr)
4575
4576
4577
4578
4579
4580
4581

4582
4583
4584
4585
4586
4587
4588
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917







+







    fprintf (st, " delta %d", vdelt);
#if defined (SIM_VERSION_MODE)
fprintf (st, " %s", SIM_VERSION_MODE);
#endif
if (flag) {
    t_bool idle_capable;
    uint32 os_ms_sleep_1, os_tick_size;
    char os_type[128] = "Unknown";

    fprintf (st, "\n    Simulator Framework Capabilities:");
    fprintf (st, "\n        %s", sim_si64);
    fprintf (st, "\n        %s", sim_sa64);
    fprintf (st, "\n        %s", eth_capabilities());
    idle_capable = sim_timer_idle_capable (&os_ms_sleep_1, &os_tick_size);
    fprintf (st, "\n        Idle/Throttling support is %savailable", idle_capable ? "" : "NOT ");
4668
4669
4670
4671
4672
4673
4674


4675
4676
4677
4678
4679
4680
4681
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012







+
+







#if defined(__ia64)
            "I64";
#elif defined(__ALPHA)
            "Alpha";
#else
            "VAX";
#endif
        strlcpy (os_type, "OpenVMS ", sizeof (os_type));
        strlcat (os_type, arch, sizeof (os_type));
        fprintf (st, "\n        OS: OpenVMS %s %s", arch, __VMS_VERSION);
        }
#elif defined(_WIN32)
    if (1) {
        char *proc_id = getenv ("PROCESSOR_IDENTIFIER");
        char *arch = getenv ("PROCESSOR_ARCHITECTURE");
        char *procs = getenv ("NUMBER_OF_PROCESSORS");
4693
4694
4695
4696
4697
4698
4699

4700
4701
4702
4703
4704
4705
4706
4707

4708
4709

4710
4711
4712
4713
4714
4715








4716


4717



4718
4719
4720
4721
4722
4723
4724
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038

5039
5040

5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055

5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068







+







-
+

-
+






+
+
+
+
+
+
+
+
-
+
+

+
+
+







                sim_trim_endspc (osversion);
                } while (osversion[0] == '\0');
            _pclose (f);
            }
        fprintf (st, "\n        OS: %s", osversion);
        fprintf (st, "\n        Architecture: %s%s%s, Processors: %s", arch, proc_arch3264 ? " on " : "", proc_arch3264 ? proc_arch3264  : "", procs);
        fprintf (st, "\n        Processor Id: %s, Level: %s, Revision: %s", proc_id ? proc_id : "", proc_level ? proc_level : "", proc_rev ? proc_rev : "");
        strlcpy (os_type, "Windows", sizeof (os_type));
        }
#else
    if (1) {
        char osversion[2*PATH_MAX+1] = "";
        FILE *f;
        
        if ((f = popen ("uname -a", "r"))) {
            memset (osversion, 0, sizeof(osversion));
            memset (osversion, 0, sizeof (osversion));
            do {
                if (NULL == fgets (osversion, sizeof(osversion)-1, f))
                if (NULL == fgets (osversion, sizeof (osversion)-1, f))
                    break;
                sim_trim_endspc (osversion);
                } while (osversion[0] == '\0');
            pclose (f);
            }
        fprintf (st, "\n        OS: %s", osversion);
        if ((f = popen ("uname", "r"))) {
            memset (os_type, 0, sizeof (os_type));
            do {
                if (NULL == fgets (os_type, sizeof (os_type)-1, f))
                    break;
                sim_trim_endspc (os_type);
                } while (os_type[0] == '\0');
            pclose (f);
        }
            }
        }
#endif
    if ((!strcmp (os_type, "Unknown")) && (getenv ("OSTYPE")))
        strlcpy (os_type, getenv ("OSTYPE"), sizeof (os_type));
    setenv ("SIM_OSTYPE", os_type, 1);
    }
#if defined(SIM_GIT_COMMIT_ID)
#define S_xstr(a) S_str(a)
#define S_str(a) #a
fprintf (st, "%sgit commit id: %8.8s", flag ? "\n        " : "        ", S_xstr(SIM_GIT_COMMIT_ID));
#undef S_str
#undef S_xstr
4875
4876
4877
4878
4879
4880
4881
4882

4883
4884
4885
4886
4887
4888
4889
5219
5220
5221
5222
5223
5224
5225

5226
5227
5228
5229
5230
5231
5232
5233







-
+







}

t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
int32 any = 0;
DEBTAB *dep;

if (dptr->flags & DEV_DEBUG) {
if ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) {
    if (dptr->dctrl == 0)
        fputs ("Debugging disabled", st);
    else if (dptr->debflags == NULL)
        fputs ("Debugging enabled", st);
    else {
        uint32 dctrl = dptr->dctrl;

5036
5037
5038
5039
5040
5041
5042
5043

5044
5045
5046
5047
5048
5049
5050
5380
5381
5382
5383
5384
5385
5386

5387
5388
5389
5390
5391
5392
5393
5394







-
+







char gbuf[4*CBUFSIZE];

if (sim_is_running)
    return SCPE_INVREM;
if ((!cptr) || (*cptr == 0))
    return SCPE_2FARG;
gbuf[sizeof(gbuf)-1] = '\0';
strncpy (gbuf, cptr, sizeof(gbuf)-1);
strlcpy (gbuf, cptr, sizeof(gbuf));
sim_trim_endspc(gbuf);
if (chdir(gbuf) != 0)
    return sim_messagef(SCPE_IOERR, "Unable to directory change to: %s\n", gbuf);
return SCPE_OK;
}

t_stat pwd_cmd (int32 flg, CONST char *cptr)
5124
5125
5126
5127
5128
5129
5130
5131

5132
5133
5134
5135

5136
5137
5138

5139
5140

5141
5142
5143


5144
5145
5146
5147

5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168

5169
5170

5171
5172
5173
5174
5175

5176
5177
5178
5179
5180
5181
5182
5468
5469
5470
5471
5472
5473
5474

5475
5476
5477
5478

5479
5480
5481

5482
5483

5484
5485


5486
5487
5488
5489
5490

5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511

5512
5513

5514
5515
5516
5517
5518

5519
5520
5521
5522
5523
5524
5525
5526







-
+



-
+


-
+

-
+

-
-
+
+



-
+




















-
+

-
+




-
+







#endif
struct stat filestat;
char *c;
char DirName[PATH_MAX + 1], WholeName[PATH_MAX + 1], WildName[PATH_MAX + 1];

memset (DirName, 0, sizeof(DirName));
memset (WholeName, 0, sizeof(WholeName));
sim_strlcpy (WildName, cptr, sizeof(WildName));
strlcpy (WildName, cptr, sizeof(WildName));
cptr = WildName;
sim_trim_endspc (WildName);
if ((!stat (WildName, &filestat)) && (filestat.st_mode & S_IFDIR))
    sim_strlcat (WildName, "/*", sizeof(WildName));
    strlcat (WildName, "/*", sizeof (WildName));
if ((*cptr != '/') || (0 == memcmp (cptr, "./", 2)) || (0 == memcmp (cptr, "../", 3))) {
#if defined (VMS)
    getcwd (WholeName, sizeof(WholeName)-1, 0);
    getcwd (WholeName, sizeof (WholeName)-1, 0);
#else
    getcwd (WholeName, sizeof(WholeName)-1);
    getcwd (WholeName, sizeof (WholeName)-1);
#endif
    sim_strlcat (WholeName, "/", sizeof(WholeName));
    sim_strlcat (WholeName, cptr, sizeof(WholeName));
    strlcat (WholeName, "/", sizeof (WholeName));
    strlcat (WholeName, cptr, sizeof (WholeName));
    sim_trim_endspc (WholeName);
    }
else
    sim_strlcpy (WholeName, cptr, sizeof(WholeName));
    strlcpy (WholeName, cptr, sizeof (WholeName));
while ((c = strstr (WholeName, "/./")))
    memmove (c + 1, c + 3, 1 + strlen (c + 3));
while ((c = strstr (WholeName, "//")))
    memmove (c + 1, c + 2, 1 + strlen (c + 2));
while ((c = strstr (WholeName, "/../"))) {
    char *c1;
    c1 = c - 1;
    while ((c1 >= WholeName) && (*c1 != '/'))
        c1 = c1 - 1;
    memmove (c1, c + 3, 1 + strlen (c + 3));
    while (0 == memcmp (WholeName, "/../", 4))
        memmove (WholeName, WholeName+3, 1 + strlen (WholeName+3));
    }
c = strrchr (WholeName, '/');
if (c) {
    memmove (DirName, WholeName, 1+c-WholeName);
    DirName[1+c-WholeName] = '\0';
    }
else {
#if defined (VMS)
    getcwd (WholeName, sizeof(WholeName)-1, 0);
    getcwd (WholeName, sizeof (WholeName)-1, 0);
#else
    getcwd (WholeName, sizeof(WholeName)-1);
    getcwd (WholeName, sizeof (WholeName)-1);
#endif
    }
cptr = WholeName;
#if defined (HAVE_GLOB)
memset (&paths, 0, sizeof(paths));
memset (&paths, 0, sizeof (paths));
if (0 == glob (cptr, 0, NULL, &paths)) {
#else
dir = opendir(DirName[0] ? DirName : "/.");
if (dir) {
    struct dirent *ent;
#endif
    t_offset FileSize;
5332
5333
5334
5335
5336
5337
5338
5339

5340
5341
5342
5343
5344
5345
5346
5676
5677
5678
5679
5680
5681
5682

5683
5684
5685
5686
5687
5688
5689
5690







-
+







{
FILE *file;
char lbuf[4*CBUFSIZE];

if ((!cptr) || (*cptr == 0))
    return SCPE_2FARG;
lbuf[sizeof(lbuf)-1] = '\0';
strncpy (lbuf, cptr, sizeof(lbuf)-1);
strlcpy (lbuf, cptr, sizeof(lbuf));
sim_trim_endspc(lbuf);
file = sim_fopen (lbuf, "r");
if (file == NULL) {                         /* open failed? */
    TYPE_CTX type_state;
    t_stat stat;

    memset (&type_state, 0, sizeof (type_state));
5385
5386
5387
5388
5389
5390
5391
























































5392
5393
5394
5395
5396
5397
5398
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    return SCPE_2FARG;
memset (&del_state, 0, sizeof (del_state));
stat = sim_dir_scan (cptr, sim_delete_entry, &del_state);
if (stat == SCPE_OK)
    return del_state.stat;
return sim_messagef (SCPE_ARG, "No such file or directory: %s\n", cptr);
}

typedef struct {
    t_stat stat;
    int count;
    char destname[CBUFSIZE];
    } COPY_CTX;

static void sim_copy_entry (const char *directory, 
                            const char *filename,
                            t_offset FileSize,
                            const struct stat *filestat,
                            void *context)
{
COPY_CTX *ctx = (COPY_CTX *)context;
struct stat deststat;
char FullPath[PATH_MAX + 1];
char dname[CBUFSIZE];\
t_stat st;

strlcpy (dname, ctx->destname, sizeof (dname));

sprintf (FullPath, "%s%s", directory, filename);

if ((dname[strlen (dname) - 1] == '/') || (dname[strlen (dname) - 1] == '\\'))
    dname[strlen (dname) - 1] = '\0';
if ((!stat (dname, &deststat)) && (deststat.st_mode & S_IFDIR)) {
    const char *dslash = (strrchr (dname, '/') ? "/" : (strrchr (dname, '\\') ? "\\" : "/"));

    dname[sizeof (dname) - 1] = '\0';
    snprintf (&dname[strlen (dname)], sizeof (dname) - strlen (dname), "%s%s", dslash, filename);
    }
st = sim_copyfile (FullPath, dname, TRUE);
if (SCPE_OK == st)
    ++ctx->count;
else
    ctx->stat = st;
}

t_stat copy_cmd (int32 flg, CONST char *cptr)
{
char sname[CBUFSIZE];
COPY_CTX copy_state;
t_stat stat;

memset (&copy_state, 0, sizeof (copy_state));
if ((!cptr) || (*cptr == 0))
    return SCPE_2FARG;
cptr = get_glyph_quoted (cptr, sname, 0);
if ((!cptr) || (*cptr == 0))
    return SCPE_2FARG;
cptr = get_glyph_quoted (cptr, copy_state.destname, 0);
stat = sim_dir_scan (sname, sim_copy_entry, &copy_state);
if ((stat == SCPE_OK) && (copy_state.count))
    return sim_messagef (SCPE_OK, "      %3d file(s) copied\n", copy_state.count);
return copy_state.stat;
}

/* Breakpoint commands */

t_stat brk_cmd (int32 flg, CONST char *cptr)
{
GET_SWITCHES (cptr);                                    /* get switches */
return ssh_break (NULL, cptr, flg);                     /* call common code */
5413
5414
5415
5416
5417
5418
5419
5420

5421
5422

5423
5424

5425


5426

5427
5428
5429
5430
5431
5432
5433
5813
5814
5815
5816
5817
5818
5819

5820

5821
5822
5823

5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836







-
+
-

+

-
+

+
+

+







if (dptr == NULL)
    return SCPE_IERR;
uptr = dptr->units;
if (uptr == NULL)
    return SCPE_IERR;
max = uptr->capac - 1;
abuf[sizeof(abuf)-1] = '\0';
strncpy (abuf, cptr, sizeof(abuf)-1);
strlcpy (abuf, cptr, sizeof(abuf));
cptr = abuf;
if ((aptr = strchr (abuf, ';'))) {                      /* ;action? */
    cptr += aptr - abuf + 1;
    if (flg != SSH_ST)                                  /* only on SET */
        return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", aptr);
        return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", cptr);
    *aptr++ = 0;                                        /* separate strings */
    if ((cptr > sim_sub_instr_buf) && ((size_t)(cptr - sim_sub_instr_buf) < sim_sub_instr_size))
        aptr = &sim_sub_instr[sim_sub_instr_off[cptr - sim_sub_instr_buf]]; /* get un-substituted string */
    }
cptr = abuf;
if (*cptr == 0) {                                       /* no argument? */
    lo = (t_addr) get_rval (sim_PC, 0);                 /* use PC */
    return ssh_break_one (st, flg, lo, 0, aptr);
    }
while (*cptr) {
    cptr = get_glyph (cptr, gbuf, ',');
    tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);
5661
5662
5663
5664
5665
5666
5667
5668

5669
5670
5671
5672
5673
5674
5675
5676


5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697

5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708

5709
5710
5711
5712
5713
5714
5715
5716
5717

5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735

5736
5737
5738
5739
5740
5741
5742
5743
5744
5745

5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758

5759
5760
5761
5762
5763
5764
5765
5766
6064
6065
6066
6067
6068
6069
6070

6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
6092


6093
6094
6095
6096
6097
6098
6099

6100
6101
6102
6103
6104
6105
6106
6107
6108
6109


6110

6111
6112
6113
6114
6115
6116


6117

6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132


6133

6134
6135
6136
6137
6138
6139
6140


6141

6142
6143
6144
6145
6146
6147
6148
6149
6150
6151


6152

6153
6154
6155
6156
6157
6158
6159







-
+








+
+











-
-







-
+









-
-
+
-






-
-
+
-















-
-
+
-







-
-
+
-










-
-
+
-







        }
    else {
        if (!(uptr->dynflags & UNIT_ATTMULT))
            return SCPE_ALATT;                          /* Already attached */
        }
    }
gbuf[sizeof(gbuf)-1] = '\0';
strncpy (gbuf, cptr, sizeof(gbuf)-1);
strlcpy (gbuf, cptr, sizeof(gbuf));
sim_trim_endspc (gbuf);                                 /* trim trailing spc */
return scp_attach_unit (dptr, uptr, gbuf);              /* attach */
}

/* Call device-specific or file-oriented attach unit routine */

t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr)
{
if (uptr->flags & UNIT_DIS)                             /* disabled? */
    return SCPE_UDIS;
if (dptr->attach != NULL)                               /* device routine? */
    return dptr->attach (uptr, (CONST char *)cptr);     /* call it */
return attach_unit (uptr, (CONST char *)cptr);          /* no, std routine */
}

/* Attach unit to file */

t_stat attach_unit (UNIT *uptr, CONST char *cptr)
{
DEVICE *dptr;

if (uptr->flags & UNIT_DIS)                             /* disabled? */
    return SCPE_UDIS;
if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
    return SCPE_NOATT;
if ((dptr = find_dev_from_unit (uptr)) == NULL)
    return SCPE_NOATT;
uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */
if (uptr->filename == NULL)
    return SCPE_MEM;
strncpy (uptr->filename, cptr, CBUFSIZE);               /* save name */
strlcpy (uptr->filename, cptr, CBUFSIZE);               /* save name */
if ((sim_switches & SWMASK ('R')) ||                    /* read only? */
    ((uptr->flags & UNIT_RO) != 0)) {
    if (((uptr->flags & UNIT_ROABLE) == 0) &&           /* allowed? */
        ((uptr->flags & UNIT_RO) == 0))
        return attach_err (uptr, SCPE_NORO);            /* no, error */
    uptr->fileref = sim_fopen (cptr, "rb");             /* open rd only */
    if (uptr->fileref == NULL)                          /* open fail? */
        return attach_err (uptr, SCPE_OPENERR);         /* yes, error */
    uptr->flags = uptr->flags | UNIT_RO;                /* set rd only */
    if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
        sim_printf ("%s: unit is read only\n", sim_dname (dptr));
    sim_messagef (SCPE_OK, "%s: unit is read only\n", sim_dname (dptr));
        }
    }
else {
    if (sim_switches & SWMASK ('N')) {                  /* new file only? */
        uptr->fileref = sim_fopen (cptr, "wb+");        /* open new file */
        if (uptr->fileref == NULL)                      /* open fail? */
            return attach_err (uptr, SCPE_OPENERR);     /* yes, error */
        if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
            sim_printf ("%s: creating new file\n", sim_dname (dptr));
        sim_messagef (SCPE_OK, "%s: creating new file\n", sim_dname (dptr));
            }
        }
    else {                                              /* normal */
        uptr->fileref = sim_fopen (cptr, "rb+");        /* open r/w */
        if (uptr->fileref == NULL) {                    /* open fail? */
#if defined(EPERM)
            if ((errno == EROFS) || (errno == EACCES) || (errno == EPERM)) {/* read only? */
#else
            if ((errno == EROFS) || (errno == EACCES)) {/* read only? */
#endif
                if ((uptr->flags & UNIT_ROABLE) == 0)   /* allowed? */
                    return attach_err (uptr, SCPE_NORO);/* no error */
                uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
                if (uptr->fileref == NULL)              /* open fail? */
                    return attach_err (uptr, SCPE_OPENERR); /* yes, error */
                uptr->flags = uptr->flags | UNIT_RO;    /* set rd only */
                if (!sim_quiet) {
                    sim_printf ("%s: unit is read only\n", sim_dname (dptr));
                sim_messagef (SCPE_OK, "%s: unit is read only\n", sim_dname (dptr));
                    }
                }
            else {                                      /* doesn't exist */
                if (sim_switches & SWMASK ('E'))        /* must exist? */
                    return attach_err (uptr, SCPE_OPENERR); /* yes, error */
                uptr->fileref = sim_fopen (cptr, "wb+");/* open new file */
                if (uptr->fileref == NULL)              /* open fail? */
                    return attach_err (uptr, SCPE_OPENERR); /* yes, error */
                if (!sim_quiet) {
                    sim_printf ("%s: creating new file\n", sim_dname (dptr));
                sim_messagef (SCPE_OK, "%s: creating new file\n", sim_dname (dptr));
                    }
                }
            }                                           /* end if null */
        }                                               /* end else */
    }
if (uptr->flags & UNIT_BUFABLE) {                       /* buffer? */
    uint32 cap = ((uint32) uptr->capac) / dptr->aincr;  /* effective size */
    if (uptr->flags & UNIT_MUSTBUF)                     /* dyn alloc? */
        uptr->filebuf = calloc (cap, SZ_D (dptr));      /* allocate */
    if (uptr->filebuf == NULL)                          /* no buffer? */
        return attach_err (uptr, SCPE_MEM);             /* error */
    if (!sim_quiet) {
        sim_printf ("%s: buffering file in memory\n", sim_dname (dptr));
    sim_messagef (SCPE_OK, "%s: buffering file in memory\n", sim_dname (dptr));
        }
    uptr->hwmark = (uint32)sim_fread (uptr->filebuf,    /* read file */
        SZ_D (dptr), cap, uptr->fileref);
    uptr->flags = uptr->flags | UNIT_BUF;               /* set buffered */
    }
uptr->flags = uptr->flags | UNIT_ATT;
uptr->pos = 0;
return SCPE_OK;
5868
5869
5870
5871
5872
5873
5874
5875
5876

5877
5878
5879
5880
5881
5882
5883
5884
6261
6262
6263
6264
6265
6266
6267


6268

6269
6270
6271
6272
6273
6274
6275







-
-
+
-







        return SCPE_NOTATT;                             /* complain */
    }
if ((dptr = find_dev_from_unit (uptr)) == NULL)
    return SCPE_OK;
if ((uptr->flags & UNIT_BUF) && (uptr->filebuf)) {
    uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
    if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {
        if (!sim_quiet) {
            sim_printf ("%s: writing buffer to file\n", sim_dname (dptr));
        sim_messagef (SCPE_OK, "%s: writing buffer to file\n", sim_dname (dptr));
            }
        rewind (uptr->fileref);
        sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
        if (ferror (uptr->fileref))
            sim_printf ("%s: I/O error - %s", sim_dname (dptr), strerror (errno));
        }
    if (uptr->flags & UNIT_MUSTBUF) {                   /* dyn alloc? */
        free (uptr->filebuf);                           /* free buf */
6005
6006
6007
6008
6009
6010
6011
6012

6013
6014
6015
6016
6017
6018
6019
6396
6397
6398
6399
6400
6401
6402

6403
6404
6405
6406
6407
6408
6409
6410







-
+







t_stat r;
char gbuf[4*CBUFSIZE];

GET_SWITCHES (cptr);                                    /* get switches */
if (*cptr == 0)                                         /* must be more */
    return SCPE_2FARG;
gbuf[sizeof(gbuf)-1] = '\0';
strncpy (gbuf, cptr, sizeof(gbuf)-1);
strlcpy (gbuf, cptr, sizeof(gbuf));
sim_trim_endspc (gbuf);
if ((sfile = sim_fopen (gbuf, "wb")) == NULL)
    return SCPE_OPENERR;
r = sim_save (sfile);
fclose (sfile);
return r;
}
6160
6161
6162
6163
6164
6165
6166
6167

6168
6169
6170
6171
6172
6173
6174
6551
6552
6553
6554
6555
6556
6557

6558
6559
6560
6561
6562
6563
6564
6565







-
+







t_stat r;
char gbuf[4*CBUFSIZE];

GET_SWITCHES (cptr);                                    /* get switches */
if (*cptr == 0)                                         /* must be more */
    return SCPE_2FARG;
gbuf[sizeof(gbuf)-1] = '\0';
strncpy (gbuf, cptr, sizeof(gbuf)-1);
strlcpy (gbuf, cptr, sizeof(gbuf));
sim_trim_endspc (gbuf);
if ((rfile = sim_fopen (gbuf, "rb")) == NULL)
    return SCPE_OPENERR;
r = sim_rest (rfile);
fclose (rfile);
return r;
}
6489
6490
6491
6492
6493
6494
6495

6496
6497
6498
6499
6500
6501
6502
6880
6881
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891
6892
6893
6894







+







                    sim_printf (", now attached to '%s'\n", attunits[j]->filename);
                else
                    sim_printf (", now unattached\n");
                }
            }
        }
    free (attnames[j]);
    attnames[j] = NULL;
    }
Cleanup_Return:
for (j=0; j < attcnt; j++)
    free (attnames[j]);
free (attnames);
free (attunits);
free (attswitches);
6560
6561
6562
6563
6564
6565
6566
6567


6568
6569
6570
6571
6572
6573
6574
6952
6953
6954
6955
6956
6957
6958

6959
6960
6961
6962
6963
6964
6965
6966
6967







-
+
+







        if (MATCH_CMD (gbuf, "UNTIL") != 0)
            cptr = get_glyph (cptr, gbuf, 0);           /* get next glyph */
        if (MATCH_CMD (gbuf, "UNTIL") != 0)
            return sim_messagef (SCPE_2MARG, "Unexpected %s command argument: %s %s\n", 
                                             (flag == RU_RUN) ? "RUN" : "GO", gbuf, cptr);
        sim_switches = 0;
        GET_SWITCHES (cptr);
        if ((*cptr == '\'') || (*cptr == '"')) {        /* Expect UNTIL condition */
        if (((*cptr == '\'') || (*cptr == '"')) ||      /* Expect UNTIL condition */
            (!sim_strncasecmp(cptr, "HALTAFTER=", 10))) {
            r = expect_cmd (1, cptr);
            if (r != SCPE_OK)
                return r;
            }
        else {                                          /* BREAK UNTIL condition */
            if (sim_switches == 0)
                sim_switches = sim_brk_dflt;
6665
6666
6667
6668
6669
6670
6671
6672

6673
6674
6675

6676
6677
6678
6679
6680

6681
6682
6683
6684
6685

6686
6687
6688
6689
6690
6691

6692
6693
6694
6695
6696
6697

6698
6699
6700
6701
6702
6703
6704
7058
7059
7060
7061
7062
7063
7064

7065
7066
7067

7068
7069
7070
7071
7072

7073
7074
7075
7076
7077

7078
7079
7080
7081
7082
7083

7084
7085
7086
7087
7088
7089

7090
7091
7092
7093
7094
7095
7096
7097







-
+


-
+




-
+




-
+





-
+





-
+







        if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ))
            if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET))
                return sim_messagef (SCPE_IERR, "Can't seek to %u in %s for %s\n", (unsigned)uptr->pos, uptr->filename, sim_uname (uptr));
        }
    }
stop_cpu = 0;
sim_is_running = 1;                                     /* flag running */
if (sim_ttrun () != SCPE_OK) {                          /* set console mode */
if ((r = sim_ttrun ()) != SCPE_OK) {                          /* set console mode */
    sim_is_running = 0;                                 /* flag idle */
    sim_ttcmd ();
    return SCPE_TTYERR;
    return sim_messagef (SCPE_TTYERR, "sim_ttrun() returned: %s\n", sim_error_text (r));
    }
if ((r = sim_check_console (30)) != SCPE_OK) {          /* check console, error? */
    sim_is_running = 0;                                 /* flag idle */
    sim_ttcmd ();
    return r;
    sim_messagef (r, "sim_check_console () returned: %s\n", sim_error_text (r));
    }
if (signal (SIGINT, int_handler) == SIG_ERR) {          /* set WRU */
    sim_is_running = 0;                                 /* flag idle */
    sim_ttcmd ();
    return SCPE_SIGERR;
    return sim_messagef (SCPE_SIGERR, "Can't establish SIGINT");
    }
#ifdef SIGHUP
if (signal (SIGHUP, int_handler) == SIG_ERR) {          /* set WRU */
    sim_is_running = 0;                                 /* flag idle */
    sim_ttcmd ();
    return SCPE_SIGERR;
    return sim_messagef (SCPE_SIGERR, "Can't establish SIGHUP");
    }
#endif
if (signal (SIGTERM, int_handler) == SIG_ERR) {         /* set WRU */
    sim_is_running = 0;                                 /* flag idle */
    sim_ttcmd ();
    return SCPE_SIGERR;
    return sim_messagef (SCPE_SIGERR, "Can't establish SIGTERM");
    }
if (sim_step)                                           /* set step timer */
    sim_activate (&sim_step_unit, sim_step);
fflush(stdout);                                         /* flush stdout */
if (sim_log)                                            /* flush log if enabled */
    fflush (sim_log);
sim_throt_sched ();                                     /* set throttle */
7009
7010
7011
7012
7013
7014
7015
7016

7017
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031

7032
7033
7034
7035
7036
7037
7038
7402
7403
7404
7405
7406
7407
7408

7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423

7424
7425
7426
7427
7428
7429
7430
7431







-
+














-
+







                    return SCPE_ARG;
                }
            }
        if (*tptr && (*tptr++ != ','))
            return SCPE_ARG;
        reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
            lowr, highr, (uint32) low, (uint32) high);
        if ((!sim_oline) && (sim_log && (ofile == stdout)))
        if ((flag & EX_E) && (!sim_oline) && (sim_log && (ofile == stdout)))
            exdep_reg_loop (sim_log, sim_schrptr, EX_E, cptr,
                lowr, highr, (uint32) low, (uint32) high);
        continue;
        }

    tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,
        (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:
        sim_dfunit->capac - sim_dfdev->aincr), 0);
    if (tptr == NULL)
        return (tstat ? tstat : SCPE_ARG);
    if (*tptr && (*tptr++ != ','))
        return SCPE_ARG;
    reason = exdep_addr_loop (ofile, sim_schaptr, flag, cptr, low, high,
        sim_dfdev, sim_dfunit);
    if ((!sim_oline) && (sim_log && (ofile == stdout)))
    if ((flag & EX_E) && (!sim_oline) && (sim_log && (ofile == stdout)))
        exdep_addr_loop (sim_log, sim_schaptr, EX_E, cptr, low, high,
            sim_dfdev, sim_dfunit);
    }                                                   /* end for */
if (sim_ofile)                                          /* close output file */
    fclose (sim_ofile);
return reason;
}
7060
7061
7062
7063
7064
7065
7066
7067

7068
7069

7070
7071
7072
7073
7074
7075
7076
7453
7454
7455
7456
7457
7458
7459

7460
7461

7462
7463
7464
7465
7466
7467
7468
7469







-
+

-
+







    if ((sim_switches & SIM_SW_HIDE) &&
        (rptr->flags & REG_HIDDEN))
        continue;
    val = last_val = 0;
    for (idx = lows; idx <= highs; idx++) {
        if (idx >= rptr->depth)
            return SCPE_SUB;
        val = get_rval (rptr, idx);
        sim_eval[0] = val = get_rval (rptr, idx);
        sim_switches = saved_switches;
        if (schptr && !test_search (&val, schptr))
        if (schptr && !test_search (sim_eval, schptr))
            continue;
        if (flag == EX_E) {
            if ((idx > lows) && (val == last_val))
                continue;
            if (idx > val_start+1) {
                if (idx-1 == val_start+1) {
                    reason = ex_reg (ofile, val, flag, rptr, idx-1);
7177
7178
7179
7180
7181
7182
7183

7184
7185
7186
7187
7188

7189
7190
7191
7192
7193
7194
7195
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581

7582
7583
7584
7585
7586
7587
7588
7589







+




-
+







    return SCPE_IERR;
if (rptr->depth > 1)
    fprintf (ofile, "%s[%d]:\t", rptr->name, idx);
else
    fprintf (ofile, "%s:\t", rptr->name);
if (!(flag & EX_E))
    return SCPE_OK;
sim_eval[0] = val;
GET_RADIX (rdx, rptr->radix);
if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr)
    sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);
else if (!(rptr->flags & REG_VMFLAGS) ||
    (fprint_sym (ofile, (rptr->flags & REG_UFMASK) | rdx, &val,
    (fprint_sym (ofile, (rptr->flags & REG_UFMASK) | rdx, sim_eval,
                 NULL, sim_switches | SIM_SW_REG) > 0)) {
        fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);
        if (rptr->fields) {
            fprintf (ofile, "\t");
            fprint_fields (ofile, val, val, rptr->fields);
            }
        }
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7774
7775
7776
7777
7778
7779
7780

7781
7782
7783
7784
7785
7786
7787







-







#if defined (USE_INT64)
else if (sz <= sizeof (uint32))
    PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
else PUT_RVAL (t_uint64, rptr, idx, val, mask);
#else
else PUT_RVAL (uint32, rptr, idx, val, mask);
#endif
return;
}

/* Examine address routine

   Inputs: (sim_eval is an implicit argument)
        ofile   =       output stream
        flag    =       type of ex/mod command (ex, iex, idep)
7662
7663
7664
7665
7666
7667
7668


7669
7670
7671
7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683

7684
7685
7686
7687
7688
7689
7690
8055
8056
8057
8058
8059
8060
8061
8062
8063
8064
8065
8066
8067
8068
8069
8070
8071
8072
8073
8074
8075
8076
8077

8078
8079
8080
8081
8082
8083
8084
8085







+
+














-
+








#define S__STR_QUOTE(tok) #tok
#define S__STR(tok) S__STR_QUOTE(tok)
    handle = dlopen("libncurses." S__STR(HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL);
    handle = dlopen("libcurses." S__STR(HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL);
    handle = dlopen("libreadline." S__STR(HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL);
    if (!handle)
        handle = dlopen("libreadline." S__STR(HAVE_DLOPEN) ".7", RTLD_NOW|RTLD_GLOBAL);
    if (!handle)
        handle = dlopen("libreadline." S__STR(HAVE_DLOPEN) ".6", RTLD_NOW|RTLD_GLOBAL);
    if (!handle)
        handle = dlopen("libreadline." S__STR(HAVE_DLOPEN) ".5", RTLD_NOW|RTLD_GLOBAL);
    if (handle) {
        p_readline = (readline_func)((size_t)dlsym(handle, "readline"));
        p_add_history = (add_history_func)((size_t)dlsym(handle, "add_history"));
        }
    }
if (prompt) {                                           /* interactive? */
    if (p_readline) {
        char *tmpc = p_readline (prompt);               /* get cmd line */
        if (tmpc == NULL)                               /* bad result? */
            cptr = NULL;
        else {
            strncpy (cptr, tmpc, size);                 /* copy result */
            strlcpy (cptr, tmpc, size);                 /* copy result */
            free (tmpc) ;                               /* free temp */
            }
        }
    else {
        printf ("%s", prompt);                          /* display prompt */
        cptr = fgets (cptr, size, stream);              /* get cmd line */
        }
7767
7768
7769
7770
7771
7772
7773
7774

7775
7776
7777
7778
7779
7780
7781
8162
8163
8164
8165
8166
8167
8168

8169
8170
8171
8172
8173
8174
8175
8176







-
+







            if ((*iptr == '"') || (*iptr == '\'')) {
                quoting = TRUE;
                quote_char = *iptr;
                }
            }
        }
    if (sim_islower (*iptr) && uc)
        *optr = (char)toupper (*iptr);
        *optr = (char)sim_toupper (*iptr);
    else *optr = *iptr;
    iptr++; optr++;
    }
*optr = 0;
if (mchar && (*iptr == mchar))                          /* skip terminator */
    iptr++;
while (sim_isspace (*iptr))                             /* absorb spaces */
7822
7823
7824
7825
7826
7827
7828
7829

7830
7831

7832
7833
7834

7835
7836

7837
7838
7839

7840


7841




7842
7843
7844
7845
7846













7847
7848
7849

7850
7851

7852
7853
7854

7855
7856

7857
7858
7859

7860
7861

7862
7863
7864
7865
7866
7867
7868
7869
7870
7871
7872
7873
7874

7875
7876

7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897

7898
7899

7900
7901
7902
7903
7904
7905
7906
8217
8218
8219
8220
8221
8222
8223

8224
8225

8226
8227
8228

8229
8230

8231
8232
8233

8234
8235
8236
8237

8238
8239
8240
8241
8242
8243



8244
8245
8246
8247
8248
8249
8250
8251
8252
8253
8254
8255
8256
8257
8258

8259
8260

8261
8262
8263

8264
8265

8266
8267
8268

8269
8270

8271
8272
8273
8274
8275
8276
8277
8278
8279
8280
8281
8282


8283


8284
8285
8286
8287
8288
8289
8290
8291
8292
8293
8294
8295
8296
8297
8298
8299
8300
8301
8302
8303


8304


8305
8306
8307
8308
8309
8310
8311
8312







-
+

-
+


-
+

-
+


-
+

+
+
-
+
+
+
+


-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+

-
+


-
+

-
+


-
+

-
+











-
-
+
-
-
+



















-
-
+
-
-
+








tptr = cptr + strlen (cptr);
while ((--tptr >= cptr) && sim_isspace (*tptr))
    *tptr = 0;
return cptr;
}

int sim_isspace (char c)
int sim_isspace (int c)
{
return (c & 0x80) ? 0 : isspace (c);
return ((c < 0) || (c >= 128)) ? 0 : isspace (c);
}

int sim_islower (char c)
int sim_islower (int c)
{
return (c & 0x80) ? 0 : islower (c);
return (c >= 'a') && (c <= 'z');
}

int sim_isalpha (char c)
int sim_isupper (int c)
{
return (c >= 'A') && (c <= 'Z');
}
return (c & 0x80) ? 0 : isalpha (c);

int sim_toupper (int c)
{
return ((c >= 'a') && (c <= 'z')) ? ((c - 'a') + 'A') : c;
}

int sim_isprint (char c)
{
return (c & 0x80) ? 0 : isprint (c);
int sim_tolower (int c)
{
return ((c >= 'A') && (c <= 'Z')) ? ((c - 'A') + 'a') : c;
}

int sim_isalpha (int c)
{
return ((c < 0) || (c >= 128)) ? 0 : isalpha (c);
}

int sim_isprint (int c)
{
return ((c < 0) || (c >= 128)) ? 0 : isprint (c);
}

int sim_isdigit (char c)
int sim_isdigit (int c)
{
return (c & 0x80) ? 0 : isdigit (c);
return ((c < 0) || (c >= 128)) ? 0 : isdigit (c);
}

int sim_isgraph (char c)
int sim_isgraph (int c)
{
return (c & 0x80) ? 0 : isgraph (c);
return ((c < 0) || (c >= 128)) ? 0 : isgraph (c);
}

int sim_isalnum (char c)
int sim_isalnum (int c)
{
return (c & 0x80) ? 0 : isalnum (c);
return ((c < 0) || (c >= 128)) ? 0 : isalnum (c);
}

/* strncasecmp() is not available on all platforms */
int sim_strncasecmp (const char* string1, const char* string2, size_t len)
{
size_t i;
unsigned char s1, s2;

for (i=0; i<len; i++) {
    s1 = (unsigned char)string1[i];
    s2 = (unsigned char)string2[i];
    if (sim_islower (s1))
        s1 = (unsigned char)toupper (s1);
    s1 = (unsigned char)sim_toupper (s1);
    if (sim_islower (s2))
        s2 = (unsigned char)toupper (s2);
    s2 = (unsigned char)sim_toupper (s2);
    if (s1 < s2)
        return -1;
    if (s1 > s2)
        return 1;
    if (s1 == 0)
        return 0;
    }
return 0;
}

/* strcasecmp() is not available on all platforms */
int sim_strcasecmp (const char *string1, const char *string2)
{
size_t i = 0;
unsigned char s1, s2;

while (1) {
    s1 = (unsigned char)string1[i];
    s2 = (unsigned char)string2[i];
    if (sim_islower (s1))
        s1 = (unsigned char)toupper (s1);
    s1 = (unsigned char)sim_toupper (s1);
    if (sim_islower (s2))
        s2 = (unsigned char)toupper (s2);
    s2 = (unsigned char)sim_toupper (s2);
    if (s1 == s2) {
        if (s1 == 0)
            return 0;
        i++;
        continue;
        }
    if (s1 < s2)
8205
8206
8207
8208
8209
8210
8211
8212

8213
8214
8215
8216
8217

8218
8219
8220
8221
8222
8223
8224
8611
8612
8613
8614
8615
8616
8617

8618
8619
8620
8621
8622

8623
8624
8625
8626
8627
8628
8629
8630







-
+




-
+







        case 'x':
            if (1) {
                static const char *hex_digits = "0123456789ABCDEF";
                const char *c;

                ++iptr;
                *optr = 0;
                c = strchr (hex_digits, toupper(*iptr));
                c = strchr (hex_digits, sim_toupper(*iptr));
                if (c) {
                    *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
                    ++iptr;
                    }
                c = strchr (hex_digits, toupper(*iptr));
                c = strchr (hex_digits, sim_toupper(*iptr));
                if (c) {
                    *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
                    ++iptr;
                    }
                ++optr;
                }
            break;
8568
8569
8570
8571
8572
8573
8574
8575
8576





8577
8578
8579

8580
8581
8582

8583
8584
8585












8586
8587
8588
8589


8590
8591

8592
8593
8594
8595
8596
8597
8598
8599
8600
8601
8602
8603
8604
8605

8606
8607
8608
8609
8610
8611
8612
8613










8614
8615
8616
8617
8618
8619
8620
8621
8622
8623
8624
8625
8626
8627
8628
8629
8630

8631
8632
8633
8634
8635
8636

8637
8638
8639
8640
8641
8642
8643
8974
8975
8976
8977
8978
8979
8980


8981
8982
8983
8984
8985
8986
8987

8988
8989


8990
8991


8992
8993
8994
8995
8996
8997
8998
8999
9000
9001
9002
9003
9004
9005


9006
9007
9008

9009
9010
9011
9012
9013
9014
9015
9016
9017
9018
9019
9020
9021
9022

9023
9024
9025
9026
9027




9028
9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041
9042
9043
9044
9045
9046
9047
9048
9049
9050
9051
9052
9053

9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
9068







-
-
+
+
+
+
+


-
+

-
-
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+

-
+













-
+




-
-
-
-
+
+
+
+
+
+
+
+
+
+
















-
+






+







}

/* get_switches         get switches from input string

   Inputs:
        cptr    =       pointer to input string
   Outputs:
        sw      =       switch bit mask
                        0 if no switches, -1 if error
        *sw      =       switch bit mask
        *mumber  =       numeric value
   Return value:        SW_ERROR     if error
                        SW_BITMASK   if switch bitmask or not a switch
                        SW_NUMBER    if numeric
*/

int32 get_switches (const char *cptr)
SWITCH_PARSE get_switches (const char *cptr, int32 *sw, int32 *number)
{
int32 sw;

*sw = 0;
if (*cptr != '-')
    return 0;
sw = 0;
    return SW_BITMASK;
if (number)
    *number = 0;
if (sim_isdigit(cptr[1])) {
    char *end;
    long val = strtol (1+cptr, &end, 10);

    if ((*end != 0) || (number == NULL))
        return SW_ERROR;
    *number = (int32)val;
    return SW_NUMBER;
    }
for (cptr++; (sim_isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
    if (sim_isalpha (*cptr) == 0)
        return -1;
    sw = sw | SWMASK (toupper (*cptr));
        return SW_ERROR;
    *sw = *sw | SWMASK (sim_toupper (*cptr));
    }
return sw;
return SW_BITMASK;
}

/* get_sim_sw           accumulate sim_switches

   Inputs:
        cptr    =       pointer to input string
   Outputs:
        ptr     =       pointer to first non-string glyph
                        NULL if error
*/

CONST char *get_sim_sw (CONST char *cptr)
{
int32 lsw;
int32 lsw, lnum;
char gbuf[CBUFSIZE];

while (*cptr == '-') {                                  /* while switches */
    cptr = get_glyph (cptr, gbuf, 0);                   /* get switch glyph */
    lsw = get_switches (gbuf);                          /* parse */
    if (lsw <= 0)                                       /* invalid? */
        return NULL;
    sim_switches = sim_switches | lsw;                  /* accumulate */
    switch (get_switches (gbuf, &lsw, &lnum)) {         /* parse */
        case SW_ERROR:
            return NULL;
        case SW_BITMASK:
            sim_switches = sim_switches | lsw;          /* accumulate */
            break;
        case SW_NUMBER:
            sim_switch_number = lnum;                   /* set number */
            break;
        }
    }
return cptr;
}

/* get_sim_opt          get simulator command options

   Inputs:
        opt     =       command options
        cptr    =       pointer to input string
   Outputs:
        ptr     =       pointer to next glypsh, NULL if error
        *stat   =       error status
*/

CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st)
{
int32 t;
int32 t, n;
char gbuf[CBUFSIZE];
CONST char *svptr;
DEVICE *tdptr;
UNIT *tuptr;

sim_switches = 0;                                       /* no switches */
sim_switch_number = 0;                                  /* no numberuc switch */
sim_ofile = NULL;                                       /* no output file */
sim_schrptr = NULL;                                     /* no search */
sim_schaptr = NULL;                                     /* no search */
sim_stabr.logic = sim_staba.logic = SCH_OR;             /* default search params */
sim_stabr.boolop = sim_staba.boolop = SCH_GE;
sim_stabr.count = 1;
sim_stabr.mask = (t_value *)realloc (sim_stabr.mask, sim_emax * sizeof(*sim_stabr.mask));
8667
8668
8669
8670
8671
8672
8673
8674
8675


8676
8677







8678

8679

8680
8681

8682
8683
8684
8685
8686
8687
8688
9092
9093
9094
9095
9096
9097
9098


9099
9100
9101
9102
9103
9104
9105
9106
9107
9108
9109

9110

9111
9112

9113
9114
9115
9116
9117
9118
9119
9120







-
-
+
+


+
+
+
+
+
+
+
-
+
-
+

-
+







            *st = SCPE_OPENERR;
            return NULL;
            }
        sim_opt_out |= CMD_OPT_OF;                      /* got output file */
        continue;
        }
    cptr = get_glyph (cptr, gbuf, 0);
    if ((t = get_switches (gbuf)) != 0) {               /* try for switches */
        if (t < 0) {                                    /* err if bad switch */
    switch (get_switches (gbuf, &t, &n)) {      /* try for switches */
        case SW_ERROR:                          /* err if bad switch */
            *st = SCPE_INVSW;
            return NULL;
        case SW_NUMBER:
            sim_switch_number = n;              /* set number */
            continue;
        case SW_BITMASK:
            if (t != 0) {
                sim_switches = sim_switches | t;/* or in new switches */
                continue;
            }
                }
        sim_switches = sim_switches | t;                /* or in new switches */
            break;
        }
    else if ((opt & CMD_OPT_SCH) &&                     /* if allowed, */
    if ((opt & CMD_OPT_SCH) &&                     /* if allowed, */
        get_rsearch (gbuf, sim_dfdev->dradix, &sim_stabr)) { /* try for search */
        sim_schrptr = &sim_stabr;                       /* set search */
        sim_schaptr = get_asearch (gbuf, sim_dfdev->dradix, &sim_staba);/* populate memory version of the same expression */
        sim_opt_out |= CMD_OPT_SCH;                     /* got search */
        }
    else if ((opt & CMD_OPT_DFT) &&                     /* default allowed? */
        ((sim_opt_out & CMD_OPT_DFT) == 0) &&           /* none yet? */
8746
8747
8748
8749
8750
8751
8752
8753

8754
8755
8756
8757
8758
8759
8760
9178
9179
9180
9181
9182
9183
9184

9185
9186
9187
9188
9189
9190
9191
9192







-
+







    for (fptr = pptr + 1, eptr = ext;                   /* match characters */
#if defined (VMS)                                       /* VMS: stop at ; or null */
    (*fptr != 0) && (*fptr != ';');
#else
    *fptr != 0;                                         /* others: stop at null */
#endif
    fptr++, eptr++) {
        if (toupper (*fptr) != toupper (*eptr))
        if (sim_toupper (*fptr) != sim_toupper (*eptr))
            return NULL;
        }
    if (*eptr != 0)                                     /* ext exhausted? */
        return NULL;
    }
return pptr;
}
9011
9012
9013
9014
9015
9016
9017
9018
9019

9020
9021
9022
9023
9024
9025
9026
9443
9444
9445
9446
9447
9448
9449


9450
9451
9452
9453
9454
9455
9456
9457







-
-
+







if ((radix < 2) || (radix > 36))
    return 0;
while (sim_isspace (*inptr))                            /* bypass white space */
    inptr++;
val = 0;
nodigit = 1;
for (c = *inptr; sim_isalnum(c); c = *++inptr) {        /* loop through char */
    if (sim_islower (c))
        c = toupper (c);
    c = sim_toupper (c);
    if (sim_isdigit (c))                                /* digit? */
        digit = c - (uint32) '0';
    else if (radix <= 10)                               /* stop if not expected */
        break;
    else digit = c + 10 - (uint32) 'A';                 /* convert letter */
    if (digit >= radix)                                 /* valid in radix? */
        return 0;
9815
9816
9817
9818
9819
9820
9821
9822

9823
9824
9825
9826
9827
9828
9829
10246
10247
10248
10249
10250
10251
10252

10253
10254
10255
10256
10257
10258
10259
10260







-
+







    free (bp->act);                                     /* deallocate */
    bp->act = NULL;                                     /* now no action */
    }
if ((act != NULL) && (*act != 0)) {                     /* new action? */
    char *newp = (char *) calloc (CBUFSIZE+1, sizeof (char)); /* alloc buf */
    if (newp == NULL)                                   /* mem err? */
        return SCPE_MEM;
    strncpy (newp, act, CBUFSIZE);                      /* copy action */
    strlcpy (newp, act, CBUFSIZE);                      /* copy action */
    bp->act = newp;                                     /* set pointer */
    }
sim_brk_summ = sim_brk_summ | (sw & ~BRK_TYP_TEMP);
return SCPE_OK;
}

/* Clear a breakpoint */
10043
10044
10045
10046
10047
10048
10049
10050

10051
10052
10053
10054
10055
10056
10057
10474
10475
10476
10477
10478
10479
10480

10481
10482
10483
10484
10485
10486
10487
10488







-
+







if ((ep = strchr (sim_brk_act[sim_do_depth], ';'))) {   /* cmd delimiter? */
    lnt = ep - sim_brk_act[sim_do_depth];               /* cmd length */
    memcpy (buf, sim_brk_act[sim_do_depth], lnt + 1);   /* copy with ; */
    buf[lnt] = 0;                                       /* erase ; */
    sim_brk_act[sim_do_depth] += lnt + 1;               /* adv ptr */
    }
else {
    strncpy (buf, sim_brk_act[sim_do_depth], size);     /* copy action */
    strlcpy (buf, sim_brk_act[sim_do_depth], size);     /* copy action */
    sim_brk_clract ();                                  /* no more */
    }
return buf;
}

/* Clear pending actions */

10180
10181
10182
10183
10184
10185
10186


10187
10188
10189
10190
10191
10192
10193


10194
10195
10196
10197
10198
10199
10200
10201
10202
10203
10204
10205
10206
10207
10208

10209
10210

10211
10212
10213






10214

10215
10216
10217
10218
10219
10220
10221
10611
10612
10613
10614
10615
10616
10617
10618
10619
10620

10621
10622
10623
10624
10625
10626
10627
10628
10629
10630
10631
10632
10633
10634
10635
10636
10637
10638
10639
10640

10641
10642
10643

10644
10645
10646
10647
10648
10649
10650
10651
10652
10653

10654
10655
10656
10657
10658
10659
10660
10661







+
+

-





+
+













-

+

-
+



+
+
+
+
+
+
-
+







/* Set expect */

t_stat sim_set_expect (EXPECT *exp, CONST char *cptr)
{
char gbuf[CBUFSIZE];
CONST char *tptr;
CONST char *c1ptr;
const char *dev_name;
uint32 after;
t_bool after_set = FALSE;
uint32 after = exp->after;
int32 cnt = 0;
t_stat r;

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_2FARG;
dev_name = tmxr_expect_line_name (exp);
after = get_default_env_parameter (dev_name, "SIM_EXPECT_HALTAFTER", 0);
if (*cptr == '[') {
    cnt = (int32) strtotv (cptr + 1, &c1ptr, 10);
    if ((cptr == c1ptr) || (*c1ptr != ']'))
        return sim_messagef (SCPE_ARG, "Invalid Repeat count specification\n");
    cptr = c1ptr + 1;
    while (sim_isspace(*cptr))
        ++cptr;
    }
tptr = get_glyph (cptr, gbuf, ',');
if ((!strncmp(gbuf, "HALTAFTER=", 10)) && (gbuf[10])) {
    after = (uint32)get_uint (&gbuf[10], 10, 100000000, &r);
    if (r != SCPE_OK)
        return sim_messagef (SCPE_ARG, "Invalid Halt After Value\n");
    after_set = TRUE;
    cptr = tptr;
    after_set = TRUE;
    }
if ((*cptr != '"') && (*cptr != '\''))
if ((*cptr != '\0') && (*cptr != '"') && (*cptr != '\''))
    return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
cptr = get_glyph_quoted (cptr, gbuf, 0);

/* Hsndle a bare HALTAFTER=nnn command */
if ((gbuf[0] == '\0') && (*cptr == '\0') && after_set) {
    set_default_env_parameter (dev_name, "SIM_EXPECT_HALTAFTER", after);
    return SCPE_OK;
    }

return sim_exp_set (exp, gbuf, cnt, (after_set ? after : exp->after), sim_switches, cptr);
return sim_exp_set (exp, gbuf, cnt, after, sim_switches, cptr);
}

/* Clear expect */

t_stat sim_set_noexpect (EXPECT *exp, const char *cptr)
{
char gbuf[CBUFSIZE];
10293
10294
10295
10296
10297
10298
10299
10300

10301
10302
10303
10304
10305
10306
10307
10733
10734
10735
10736
10737
10738
10739

10740
10741
10742
10743
10744
10745
10746
10747







-
+







    }
free (exp->rules);
exp->rules = NULL;
exp->size = 0;
free (exp->buf);
exp->buf = NULL;
exp->buf_size = 0;
exp->buf_ins = 0;
exp->buf_data = exp->buf_ins = 0;
return SCPE_OK;
}

/* Set/Add an expect rule */

t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, uint32 after, int32 switches, const char *act)
{
10359
10360
10361
10362
10363
10364
10365
10366
10367

10368
10369
10370
10371
10372
10373
10374
10799
10800
10801
10802
10803
10804
10805

10806
10807
10808
10809
10810
10811
10812
10813
10814







-

+







        return sim_messagef (SCPE_ARG, "Persistent Expect rule with identical match string already exists\n");
    }
if (after && exp->size)
    return sim_messagef (SCPE_ARG, "Multiple concurrent EXPECT rules aren't valid when a HALTAFTER parameter is non-zero\n");
exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
ep = &exp->rules[exp->size];
exp->size += 1;
exp->after = after;                                     /* set halt after value */
memset (ep, 0, sizeof(*ep));
ep->after = after;                                     /* set halt after value */
ep->match_pattern = (char *)malloc (strlen (match) + 1);
if (ep->match_pattern)
    strcpy (ep->match_pattern, match);
ep->cnt = cnt;                                          /* set proceed count */
ep->switches = switches;                                /* set switches */
match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
if ((match_buf == NULL) || (ep->match_pattern == NULL)) {
10396
10397
10398
10399
10400
10401
10402




10403

10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414
10415
10416
10417
10418
10419
10420
10421
10422
10423



10424
10425
10426

10427
10428
10429
10430
10431
10432
10433
10434


10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446


10447
10448
10449
10450
10451
10452
10453





10454
10455
10456
10457
10458
10459
10460



10461
10462
10463
10464

10465
10466
10467
10468
10469
10470
10471
10836
10837
10838
10839
10840
10841
10842
10843
10844
10845
10846

10847
10848
10849
10850
10851
10852
10853
10854
10855
10856
10857
10858
10859
10860
10861
10862
10863
10864
10865
10866
10867
10868
10869
10870
10871
10872

10873
10874
10875
10876
10877
10878
10879
10880
10881
10882
10883
10884
10885
10886
10887
10888
10889
10890
10891
10892
10893
10894
10895
10896
10897
10898
10899
10900
10901



10902
10903
10904
10905
10906
10907
10908





10909
10910
10911
10912
10913
10914

10915
10916
10917
10918
10919
10920
10921
10922







+
+
+
+
-
+




















+
+
+


-
+








+
+












+
+




-
-
-
+
+
+
+
+


-
-
-
-
-
+
+
+



-
+







if (ep->act) {                                          /* replace old action? */
    free (ep->act);                                     /* deallocate */
    ep->act = NULL;                                     /* now no action */
    }
if (act)
    while (sim_isspace(*act)) ++act;                    /* skip leading spaces in action string */
if ((act != NULL) && (*act != 0)) {                     /* new action? */
    char *newp;

    if ((act > sim_sub_instr_buf) && ((size_t)(act - sim_sub_instr_buf) < sim_sub_instr_size))
        act = &sim_sub_instr[sim_sub_instr_off[act - sim_sub_instr_buf]]; /* get un-substituted string */
    char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
    newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
    if (newp == NULL)                                   /* mem err? */
        return SCPE_MEM;
    strcpy (newp, act);                                 /* copy action */
    ep->act = newp;                                     /* set pointer */
    }
/* Make sure that the production buffer is large enough to detect a match for all rules including a NUL termination byte */
for (i=0; i<exp->size; i++) {
    uint32 compare_size = (exp->rules[i].switches & EXP_TYP_REGEX) ? MAX(10 * strlen(ep->match_pattern), 1024) : exp->rules[i].size;
    if (compare_size >= exp->buf_size) {
        exp->buf = (uint8 *)realloc (exp->buf, compare_size + 2); /* Extra byte to null terminate regex compares */
        exp->buf_size = compare_size + 1;
        }
    }
return SCPE_OK;
}

/* Show an expect rule */

t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep)
{
const char *dev_name = tmxr_expect_line_name (exp);
uint32 default_haltafter = get_default_env_parameter (dev_name, "SIM_EXPECT_HALTAFTER", 0);

if (!ep)
    return SCPE_OK;
fprintf (st, "EXPECT");
fprintf (st, "    EXPECT");
if (ep->switches & EXP_TYP_PERSIST)
    fprintf (st, " -p");
if (ep->switches & EXP_TYP_CLEARALL)
    fprintf (st, " -c");
if (ep->switches & EXP_TYP_REGEX)
    fprintf (st, " -r");
if (ep->switches & EXP_TYP_REGEX_I)
    fprintf (st, " -i");
if (ep->after != default_haltafter)
    fprintf (st, " HALTAFTER=%d", (int)ep->after);
fprintf (st, " %s", ep->match_pattern);
if (ep->cnt > 0)
    fprintf (st, " [%d]", ep->cnt);
if (ep->act)
    fprintf (st, " %s", ep->act);
fprintf (st, "\n");
return SCPE_OK;
}

t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match)
{
CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0);
const char *dev_name = tmxr_expect_line_name (exp);
uint32 default_haltafter = get_default_env_parameter (dev_name, "SIM_EXPECT_HALTAFTER", 0);

if (exp->buf_size) {
    char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins);

    fprintf (st, "Match Buffer Size: %d\n", exp->buf_size);
    fprintf (st, "Buffer Insert Offset: %d\n", exp->buf_ins);
    fprintf (st, "Buffer Contents: %s\n", bstr);
    fprintf (st, "  Match Buffer Size: %d\n", exp->buf_size);
    fprintf (st, "  Buffer Insert Offset: %d\n", exp->buf_ins);
    fprintf (st, "  Buffer Contents: %s\n", bstr);
    if (default_haltafter)
        fprintf (st, "  Default HaltAfter: %u instructions\n", (unsigned)default_haltafter);
    free (bstr);
    }
if (exp->after)
    fprintf (st, "Halt After: %d instructions\n", exp->after);
if (exp->dptr && exp->dbit)
    fprintf (st, "Debugging via: SET %s DEBUG%s%s\n", sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "", exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : "");
fprintf (st, "Match Rules:\n");
if (exp->dptr && (exp->dbit & exp->dptr->dctrl))
    fprintf (st, "  Expect Debugging via: SET %s DEBUG%s%s\n", sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "", exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : "");
fprintf (st, "  Match Rules:\n");
if (!*match)
    return sim_exp_showall (st, exp);
if (!ep) {
    fprintf (st, "No Rules match '%s'\n", match);
    fprintf (st, "  No Rules match '%s'\n", match);
    return SCPE_ARG;
    }
do {
    sim_exp_show_tab (st, exp, ep);
    ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 1 + (ep - exp->rules));
    } while (ep);
return SCPE_OK;
10492
10493
10494
10495
10496
10497
10498


10499
10500
10501
10502
10503
10504
10505
10506
10507
10508
10509
10510
10511
10512
10513
10514

10515
10516
10517
10518
10519
10520
10521
10943
10944
10945
10946
10947
10948
10949
10950
10951
10952
10953
10954
10955
10956
10957
10958
10959
10960
10961
10962
10963
10964
10965

10966
10967
10968
10969
10970
10971
10972
10973
10974







+
+














-

+







char *tstr = NULL;

if ((!exp) || (!exp->rules))                            /* Anying to check? */
    return SCPE_OK;

exp->buf[exp->buf_ins++] = data;                        /* Save new data */
exp->buf[exp->buf_ins] = '\0';                          /* Nul terminate for RegEx match */
if (exp->buf_data < exp->buf_size)
    ++exp->buf_data;                                    /* Record amount of data in buffer */

for (i=0; i < exp->size; i++) {
    ep = &exp->rules[i];
    if (ep->switches & EXP_TYP_REGEX) {
#if defined (USE_REGEX)
        regmatch_t *matches;
        char *cbuf = (char *)exp->buf;
        static size_t sim_exp_match_sub_count = 0;

        if (tstr)
            cbuf = tstr;
        else {
            if (strlen ((char *)exp->buf) != exp->buf_ins) { /* Nul characters in buffer? */
                size_t off;
                tstr = (char *)malloc (exp->buf_ins + 1);

                tstr = (char *)malloc (exp->buf_ins + 1);
                tstr[0] = '\0';
                for (off=0; off < exp->buf_ins; off += 1 + strlen ((char *)&exp->buf[off]))
                    strcpy (&tstr[strlen (tstr)], (char *)&exp->buf[off]);
                cbuf = tstr;
                }
            }
        ++regex_checks;
10550
10551
10552
10553
10554
10555
10556


10557

10558
10559
10560
10561
10562
10563
10564
10565
10566
10567
10568
10569
10570
10571
10572
10573


10574
10575
10576
10577
10578
10579
10580
10581
10582
10583
10584
10585


10586
10587
10588
10589
10590
10591
10592
10593
10594
10595
10596
10597
10598
10599


10600
10601
10602
10603
10604
10605
10606
10607
10608
10609
10610
10611
10612

10613
10614
10615
10616
10617
10618
10619
11003
11004
11005
11006
11007
11008
11009
11010
11011

11012
11013
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026


11027
11028
11029
11030
11031
11032
11033
11034
11035
11036
11037
11038


11039
11040
11041
11042
11043
11044
11045
11046
11047
11048
11049
11050
11051
11052


11053
11054
11055
11056
11057
11058
11059
11060
11061
11062
11063
11064
11065
11066
11067
11068
11069
11070
11071
11072
11073
11074
11075







+
+
-
+














-
-
+
+










-
-
+
+












-
-
+
+













+







            free (buf);
            break;
            }
        free (matches);
#endif
        }
    else {
        if (exp->buf_data < ep->size)                           /* Too little data to match yet? */
            continue;                                           /* Yes, Try next one. */
        if (exp->buf_ins < ep->size) {                          /* Match stradle end of buffer */
        if (exp->buf_ins < ep->size) {                          /* Match might stradle end of buffer */
            /* 
             * First compare the newly deposited data at the beginning 
             * of buffer with the end of the match string
             */
            if (exp->buf_ins) {
                if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
                    char *estr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
                    char *mstr = sim_encode_quoted_string (&ep->match[ep->size-exp->buf_ins], exp->buf_ins);

                    sim_debug (exp->dbit, exp->dptr, "Checking String[0:%d]: %s\n", exp->buf_ins, estr);
                    sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
                    free (estr);
                    free (mstr);
                    }
                if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
                    continue;
                if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins)) /* Tail Match? */
                    continue;                                   /* Nope, Try next one. */
                }
            if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
                char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->size-exp->buf_ins);
                char *mstr = sim_encode_quoted_string (ep->match, ep->size-exp->buf_ins);

                sim_debug (exp->dbit, exp->dptr, "Checking String[%d:%d]: %s\n", exp->buf_size-(ep->size-exp->buf_ins), ep->size-exp->buf_ins, estr);
                sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
                free (estr);
                free (mstr);
                }
            if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
                continue;
            if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins)) /* Front Match? */
                continue;                                       /* Nope, Try next one. */
            break;
            }
        else {
            if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
                char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_ins-ep->size], ep->size);
                char *mstr = sim_encode_quoted_string (ep->match, ep->size);

                sim_debug (exp->dbit, exp->dptr, "Checking String[%d:%d]: %s\n", exp->buf_ins-ep->size, ep->size, estr);
                sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
                free (estr);
                free (mstr);
                }
            if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
                continue;
            if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size)) /* Whole string match? */
                continue;                                       /* Nope, Try next one. */
            break;
            }
        }
    }
if (exp->buf_ins == exp->buf_size) {                    /* At end of match buffer? */
    if (regex_checks) {
        /* When processing regular expressions, let the match buffer fill 
           up and then shuffle the buffer contents down by half the buffer size
           so that the regular expression has a single contiguous buffer to 
           match against instead of the wrapping buffer paradigm which is 
           used when no regular expression rules are in effect */
        memmove (exp->buf, &exp->buf[exp->buf_size/2], exp->buf_size-(exp->buf_size/2));
        exp->buf_ins -= exp->buf_size/2;
        exp->buf_data = exp->buf_ins;
        sim_debug (exp->dbit, exp->dptr, "Buffer Full - sliding the last %d bytes to start of buffer new insert at: %d\n", (exp->buf_size/2), exp->buf_ins);
        }
    else {
        exp->buf_ins = 0;                               /* wrap around to beginning */
        sim_debug (exp->dbit, exp->dptr, "Buffer wrapping\n");
        }
    }
10637
10638
10639
10640
10641
10642
10643
10644
10645


10646
10647
10648

10649
10650
10651
10652
10653
10654
10655
11093
11094
11095
11096
11097
11098
11099


11100
11101
11102
11103

11104
11105
11106
11107
11108
11109
11110
11111







-
-
+
+


-
+







            sim_exp_clrall (exp);                       /* delete all rules */
        else {
            if (!(ep->switches & EXP_TYP_PERSIST))      /* One shot expect rule? */
                sim_exp_clr_tab (exp, ep);              /* delete it */
            }
        sim_activate (&sim_expect_unit,                 /* schedule simulation stop when indicated */
                      (ep->switches & EXP_TYP_TIME) ?  
                            (int32)((sim_timer_inst_per_sec ()*exp->after)/1000000.0) : 
                            exp->after);
                            (int32)((sim_timer_inst_per_sec ()*ep->after)/1000000.0) : 
                            ep->after);
        }
    /* Matched data is no longer available for future matching */
    exp->buf_ins = 0;
    exp->buf_data = exp->buf_ins = 0;
    }
free (tstr);
return SCPE_OK;
}

/* Queue input data for sending */

10663
10664
10665
10666
10667
10668
10669
10670
10671

10672
10673

10674
10675
10676
10677
10678
10679
10680
10681
10682
10683
10684
10685
10686
10687
10688
10689
10690
10691

10692
10693

10694
10695
10696
10697
10698

10699
10700
10701


10702
10703
10704

10705
10706
10707


10708
10709
10710
10711



10712
10713
10714
10715
10716
10717
10718
11119
11120
11121
11122
11123
11124
11125


11126


11127


11128
11129
11130
11131
11132
11133
11134
11135
11136
11137
11138
11139
11140
11141
11142
11143
11144
11145

11146
11147
11148
11149
11150

11151
11152


11153
11154
11155
11156

11157
11158


11159
11160
11161



11162
11163
11164
11165
11166
11167
11168
11169
11170
11171







-
-
+
-
-
+
-
-
















+

-
+




-
+

-
-
+
+


-
+

-
-
+
+

-
-
-
+
+
+







    }
if (snd->insoff+size > snd->bufsize) {
    snd->bufsize = snd->insoff+size;
    snd->buffer = (uint8 *)realloc(snd->buffer, snd->bufsize);
    }
memcpy(snd->buffer+snd->insoff, data, size);
snd->insoff += size;
if (delay)
    snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay;
snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay;
if (after)
    snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after;
snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after;
if (snd->after == 0)
    snd->after = snd->delay;
snd->next_time = sim_gtime() + snd->after;
return SCPE_OK;
}

/* Cancel Queued input data */
t_stat sim_send_clear (SEND *snd)
{
snd->insoff = 0;
snd->extoff = 0;
return SCPE_OK;
}

/* Display console Queued input data status */

t_stat sim_show_send_input (FILE *st, const SEND *snd)
{
fprintf (st, "%s\n", tmxr_send_line_name (snd));
if (snd->extoff < snd->insoff) {
    fprintf (st, "%d bytes of pending input Data:\n    ", snd->insoff-snd->extoff);
    fprintf (st, "  %d bytes of pending input Data:\n    ", snd->insoff-snd->extoff);
    fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
    fprintf (st, "\n");
    }
else
    fprintf (st, "No Pending Input Data\n");
    fprintf (st, "  No Pending Input Data\n");
if ((snd->next_time - sim_gtime()) > 0) {
    if ((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0))
        fprintf (st, "Minimum of %d instructions (%d microseconds) before sending first character\n", (int)(snd->next_time - sim_gtime()),
    if (((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0)) && ((sim_timer_inst_per_sec()/1000000.0) > 0.0))
        fprintf (st, "  Minimum of %d instructions (%d microseconds) before sending first character\n", (int)(snd->next_time - sim_gtime()),
                                                        (int)((snd->next_time - sim_gtime())/(sim_timer_inst_per_sec()/1000000.0)));
    else
        fprintf (st, "Minimum of %d instructions before sending first character\n", (int)(snd->next_time - sim_gtime()));
        fprintf (st, "  Minimum of %d instructions before sending first character\n", (int)(snd->next_time - sim_gtime()));
    }
if (snd->delay > (sim_timer_inst_per_sec()/1000000.0))
    fprintf (st, "Minimum of %d instructions (%d microseconds) between characters\n", (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0)));
if ((snd->delay > (sim_timer_inst_per_sec()/1000000.0)) && ((sim_timer_inst_per_sec()/1000000.0) > 0.0))
    fprintf (st, "  Minimum of %d instructions (%d microseconds) between characters\n", (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0)));
else
    fprintf (st, "Minimum of %d instructions between characters\n", (int)snd->delay);
if (snd->dptr && snd->dbit)
    fprintf (st, "Debugging via: SET %s DEBUG%s%s\n", sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "", snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : "");
    fprintf (st, "  Minimum of %d instructions between characters\n", (int)snd->delay);
if (snd->dptr && (snd->dbit & snd->dptr->dctrl))
    fprintf (st, "  Send Debugging via: SET %s DEBUG%s%s\n", sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "", snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : "");
return SCPE_OK;
}

/* Poll for Queued input data */

t_bool sim_send_poll_data (SEND *snd, t_stat *stat)
{
10791
10792
10793
10794
10795
10796
10797
10798

10799
10800
10801
10802
10803
10804
10805
11244
11245
11246
11247
11248
11249
11250

11251
11252
11253
11254
11255
11256
11257
11258







-
+







static const char *debtab_nomatch = "DEBTAB_NOMATCH";
const char *some_match = NULL;
int32 offset = 0;

if (dptr->debflags == 0)
    return debtab_none;

dbits &= dptr->dctrl;                           /* Look for just the bits tha matched */
dbits &= dptr->dctrl;                           /* Look for just the bits that matched */

/* Find matching words for bitmask */

while (dptr->debflags[offset].name && (offset < 32)) {
    if (dptr->debflags[offset].mask == dbits)   /* All Bits Match */
        return dptr->debflags[offset].name;
    if (dptr->debflags[offset].mask & dbits)
10994
10995
10996
10997
10998
10999
11000


11001
11002
11003
11004
11005
11006
11007
11447
11448
11449
11450
11451
11452
11453
11454
11455
11456
11457
11458
11459
11460
11461
11462







+
+







char stackbuf[STACKBUFSIZE];
int32 bufsize = sizeof(stackbuf);
char *buf = stackbuf;
int32 len;
va_list arglist;
t_bool inhibit_message = (!sim_show_message || (stat & SCPE_NOMESSAGE));

if ((stat == SCPE_OK) && (sim_quiet || (sim_switches & SWMASK ('Q'))))
    return stat;
while (1) {                                         /* format passed string, args */
    va_start (arglist, fmt);
#if defined(NO_vsnprintf)
    len = vsprintf (buf, fmt, arglist);
#else                                               /* !defined(NO_vsnprintf) */
    len = vsnprintf (buf, bufsize-1, fmt, arglist);
#endif                                              /* NO_vsnprintf */
11021
11022
11023
11024
11025
11026
11027
11028

11029


11030
11031
11032
11033
11034
11035
11036
11476
11477
11478
11479
11480
11481
11482

11483
11484
11485
11486
11487
11488
11489
11490
11491
11492
11493







-
+

+
+







        buf[bufsize-1] = '\0';
        continue;
        }
    break;
    }

if (sim_do_ocptr[sim_do_depth]) {
    if (!sim_do_echo && !sim_quiet && !inhibit_message)
    if (!sim_do_echo && !inhibit_message && !sim_cmd_echoed) {
        sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
        sim_cmd_echoed = TRUE;
        }
    else {
        if (sim_deb) {                      /* Always put context in debug output */
            TMLN *saved_oline = sim_oline;

            sim_oline = NULL;               /* avoid potential debug to active socket */
            fprintf (sim_deb, "%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
            sim_oline = saved_oline;        /* restore original socket */
11073
11074
11075
11076
11077
11078
11079
11080
11081
11082
11083
11084
11085
11086
11087
11088
11089
11090
11091
11530
11531
11532
11533
11534
11535
11536



11537

11538
11539
11540
11541
11542
11543
11544







-
-
-

-







   set and the bitmask matches the current device debug options.
   Extra returns are added for un*x systems, since the output
   device is set into 'raw' mode when the cpu is booted,
   and the extra returns don't hurt any other systems. 
   Callers should be calling sim_debug() which is a macro
   defined in scp.h which evaluates the action condition before 
   incurring call overhead. */
#if defined(__cplusplus)
void _sim_debug (uint32 dbits, void* vdptr, const char* fmt, ...)
#else
void _sim_debug (uint32 dbits, DEVICE* vdptr, const char* fmt, ...)
#endif
{
DEVICE *dptr = (DEVICE *)vdptr;
if (sim_deb && dptr && (dptr->dctrl & dbits)) {
    TMLN *saved_oline = sim_oline;
    char stackbuf[STACKBUFSIZE];
    int32 bufsize = sizeof(stackbuf);
    char *buf = stackbuf;
11575
11576
11577
11578
11579
11580
11581
11582

11583
11584
11585
11586
11587
11588
11589
12028
12029
12030
12031
12032
12033
12034

12035
12036
12037
12038
12039
12040
12041
12042







-
+







                while (sim_isdigit (*start))    /* Get param # */
                    n += (n * 10) + (*start++ - '0');
                if (!*start || *start == '\n'|| n == 0 || n >= VSMAX)
                    FAIL (SCPE_ARG, Invalid parameter number, start);
                while (n > vsnum)               /* Get arg pointer if not cached */
                    vstrings[vsnum++] = va_arg (ap, char *);
                end = vstrings[n-1];            /* Check for True */
                if (!end || !(toupper (*end) == 'T' || *end == '1')) {
                if (!end || !(sim_toupper (*end) == 'T' || *end == '1')) {
                    excluded = TRUE;            /* False, skip topic this time */
                    if (*htext)
                        htext++;
                    continue;
                    }
                }
            newt = (TOPIC *) calloc (sizeof (TOPIC), 1);
11804
11805
11806
11807
11808
11809
11810
11811

11812
11813
11814
11815
11816
11817
11818
12257
12258
12259
12260
12261
12262
12263

12264
12265
12266
12267
12268
12269
12270
12271







-
+







            ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
    cptr = cbuf;
    while (*cptr) {
        if (blankch (*cptr)) {
            *cptr++ = '_';
            } 
        else {
            *cptr = (char)toupper (*cptr);
            *cptr = (char)sim_toupper (*cptr);
            cptr++;
            }
        }
    if (!strncmp (cbuf, token, strlen (token))) {
        if (match)
            return HLP_MATCH_AMBIGUOUS;
        match = i+1;
11876
11877
11878
11879
11880
11881
11882
11883

11884
11885
11886
11887
11888
11889
11890
12329
12330
12331
12332
12333
12334
12335

12336
12337
12338
12339
12340
12341
12342
12343







-
+







    p = dptr->name;
    flat_help = (dptr->flags & DEV_FLATHELP) != 0;
    }
else
    p = sim_name;
top.title = (char *) malloc (strlen (p) + ((flag & SCP_HELP_ATTACH)? sizeof (attach_help)-1: 0) +1);
for (i = 0; p[i]; i++ )
    top.title[i] = (char)toupper (p[i]);
    top.title[i] = (char)sim_toupper (p[i]);
top.title[i] = '\0';
if (flag & SCP_HELP_ATTACH)
    strcpy (top.title+i, attach_help);

top.label = (char *) malloc (sizeof ("1"));
strcpy (top.label, "1");

12101
12102
12103
12104
12105
12106
12107
12108

12109
12110
12111
12112
12113
12114
12115
12554
12555
12556
12557
12558
12559
12560

12561
12562
12563
12564
12565
12566
12567
12568







-
+







         * work (one reason files are probably not a good idea),
         * but we might as well try.  Some OSs won't include a
         * path.  Having failed in the CWD, try to find the location
         * of the executable.  Failing that, try the 'help' subdirectory
         * of the executable.  Failing that, we're out of luck.
         */
        fbuf[sizeof(fbuf)-1] = '\0';
        strncpy (fbuf, sim_argv[0], sizeof (fbuf)-1);
        strlcpy (fbuf, sim_argv[0], sizeof (fbuf));
        if ((p = (char *)match_ext (fbuf, "EXE")))
            *p = '\0';
        if ((p = strrchr (fbuf, '\\'))) {
            p[1] = '\0';
            d = "%s\\";
            } 
        else {
Changes to src/scp.h.
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
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







+











+







t_stat set_cmd (int32 flag, CONST char *ptr);
t_stat show_cmd (int32 flag, CONST char *ptr);
t_stat set_default_cmd (int32 flg, CONST char *cptr);
t_stat pwd_cmd (int32 flg, CONST char *cptr);
t_stat dir_cmd (int32 flg, CONST char *cptr);
t_stat type_cmd (int32 flg, CONST char *cptr);
t_stat delete_cmd (int32 flg, CONST char *cptr);
t_stat copy_cmd (int32 flg, CONST char *cptr);
t_stat brk_cmd (int32 flag, CONST char *ptr);
t_stat do_cmd (int32 flag, CONST char *ptr);
t_stat goto_cmd (int32 flag, CONST char *ptr);
t_stat return_cmd (int32 flag, CONST char *ptr);
t_stat shift_cmd (int32 flag, CONST char *ptr);
t_stat call_cmd (int32 flag, CONST char *ptr);
t_stat on_cmd (int32 flag, CONST char *ptr);
t_stat noop_cmd (int32 flag, CONST char *ptr);
t_stat assert_cmd (int32 flag, CONST char *ptr);
t_stat send_cmd (int32 flag, CONST char *ptr);
t_stat expect_cmd (int32 flag, CONST char *ptr);
t_stat sleep_cmd (int32 flag, CONST char *ptr);
t_stat help_cmd (int32 flag, CONST char *ptr);
t_stat screenshot_cmd (int32 flag, CONST char *ptr);
t_stat spawn_cmd (int32 flag, CONST char *ptr);
t_stat echo_cmd (int32 flag, CONST char *ptr);

/* Allow compiler to help validate printf style format arguments */
#if !defined __GNUC__
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
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







-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+







t_stat reset_all (uint32 start_device);
t_stat reset_all_p (uint32 start_device);
const char *sim_dname (DEVICE *dptr);
const char *sim_uname (UNIT *dptr);
const char *sim_set_uname (UNIT *uptr, const char *uname);
t_stat get_yn (const char *ques, t_stat deflt);
char *sim_trim_endspc (char *cptr);
int sim_isspace (char c);
int sim_islower (char c);
int sim_isalpha (char c);
int sim_isprint (char c);
int sim_isdigit (char c);
int sim_isgraph (char c);
int sim_isalnum (char c);
int sim_isspace (int c);
#ifdef isspace
#undef isspace
#endif
#ifndef IN_SCP_C
#define isspace(chr) sim_isspace (chr)
#endif
int sim_islower (int c);
#ifdef islower
#undef islower
#endif
#ifndef IN_SCP_C
#define islower(chr) sim_islower (chr)
#endif
int sim_isalpha (int c);
#ifdef isalpha
#undef isalpha
#endif
#ifndef IN_SCP_C
#define isalpha(chr) sim_isalpha (chr)
#endif
int sim_isprint (int c);
#ifdef isprint
#undef isprint
#endif
#ifndef IN_SCP_C
#define isprint(chr) sim_isprint (chr)
#endif
int sim_isdigit (int c);
#ifdef isdigit
#undef isdigit
#endif
#ifndef IN_SCP_C
#define isdigit(chr) sim_isdigit (chr)
#endif
int sim_isgraph (int c);
#ifdef isgraph
#undef isgraph
#endif
#ifndef IN_SCP_C
#define isgraph(chr) sim_isgraph (chr)
#endif
int sim_isalnum (int c);
#ifdef isalnum
#undef isalnum
#endif
#ifndef IN_SCP_C
#define isalnum(chr) sim_isalnum (chr)
#endif
int sim_toupper (int c);
int sim_tolower (int c);
#ifdef toupper
#undef toupper
#endif
#define toupper(chr) sim_toupper(chr)
#ifdef tolower
#undef tolower
#endif
#define tolower(chr) sim_tolower(chr)
int sim_strncasecmp (const char *string1, const char *string2, size_t len);
int sim_strcasecmp (const char *string1, const char *string2);
size_t sim_strlcat (char *dst, const char *src, size_t size);
size_t sim_strlcpy (char *dst, const char *src, size_t size);
#ifndef strlcpy
#define strlcpy(dst, src, size) sim_strlcpy((dst), (src), (size))
#endif
#ifndef strlcat
#define strlcat(dst, src, size) sim_strlcat((dst), (src), (size))
#endif
#ifndef strncasecmp
#define strncasecmp(str1, str2, len) sim_strncasecmp((str1), (str2), (len))
#endif
#ifndef strcasecmp
#define strcasecmp(str1, str2) sim_strcasecmp ((str1), (str2))
#endif
CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st);
const char *put_switches (char *buf, size_t bufsize, uint32 sw);
CONST char *get_glyph (const char *iptr, char *optr, char mchar);
CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar);
CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar);
CONST char *get_glyph_cmd (const char *iptr, char *optr);
t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status);
182
183
184
185
186
187
188

189
190
191
192
193
194
195
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262







+







t_stat fprint_val (FILE *stream, t_value val, uint32 rdx, uint32 wid, uint32 fmt);
t_stat sprint_val (char *buf, t_value val, uint32 rdx, uint32 wid, uint32 fmt);
t_stat sim_print_val (t_value val, uint32 radix, uint32 width, uint32 format);
const char *sim_fmt_secs (double seconds);
const char *sim_fmt_numeric (double number);
const char *sprint_capac (DEVICE *dptr, UNIT *uptr);
char *read_line (char *cptr, int32 size, FILE *stream);
char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream);
void fprint_reg_help (FILE *st, DEVICE *dptr);
void fprint_set_help (FILE *st, DEVICE *dptr);
void fprint_show_help (FILE *st, DEVICE *dptr);
CTAB *find_cmd (const char *gbuf);
DEVICE *find_dev (const char *ptr);
DEVICE *find_unit (const char *ptr, UNIT **uptr);
DEVICE *find_dev_from_unit (UNIT *uptr);
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
300
301
302
303
304
305
306









307
308
309
310
311

312

313
314
315
316
317
318
319







-
-
-
-
-
-
-
-
-





-
+
-







void sim_debug_bits_hdr (uint32 dbits, DEVICE* dptr, const char *header, 
    BITFIELD* bitdefs, uint32 before, uint32 after, int terminate);
void sim_debug_bits (uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs,
    uint32 before, uint32 after, int terminate);
#if defined (__DECC) && defined (__VMS) && (defined (__VAX) || (__DECC_VER < 60590001))
#define CANT_USE_MACRO_VA_ARGS 1
#endif
#if defined(__cplusplus)
#ifdef CANT_USE_MACRO_VA_ARGS
#define _sim_debug sim_debug
void sim_debug (uint32 dbits, void* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4);
#else
void _sim_debug (uint32 dbits, void* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4);
#define sim_debug(dbits, dptr, ...) do { if (sim_deb && dptr && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__);} while (0)
#endif
#else
#ifdef CANT_USE_MACRO_VA_ARGS
#define _sim_debug sim_debug
void sim_debug (uint32 dbits, DEVICE* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4);
#else
void _sim_debug (uint32 dbits, DEVICE* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4);
#define sim_debug(dbits, dptr, ...) do { if (sim_deb && dptr && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__);} while (0)
#define sim_debug(dbits, dptr, ...) do { if (sim_deb && dptr && ((dptr)->dctrl & (dbits))) _sim_debug (dbits, dptr, __VA_ARGS__);} while (0)
#endif
#endif
void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr);
#define SCP_HELP_FLAT   (1u << 31)       /* Force flat help when prompting is not possible */
#define SCP_HELP_ONECMD (1u << 30)       /* Display one topic, do not prompt */
#define SCP_HELP_ATTACH (1u << 29)       /* Top level topic is ATTACH help */
t_stat scp_help (FILE *st, DEVICE *dptr,
                 UNIT *uptr, int32 flag, const char *help, const char *cptr, ...);
270
271
272
273
274
275
276

277
278
279
280
281
282
283
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341







+







/* Global data */

extern DEVICE *sim_dflt_dev;
extern DEVICE *sim_dfdev;
extern UNIT *sim_dfunit;
extern int32 sim_interval;
extern int32 sim_switches;
extern int32 sim_switch_number;
extern int32 sim_quiet;
extern int32 sim_step;
extern t_stat sim_last_cmd_stat;                        /* Command Status */
extern FILE *sim_log;                                   /* log file */
extern FILEREF *sim_log_ref;                            /* log file file reference */
extern FILE *sim_deb;                                   /* debug file */
extern FILEREF *sim_deb_ref;                            /* debug file file reference */
Changes to src/sim_console.c.
224
225
226
227
228
229
230
231

232
233
234
235
236
237

238
239
240
241
242
243
244
224
225
226
227
228
229
230

231
232
233
234
235
236

237
238
239
240
241
242
243
244







-
+





-
+







return "Console telnet support";
}

DEVICE sim_con_telnet = {
    "CON-TELNET", sim_con_units, sim_con_reg, sim_con_mod, 
    2, 0, 0, 0, 0, 0, 
    NULL, NULL, sim_con_reset, NULL, sim_con_attach, sim_con_detach, 
    NULL, DEV_DEBUG, 0, sim_con_debug,
    NULL, DEV_DEBUG | DEV_NOSAVE, 0, sim_con_debug,
    NULL, NULL, NULL, NULL, NULL, sim_con_telnet_description};
TMLN sim_con_ldsc = { 0 };                                          /* console line descr */
TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc, NULL, &sim_con_telnet };/* console line mux */


SEND sim_con_send = {SEND_DEFAULT_DELAY, &sim_con_telnet, DBG_SND};
SEND sim_con_send = {0, &sim_con_telnet, DBG_SND};
EXPECT sim_con_expect = {&sim_con_telnet, DBG_EXP};

static t_bool sim_con_console_port = TRUE;

/* Enable automatic WRU console polling */

t_stat sim_set_noconsole_port (void)
336
337
338
339
340
341
342
343

344
345
346


347
348
349
350
351
352
353
336
337
338
339
340
341
342

343
344


345
346
347
348
349
350
351
352
353







-
+

-
-
+
+







    { "PCHAR", &sim_show_pchar, 0 },
    { "SPEED", &sim_show_cons_speed, 0 },
    { "LOG", &sim_show_cons_log, 0 },
    { "TELNET", &sim_show_telnet, 0 },
    { "DEBUG", &sim_show_cons_debug, 0 },
    { "BUFFERED", &sim_show_cons_buff, 0 },
    { "EXPECT", &sim_show_cons_expect, 0 },
    { "HALT", &sim_show_cons_expect, 0 },
    { "HALT", &sim_show_cons_expect, -1 },
    { "INPUT", &sim_show_cons_send_input, 0 },
    { "RESPONSE", &sim_show_cons_send_input, 0 },
    { "DELAY", &sim_show_cons_expect, 0 },
    { "RESPONSE", &sim_show_cons_send_input, -1 },
    { "DELAY", &sim_show_cons_expect, -1 },
    { NULL, NULL, 0 }
    };

static CTAB set_con_telnet_tab[] = {
    { "LOG", &sim_set_cons_log, 0 },
    { "NOLOG", &sim_set_cons_nolog, 0 },
    { "BUFFERED", &sim_set_cons_buff, 0 },
407
408
409
410
411
412
413

414

415
416
417
418
419
420
421
407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422







+
-
+







{
char gbuf[CBUFSIZE];
SHTAB *shptr;
int32 i;

if (*cptr == 0) {                                       /* show all */
    for (i = 0; show_con_tab[i].name; i++)
        if (show_con_tab[i].arg != -1)
        show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr);
            show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr);
    return SCPE_OK;
    }
while (*cptr != 0) {
    cptr = get_glyph (cptr, gbuf, ',');                 /* get modifier */
    if ((shptr = find_shtab (show_con_tab, gbuf)))
        shptr->action (st, dptr, uptr, shptr->arg, cptr);
    else return SCPE_NOPARAM;
475
476
477
478
479
480
481

482
483
484
485
486
487
488
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490







+







    int             ptr;            /* pointer to next value cell */
    int             depth;          /* number of values */
    int             *vals;          /* values */
    };
typedef struct BITSAMPLE_REG BITSAMPLE_REG;
struct BITSAMPLE_REG {
    REG             *reg;           /* Register to be sampled */
    uint32           idx;           /* Register index */
    t_bool          indirect;       /* Register value points at memory */
    DEVICE          *dptr;          /* Device register is part of */
    UNIT            *uptr;          /* Unit Register is related to */
    uint32          width;          /* number of bits to sample */
    BITSAMPLE       *bits;
    };
typedef struct REMOTE REMOTE;
498
499
500
501
502
503
504

505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514

515
516
517
518
519
520
521







+







-







    int             line;                   /* remote console line number */
    TMLN            *lp;                    /* mux line/socket for remote session */
    UNIT            *uptr;                  /* remote console unit */
    uint32          repeat_interval;        /* usecs between repeat execution */
    t_bool          repeat_pending;         /* repeat delivery pending */
    char            *repeat_action;         /* command(s) to repeatedly execute */
    int             smp_sample_interval;    /* cycles between samples */
    int             smp_sample_dither_pct;  /* dithering of cycles interval */
    uint32          smp_reg_count;          /* sample register count */
    BITSAMPLE_REG   *smp_regs;              /* registers being sampled */
    };
REMOTE *sim_rem_consoles = NULL;

static TMXR sim_rem_con_tmxr = { 0, 0, 0, NULL, NULL, &sim_remote_console };/* remote console line mux */
static uint32 sim_rem_read_timeout = 30;    /* seconds before automatic continue */
static uint32 *sim_rem_read_timeouts = NULL;/* per line read timeout (default from sim_rem_read_timeout) */
static int32 sim_rem_active_number = -1;    /* -1 - not active, >= 0 is index of active console */
int32 sim_rem_cmd_active_line = -1;         /* step in progress on line # */
static CTAB *sim_rem_active_command = NULL; /* active command */
static char *sim_rem_command_buf;           /* active command buffer */
static t_bool sim_log_temp = FALSE;         /* temporary log file active */
static char sim_rem_con_temp_name[PATH_MAX+1];
static t_bool sim_rem_master_mode = FALSE;  /* Master Mode Enabled Flag */
529
530
531
532
533
534
535



536

537
538
539
540
541
542
543
531
532
533
534
535
536
537
538
539
540

541
542
543
544
545
546
547
548







+
+
+
-
+







if (rem->smp_reg_count == 0) {
    fprintf (st, "Samples are not being collected\n");
    return SCPE_OK;
    }
for (reg = 0; reg < rem->smp_reg_count; reg++) {
    uint32 bit;

    if (rem->smp_regs[reg].reg->depth > 1)
        fprintf (st, "}%s %s[%d] %s %d:", rem->smp_regs[reg].dptr->name, rem->smp_regs[reg].reg->name, rem->smp_regs[reg].idx, rem->smp_regs[reg].indirect ? " -I" : "", rem->smp_regs[reg].bits[0].depth);
    else
    fprintf (st, "}%s %s%s %d:", rem->smp_regs[reg].dptr->name, rem->smp_regs[reg].reg->name, rem->smp_regs[reg].indirect ? " -I" : "", rem->smp_regs[reg].bits[0].depth);
        fprintf (st, "}%s %s%s %d:", rem->smp_regs[reg].dptr->name, rem->smp_regs[reg].reg->name, rem->smp_regs[reg].indirect ? " -I" : "", rem->smp_regs[reg].bits[0].depth);
    for (bit = 0; bit < rem->smp_regs[reg].width; bit++)
        fprintf (st, "%s%d", (bit != 0) ? "," : "", rem->smp_regs[reg].bits[bit].tot);
    fprintf (st, "\n");
    }
return SCPE_OK;
}

610
611
612
613
614
615
616



617

618
619
620
621
622
623



624

625
626
627
628
629
630
631
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







+
+
+
-
+






+
+
+
-
+







        fprintf (st, "The Command: %s\n", rem->repeat_action);
        fprintf (st, "    is repeated every %s\n", sim_fmt_secs (rem->repeat_interval / 1000000.0));
        }
    if (rem->smp_reg_count) {
        uint32 reg;
        DEVICE *dptr = NULL;

        if (rem->smp_sample_dither_pct)
            fprintf (st, "Register Bit Sampling is occurring every %d cycles (dithered %d percent)\n", rem->smp_sample_interval, rem->smp_sample_dither_pct);
        else
        fprintf (st, "Register Bit Sampling is occurring every %d cycles\n", rem->smp_sample_interval);
            fprintf (st, "Register Bit Sampling is occurring every %d cycles\n", rem->smp_sample_interval);
        fprintf (st, " Registers being sampled are: ");
        for (reg = 0; reg < rem->smp_reg_count; reg++) {
            if (rem->smp_regs[reg].indirect)
                fprintf (st, " indirect ");
            if (dptr != rem->smp_regs[reg].dptr)
                fprintf (st, "%s ", rem->smp_regs[reg].dptr->name);
            if (rem->smp_regs[reg].reg->depth > 1)
                fprintf (st, "%s[%d]%s", rem->smp_regs[reg].reg->name, rem->smp_regs[reg].idx, ((reg + 1) < rem->smp_reg_count) ? ", " : "");
            else
            fprintf (st, "%s%s", rem->smp_regs[reg].reg->name, ((reg + 1) < rem->smp_reg_count) ? ", " : "");
                fprintf (st, "%s%s", rem->smp_regs[reg].reg->name, ((reg + 1) < rem->smp_reg_count) ? ", " : "");
            dptr = rem->smp_regs[reg].dptr;
            }
        fprintf (st, "\n");
        if (sim_switches & SWMASK ('D'))
            sim_rem_sample_output (st, rem->line);
        }
    }
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
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







-
+













+
+







void sim_remote_process_command (void)
{
char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], *argv[1] = {NULL};
CONST char *cptr;
int32 saved_switches = sim_switches;
t_stat stat;

sim_strlcpy (cbuf, sim_rem_command_buf, sizeof (cbuf));
strlcpy (cbuf, sim_rem_command_buf, sizeof (cbuf));
while (isspace(cbuf[0]))
    memmove (cbuf, cbuf+1, strlen(cbuf+1)+1);   /* skip leading whitespace */
sim_sub_args (cbuf, sizeof(cbuf), argv);
cptr = cbuf;
cptr = get_glyph (cptr, gbuf, 0);               /* get command glyph */
sim_rem_active_command = find_cmd (gbuf);       /* find command */

if (!sim_processing_event)
    sim_ttcmd ();                               /* restore console */
stat = sim_rem_active_command->action (sim_rem_active_command->arg, cptr);/* execute command */
if (stat != SCPE_OK)
    stat = _sim_rem_message (gbuf, stat);       /* display results */
sim_last_cmd_stat = SCPE_BARE_STATUS(stat);
if (sim_vm_post != NULL)                        /* optionally let the simulator know */
    (*sim_vm_post) (TRUE);                      /* something might have changed */
if (!sim_processing_event) {
    sim_ttrun ();                               /* set console mode */
    sim_cancel (rem_con_data_unit);             /* force immediate activation of sim_rem_con_data_svc */
    sim_activate (rem_con_data_unit, -1);
    }
sim_switches = saved_switches;                  /* restore original switches */
}
1023
1024
1025
1026
1027
1028
1029
1030

1031
1032
1033
1034
1035
1036
1037
1036
1037
1038
1039
1040
1041
1042

1043
1044
1045
1046
1047
1048
1049
1050







-
+







/* 
    Parse and setup Remote Console REPEAT command:
       COLLECT nnn SAMPLES EVERY nnn CYCLES reg{,reg...}
 */
static t_stat sim_rem_collect_cmd_setup (int32 line, CONST char **iptr)
{
char gbuf[CBUFSIZE];
int32 samples, cycles;
int32 samples, cycles, dither_pct;
t_bool all_stop = FALSE;
t_stat stat = SCPE_OK;
CONST char *cptr = *iptr;
REMOTE *rem = &sim_rem_consoles[line];

sim_debug (DBG_SAM, &sim_remote_console, "Collect Setup: %s\n", cptr);
if (*cptr == 0)         /* required argument? */
1069
1070
1071
1072
1073
1074
1075

1076
1077
1078
1079
1080
1081
1082
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096







+







            }
        }
    else
        stat = sim_messagef (SCPE_ARG, "Expected value or STOP found: %s\n", gbuf);
    }
else {
    const char *tptr;
    int32 event_time = rem->smp_sample_interval;

    cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
    if (MATCH_CMD (gbuf, "SAMPLES") != 0) {
        *iptr = cptr;
        return sim_messagef (SCPE_ARG, "Expected SAMPLES found: %s\n", gbuf);
        }
    cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
1091
1092
1093
1094
1095
1096
1097

















1098
1099
1100
1101
1102
1103
1104
1105
1106

1107
1108
1109
1110
1111
1112
1113
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+







        return sim_messagef (SCPE_ARG, "Expected value found: %s\n", gbuf);
        }
    cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
    if ((MATCH_CMD (gbuf, "CYCLES") != 0) || (*cptr == 0)) {
        *iptr = cptr;
        return sim_messagef (SCPE_ARG, "Expected CYCLES found: %s\n", gbuf);
        }
    cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
    if ((MATCH_CMD (gbuf, "DITHER") != 0) || (*cptr == 0)) {
        *iptr = cptr;
        return sim_messagef (SCPE_ARG, "Expected DITHER found: %s\n", gbuf);
        }
    cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
    dither_pct = (int32) get_uint (gbuf, 10, INT_MAX, &stat);
    if ((stat != SCPE_OK) ||                        /* error? */
        (dither_pct < 0) || (dither_pct > 25)) {
        *iptr = cptr;
        return sim_messagef (SCPE_ARG, "Expected value found: %s\n", gbuf);
        }
    cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
    if ((MATCH_CMD (gbuf, "PERCENT") != 0) || (*cptr == 0)) {
        *iptr = cptr;
        return sim_messagef (SCPE_ARG, "Expected PERCENT found: %s\n", gbuf);
        }
    tptr = strcpy (gbuf, "STOP");                   /* Start from a clean slate */
    sim_rem_collect_cmd_setup (rem->line, &tptr);
    rem->smp_sample_interval = cycles;
    rem->smp_reg_count = 0;
    while (cptr && *cptr) {
        const char *comma = strchr (cptr, ',');
        char tbuf[2*CBUFSIZE];
        uint32 bit, width;
        REG *reg;
        uint32 idx;
        int32 saved_switches = sim_switches;
        t_bool indirect = FALSE;
        BITSAMPLE_REG *smp_regs;

        if (comma) {
            strncpy (tbuf, cptr, comma - cptr);
            tbuf[comma - cptr] = '\0';
1128
1129
1130
1131
1132
1133
1134



















1135
1136
1137
1138
1139
1140
1141

1142
1143
1144
1145
1146
1147
1148
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+







            break;
        tptr = get_glyph (tptr, gbuf, 0);     /* get next glyph */
        reg = find_reg (gbuf, &tptr, sim_dfdev);
        if (reg == NULL) {
            stat = sim_messagef (SCPE_NXREG, "Nonexistent Register: %s\n", gbuf);
            break;
            }
        if (*tptr == '[') {                             /* subscript? */
            const char *tgptr = ++tptr;

            if (reg->depth <= 1) {                      /* array register? */
                stat = sim_messagef (SCPE_SUB, "Not Array Register: %s\n", reg->name);
                break;
                }
            idx = (uint32) strtotv (tgptr, &tptr, 10);  /* convert index */
            if ((tgptr == tptr) || (*tptr++ != ']')) {
                stat = sim_messagef (SCPE_SUB, "Missing or Invalid Register Subscript: %s[%s\n", reg->name, tgptr);
                break;
                }
            if (idx >= reg->depth) {                    /* validate subscript */
                stat = sim_messagef (SCPE_SUB, "Invalid Register Subscript: %s[%d]\n", reg->name, idx);
                break;
                }
            }
        else
            idx = 0;                                    /* not array */
        smp_regs = (BITSAMPLE_REG *)realloc (rem->smp_regs, (rem->smp_reg_count + 1) * sizeof(*smp_regs));
        if (smp_regs == NULL) {
            stat = SCPE_MEM;
            break;
            }
        rem->smp_regs = smp_regs;
        smp_regs[rem->smp_reg_count].reg = reg;
        smp_regs[rem->smp_reg_count].idx = idx;
        smp_regs[rem->smp_reg_count].dptr = sim_dfdev;
        smp_regs[rem->smp_reg_count].uptr = sim_dfunit;
        smp_regs[rem->smp_reg_count].indirect = indirect;
        width = indirect ? sim_dfdev->dwidth : reg->width;
        smp_regs[rem->smp_reg_count].width = width;
        smp_regs[rem->smp_reg_count].bits = (BITSAMPLE *)calloc (width, sizeof (*smp_regs[rem->smp_reg_count - 1].bits));
        if (smp_regs[rem->smp_reg_count].bits == NULL) {
1163
1164
1165
1166
1167
1168
1169


1170

1171
1172
1173
1174
1175
1176
1177
1215
1216
1217
1218
1219
1220
1221
1222
1223

1224
1225
1226
1227
1228
1229
1230
1231







+
+
-
+







        }
    if (stat != SCPE_OK) {                      /* Error? */
        *iptr = cptr;
        cptr = strcpy (gbuf, "STOP");
        sim_rem_collect_cmd_setup (line, &cptr);/* Cleanup mess */
        return stat;
        }
    if (rem->smp_sample_dither_pct)
        event_time = (((rand() % (2 * rem->smp_sample_dither_pct)) - rem->smp_sample_dither_pct) * event_time) / 100;
    sim_activate (&rem_con_smp_smpl_units[rem->line], rem->smp_sample_interval);
    sim_activate (&rem_con_smp_smpl_units[rem->line], event_time);
    }
*iptr = cptr;
return stat;
}

t_stat sim_rem_con_repeat_svc (UNIT *uptr)
{
1205
1206
1207
1208
1209
1210
1211
1212

1213
1214
1215
1216
1217
1218
1219
1259
1260
1261
1262
1263
1264
1265

1266
1267
1268
1269
1270
1271
1272
1273







-
+







for (i = 0; i < bit->depth; i++)    /* set all value bits */
    bit->vals[i] = val;
}

static void sim_rem_collect_reg_bits (BITSAMPLE_REG *reg)
{
uint32 i;
t_value val = get_rval (reg->reg, 0);
t_value val = get_rval (reg->reg, reg->idx);

if (reg->indirect)
    val = get_aval ((t_addr)val, reg->dptr, reg->uptr);
val = val >> reg->reg->offset;
for (i = 0; i < reg->width; i++) {
    if (sim_is_running)
        sim_rem_record_reg_bit (&reg->bits[i], val&1);
1240
1241
1242
1243
1244
1245
1246
1247

1248




1249
1250

1251
1252
1253
1254
1255
1256
1257
1294
1295
1296
1297
1298
1299
1300

1301
1302
1303
1304
1305
1306
1307

1308
1309
1310
1311
1312
1313
1314
1315







-
+

+
+
+
+

-
+







}

t_stat sim_rem_con_smp_collect_svc (UNIT *uptr)
{
int line = uptr - rem_con_smp_smpl_units;
REMOTE *rem = &sim_rem_consoles[line];

sim_debug (DBG_SAM, &sim_remote_console, "sim_rem_con_smp_collect_svc(line=%d) - interval=%d\n", line, rem->smp_sample_interval);
sim_debug (DBG_SAM, &sim_remote_console, "sim_rem_con_smp_collect_svc(line=%d) - interval=%d, dither=%d%%\n", line, rem->smp_sample_interval, rem->smp_sample_dither_pct);
if (rem->smp_sample_interval && (rem->smp_reg_count != 0)) {
    int32 event_time = rem->smp_sample_interval;

    if (rem->smp_sample_dither_pct)
        event_time = (((rand() % (2 * rem->smp_sample_dither_pct)) - rem->smp_sample_dither_pct) * event_time) / 100;
    sim_rem_collect_registers (rem);
    sim_activate (uptr, rem->smp_sample_interval);        /* reschedule */
    sim_activate (uptr, event_time);                    /* reschedule */
    }
return SCPE_OK;
}

/* Unit service for remote console data polling */

t_stat sim_rem_con_data_svc (UNIT *uptr)
1345
1346
1347
1348
1349
1350
1351
1352

1353
1354
1355
1356
1357
1358
1359
1403
1404
1405
1406
1407
1408
1409

1410
1411
1412
1413
1414
1415
1416
1417







-
+







                        tmxr_linemsgf (lpj, "\nRemote Console %d(%s) Entering Commands\n", i, lp->ipad);
                        tmxr_send_buffered_data (lpj);  /* flush any buffered data */
                        }
                    lp = &sim_rem_con_tmxr.ldsc[i];
                    if (!master_session)
                        tmxr_linemsg (lp, "\r\nSimulator paused.\r\n");
                    if (!master_session && rem->read_timeout) {
                        tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", sim_rem_read_timeouts[i]);
                        tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", rem->read_timeout);
                        tmxr_linemsgf (lp, "\r\n");
                        tmxr_send_buffered_data (lp);   /* flush any buffered data */
                        }
                    }
                else {
                    if ((rem->buf_ptr == 0) &&          /* At beginning of input line */
                        ((c == '\n') ||                 /* Ignore bare LF between commands (Microsoft Telnet bug) */
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
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







+









+


+
+
+
+
+








if (sim_rem_master_mode) {
    t_stat stat_nomessage;

    sim_printf ("Command input starting on Master Remote Console Session\n");
    stat = sim_run_boot_prep (0);
    sim_rem_master_was_enabled = TRUE;
    sim_last_cmd_stat = SCPE_OK;
    while (sim_rem_master_mode) {
        sim_rem_consoles[0].single_mode = FALSE;
        sim_cancel (rem_con_data_unit);
        sim_activate (rem_con_data_unit, -1);
        stat = run_cmd (RU_GO, "");
        if (stat != SCPE_TTMO) {
            stat_nomessage = stat & SCPE_NOMESSAGE;         /* extract possible message supression flag */
            stat = _sim_rem_message ("RUN", stat);
            }
        sim_debug (DBG_MOD, &sim_remote_console, "Master Session Returned: Status - %d Active_Line: %d, Mode: %s, Active Cmd: %s\n", stat, sim_rem_cmd_active_line, sim_rem_consoles[0].single_mode ? "Single" : "^E Stopped", sim_rem_active_command ? sim_rem_active_command->name : "");
        if (stat == SCPE_EXIT)
            sim_rem_master_mode = FALSE;
        sim_rem_cmd_active_line = 0;                    /* Make it look like */
        sim_rem_consoles[0].single_mode = FALSE;
        if (stat != SCPE_STEP)
            sim_rem_active_command = &allowed_single_remote_cmds[0];/* Dummy */
        sim_last_cmd_stat = SCPE_BARE_STATUS(stat);     /* make exit status available to remote console */
        }
    sim_rem_master_was_enabled = FALSE;
    sim_rem_master_was_connected = FALSE;
    if (sim_log_temp) {                                     /* If we setup a temporary log, clean it now  */
        int32 save_quiet = sim_quiet;

        sim_quiet = 1;
2202
2203
2204
2205
2206
2207
2208
2209

2210
2211
2212
2213
2214
2215
2216
2217

2218
2219
2220
2221
2222
2223
2224
2267
2268
2269
2270
2271
2272
2273

2274
2275
2276
2277
2278
2279
2280
2281

2282
2283
2284
2285
2286
2287
2288
2289







-
+







-
+







        fprintf (st, "   Debug messages contain current PC value\n");
    if (sim_deb_switches & SWMASK ('T'))
        fprintf (st, "   Debug messages display time of day as hh:mm:ss.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : "");
    if (sim_deb_switches & SWMASK ('A'))
        fprintf (st, "   Debug messages display time of day as seconds.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : "");
    for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
        if (!(dptr->flags & DEV_DIS) &&
            (dptr->flags & DEV_DEBUG) &&
            ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) &&
            (dptr->dctrl)) {
            fprintf (st, "Device: %-6s ", dptr->name);
            show_dev_debug (st, dptr, NULL, 0, NULL);
            }
        }
    for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
        if (!(dptr->flags & DEV_DIS) &&
            (dptr->flags & DEV_DEBUG) &&
            ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) &&
            (dptr->dctrl)) {
            fprintf (st, "Device: %-6s ", dptr->name);
            show_dev_debug (st, dptr, NULL, 0, NULL);
            }
        }
    }
else
2429
2430
2431
2432
2433
2434
2435

2436
2437
2438
2439
2440
2441
2442
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508







+







return tmxr_close_master (&sim_con_tmxr);               /* close master socket */
}

/* Show the console expect rules and state */

t_stat sim_show_cons_expect (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr)
{
fprintf (st, "Console Expect processing:\n");
return sim_exp_show (st, &sim_con_expect, cptr);
}

/* Log File Open/Close/Show Support */

/* Open log file */

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







+















+
-
+
+
+







return &sim_con_expect;
}

/* Display console Queued input data status */

t_stat sim_show_cons_send_input (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
fprintf (st, "Console Send processing:\n");
return sim_show_send_input (st, &sim_con_send);
}

/* Poll for character */

t_stat sim_poll_kbd (void)
{
t_stat c;

if (sim_send_poll_data (&sim_con_send, &c))                 /* injected input characters available? */
    return c;
if (!sim_rem_master_mode) {
    if ((sim_con_ldsc.rxbps) &&                             /* rate limiting && */
        (sim_gtime () < sim_con_ldsc.rxnexttime))           /* too soon? */
        return SCPE_OK;                                     /* not yet */
    if (sim_ttisatty ())
    c = sim_os_poll_kbd ();                                 /* get character */
        c = sim_os_poll_kbd ();                             /* get character */
    else
        c = SCPE_OK;
    if (c == SCPE_STOP) {                                   /* ^E */
        stop_cpu = 1;                                       /* Force a stop (which is picked up by sim_process_event */
        return SCPE_OK;
        }
    if ((sim_con_tmxr.master == 0) &&                       /* not Telnet? */
        (sim_con_ldsc.serport == 0)) {                      /* and not serial? */
        if (c && sim_con_ldsc.rxbps)                        /* got something && rate limiting? */
2978
2979
2980
2981
2982
2983
2984



2985


2986
2987
2988
2989
2990
2991
2992
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057

3058
3059
3060
3061
3062
3063
3064
3065
3066







+
+
+
-
+
+







if (r1 != SCPE_OK)
    return r1;
return r2;
}

t_bool sim_ttisatty (void)
{
static int answer = -1;

if (answer == -1)
return sim_os_ttisatty ();
    answer = sim_os_ttisatty ();
return (t_bool)answer;
}


/* Platform specific routine definitions */

/* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */

3214
3215
3216
3217
3218
3219
3220

3221

3222
3223
3224

3225
3226
3227

3228
3229
3230
3231
3232


3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248

3249

3250
3251
3252
3253
3254
3255
3256
3288
3289
3290
3291
3292
3293
3294
3295

3296
3297
3298

3299
3300
3301

3302
3303
3304
3305
3306

3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325

3326
3327
3328
3329
3330
3331
3332
3333







+
-
+


-
+


-
+




-
+
+
















+
-
+







    (std_output != INVALID_HANDLE_VALUE))
    GetConsoleMode (std_output, &saved_output_mode);    /* Save Output Mode */
return SCPE_OK;
}

static t_stat sim_os_ttrun (void)
{
if ((sim_ttisatty ()) &&
if ((std_input) &&                                      /* If Not Background process? */
    (std_input) &&                                      /* If Not Background process? */
    (std_input != INVALID_HANDLE_VALUE)) {
    if (!GetConsoleMode(std_input, &saved_input_mode))
        return SCPE_TTYERR;
        return sim_messagef (SCPE_TTYERR, "GetConsoleMode() error: 0x%X\n", (unsigned int)GetLastError ());
    if ((!SetConsoleMode(std_input, ENABLE_VIRTUAL_TERMINAL_INPUT)) &&
        (!SetConsoleMode(std_input, RAW_MODE)))
        return SCPE_TTYERR;
        return sim_messagef (SCPE_TTYERR, "SetConsoleMode() error: 0x%X\n", (unsigned int)GetLastError ());
    }
if ((std_output) &&                                     /* If Not Background process? */
    (std_output != INVALID_HANDLE_VALUE)) {
    if (GetConsoleMode(std_output, &saved_output_mode))
        SetConsoleMode(std_output, ENABLE_VIRTUAL_TERMINAL_PROCESSING|ENABLE_PROCESSED_OUTPUT);
        if (!SetConsoleMode(std_output, ENABLE_VIRTUAL_TERMINAL_PROCESSING|ENABLE_PROCESSED_OUTPUT))
            SetConsoleMode(std_output, ENABLE_PROCESSED_OUTPUT);
    }
if (sim_log) {
    fflush (sim_log);
    _setmode (_fileno (sim_log), _O_BINARY);
    }
sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL);
return SCPE_OK;
}

static t_stat sim_os_ttcmd (void)
{
if (sim_log) {
    fflush (sim_log);
    _setmode (_fileno (sim_log), _O_TEXT);
    }
sim_os_set_thread_priority (PRIORITY_NORMAL);
if ((sim_ttisatty ()) &&
if ((std_input) &&                                      /* If Not Background process? */
    (std_input) &&                                      /* If Not Background process? */
    (std_input != INVALID_HANDLE_VALUE) &&
    (!SetConsoleMode(std_input, saved_input_mode)))     /* Restore Normal mode */
    return SCPE_TTYERR;
if ((std_output) &&                                     /* If Not Background process? */
    (std_output != INVALID_HANDLE_VALUE) &&
    (!SetConsoleMode(std_output, saved_output_mode)))   /* Restore Normal mode */
    return SCPE_TTYERR;
3594
3595
3596
3597
3598
3599
3600
3601
3602


3603
3604
3605
3606
3607
3608
3609
3671
3672
3673
3674
3675
3676
3677


3678
3679
3680
3681
3682
3683
3684
3685
3686







-
-
+
+







    SIOUXSettings.asktosaveonclose = FALSE;
    SIOUXSettings.showstatusline = FALSE;
    SIOUXSettings.columns = 80;
    SIOUXSettings.rows = 40;
    SIOUXSettings.toppixel = 42;
    SIOUXSettings.leftpixel     = 6;
    iBeamCursorH = GetCursor(iBeamCursor);
    sim_strlcat(title, sim_name, sizeof(title));
    sim_strlcat(title, " Simulator", sizeof(title));
    strlcat(title, sim_name, sizeof(title));
    strlcat(title, " Simulator", sizeof(title));
    title[0] = strlen(title) - 1;                       /* Pascal string done */
    for (i = 0; i <= title[0]; i++) {                   /* copy to unsigned char */
        ptitle[i] = title[i];
    }
    SIOUXSetTitle(ptitle);
    return SCPE_OK;
}
3879
3880
3881
3882
3883
3884
3885
3886

3887
3888
3889
3890
3891
3892
3893
3956
3957
3958
3959
3960
3961
3962

3963
3964
3965
3966
3967
3968
3969
3970







-
+







}

static t_bool sim_os_poll_kbd_ready (int ms_timeout)
{
fd_set readfds;
struct timeval timeout;

if (!sim_os_ttisatty()) {                   /* skip if !tty */
if (!sim_ttisatty()) {                      /* skip if !tty */
    sim_os_ms_sleep (ms_timeout);
    return FALSE;
    }
FD_ZERO (&readfds);
FD_SET (0, &readfds);
timeout.tv_sec = (ms_timeout*1000)/1000000;
timeout.tv_usec = (ms_timeout*1000)%1000000;
3953
3954
3955
3956
3957
3958
3959

3960

3961
3962
3963
3964
3965
3966
3967
4030
4031
4032
4033
4034
4035
4036
4037

4038
4039
4040
4041
4042
4043
4044
4045







+
-
+








    mbuf2 = (char *)malloc (3 + strlen(cptr));
    sprintf (mbuf2, "%s%s%s", (sim_switches & SWMASK ('A')) ? "\n" : "",
                              mbuf, 
                              (sim_switches & SWMASK ('I')) ? "" : "\n");
    free (mbuf);
    mbuf = sim_encode_quoted_string ((uint8 *)mbuf2, strlen (mbuf2));
    sim_switches = EXP_TYP_PERSIST;
    sim_exp_set (&sim_con_expect, mbuf, 0, sim_con_expect.after, EXP_TYP_PERSIST, NULL);
    sim_set_expect (&sim_con_expect, mbuf);
    free (mbuf);
    free (mbuf2);
    }

return SCPE_OK;
}

3976
3977
3978
3979
3980
3981
3982
3983

3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001


4002
4003
4004



4005
4006
4007
4054
4055
4056
4057
4058
4059
4060

4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082


4083
4084
4085
4086
4087
4088







-
+


















+
+

-
-
+
+
+



    uint8 *rbuf;

    if (cptr == NULL || *cptr == 0)
        return SCPE_2FARG;                              /* need arg */

    rbuf = (uint8 *)malloc (1 + strlen(cptr));

    decode ((char *)rbuf, cptr);                        /* decod string */
    decode ((char *)rbuf, cptr);                        /* decode string */
    sim_send_input (&sim_con_send, rbuf, strlen((char *)rbuf), 0, 0); /* queue it for output */
    free (rbuf);
    }

return SCPE_OK;
}

/* Set console delay */

static t_stat sim_set_delay (int32 flag, CONST char *cptr)
{
int32 val;
t_stat r;

if (cptr == NULL || *cptr == 0)                         /* no argument string? */
    return SCPE_2FARG;                                  /* need an argument */

val = (int32) get_uint (cptr, 10, INT_MAX, &r);         /* parse the argument */
if (r == SCPE_OK) {                                     /* parse OK? */
    char gbuf[CBUFSIZE];

if (r == SCPE_OK)                                       /* parse OK? */
    sim_con_expect.after = val;                         /* save the delay value */
    snprintf (gbuf, sizeof (gbuf), "HALTAFTER=%d", val);
    expect_cmd (1, gbuf);
    }

return r;
}
Changes to src/sim_defs.h.
104
105
106
107
108
109
110

111
112
113
114
115
116




117
118
119
120

121
122
123
124
125
126
127
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







+






+
+
+
+




+







        print_sym               print symbolic output
        parse_sym               parse symbolic input
*/

#ifndef SIM_DEFS_H_
#define SIM_DEFS_H_    0

#include "sim_rev.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#if defined(_MSC_VER) && (_MSC_VER < 1900)
#define snprintf _snprintf      /* poor man's snprintf which will work most of the time but has different return value */
#endif
#if defined(__VAX)
extern int sim_vax_snprintf(char *buf, size_t buf_size, const char *fmt, ...);
#define snprintf sim_vax_snprintf
#endif
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>

#ifdef _WIN32
#include <winsock2.h>
#undef PACKED                       /* avoid macro name collision */
#undef ERROR                        /* avoid macro name collision */
#undef MEM_MAPPED                   /* avoid macro name collision */
#include <process.h>
175
176
177
178
179
180
181

182
183
184
185
186
187
188
189
190
191



















192
193
194
195
196
197
198
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







+


-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







*/
#ifndef CONST
#define CONST const
#endif

/* Length specific integer declarations */

/* Handle the special/unusual cases first with everything else leveraging stdints.h */
#if defined (VMS)
#include <ints.h>
#else
typedef signed char     int8;
typedef signed short    int16;
typedef signed int      int32;
typedef unsigned char   uint8;
typedef unsigned short  uint16;
typedef unsigned int    uint32;
#endif
#elif defined(_MSC_VER) && (_MSC_VER < 1600)
typedef __int8           int8;
typedef __int16          int16;
typedef __int32          int32;
typedef unsigned __int8  uint8;
typedef unsigned __int16 uint16;
typedef unsigned __int32 uint32;
#else                                                   
/* All modern/standard compiler environments */
/* any other environment needa a special case above */
#include <stdint.h>
typedef int8_t          int8;
typedef int16_t         int16;
typedef int32_t         int32;
typedef uint8_t         uint8;
typedef uint16_t        uint16;
typedef uint32_t        uint32;
#endif                                                  /* end standard integers */

typedef int             t_stat;                         /* status */
typedef int             t_bool;                         /* boolean */

/* 64b integers */

#if defined (__GNUC__)                                  /* GCC */
typedef signed long long        t_int64;
213
214
215
216
217
218
219

220
221
222

223
224
225
226
227
228
229
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249







+



+







#ifndef INT64_C
#define INT64_C(x)      x ## LL
#endif

#if defined (USE_INT64)                                 /* 64b data */
typedef t_int64         t_svalue;                       /* signed value */
typedef t_uint64        t_value;                        /* value */
#define T_VALUE_MAX     0xffffffffffffffffuLL
#else                                                   /* 32b data */
typedef int32           t_svalue;
typedef uint32          t_value;
#define T_VALUE_MAX     0xffffffffUL
#endif                                                  /* end 64b data */

#if defined (USE_INT64) && defined (USE_ADDR64)         /* 64b address */
typedef t_uint64        t_addr;
#define T_ADDR_W        64
#define T_ADDR_FMT      LL_FMT
#else                                                   /* 32b address */
405
406
407
408
409
410
411
412

413
414
415
416
417
418
419
425
426
427
428
429
430
431

432
433
434
435
436
437
438
439







-
+








/* Convert switch letter to bit mask */

#define SWMASK(x) (1u << (((int) (x)) - ((int) 'A')))

/* String match - at least one character required */

#define MATCH_CMD(ptr,cmd) ((NULL == (ptr)) || (!*(ptr)) || sim_strncasecmp ((ptr), (cmd), strlen (ptr)))
#define MATCH_CMD(ptr,cmd) ((NULL == (ptr)) || (!*(ptr)) || strncasecmp ((ptr), (cmd), strlen (ptr)))

/* End of Linked List/Queue value                           */
/* Chosen for 2 reasons:                                    */
/*     1 - to not be NULL, this allowing the NULL value to  */
/*         indicate inclusion on a list                     */
/* and                                                      */
/*     2 - to not be a valid/possible pointer (alignment)   */
630
631
632
633
634
635
636
637
638
639


640
641
642
643
644
645
646
650
651
652
653
654
655
656

657
658
659
660
661
662
663
664
665
666
667







-


+
+







    void                *loc;                           /* location */
    uint32              radix;                          /* radix */
    uint32              width;                          /* width */
    uint32              offset;                         /* starting bit */
    uint32              depth;                          /* save depth */
    const char          *desc;                          /* description */
    BITFIELD            *fields;                        /* bit fields */
    uint32              flags;                          /* flags */
    uint32              qptr;                           /* circ q ptr */
    size_t              str_size;                       /* structure size */
    /* NOTE: Flags MUST always be last since it is initialized outside of macro definitions */
    uint32              flags;                          /* flags */
    };

/* Register flags */

#define REG_FMT         00003                           /* see PV_x */
#define REG_RO          00004                           /* read only */
#define REG_HIDDEN      00010                           /* hidden */
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
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







+



















-



+







/* Expect rule */

struct EXPTAB {
    uint8               *match;                         /* match string */
    uint32              size;                           /* match string size */
    char                *match_pattern;                 /* match pattern for format */
    int32               cnt;                            /* proceed count */
    uint32              after;                          /* delay before halting */
    int32               switches;                       /* flags */
#define EXP_TYP_PERSIST         (SWMASK ('P'))      /* rule persists after match, default is once a rule matches, it is removed */
#define EXP_TYP_CLEARALL        (SWMASK ('C'))      /* clear all rules after matching this rule, default is to once a rule matches, it is removed */
#define EXP_TYP_REGEX           (SWMASK ('R'))      /* rule pattern is a regular expression */
#define EXP_TYP_REGEX_I         (SWMASK ('I'))      /* regular expression pattern matching should be case independent */
#define EXP_TYP_TIME            (SWMASK ('T'))      /* halt delay is in microseconds instead of instructions */
#if defined(USE_REGEX)
    regex_t             regex;                          /* compiled regular expression */
#endif
    char                *act;                           /* action string */
    };

/* Expect Context */

struct EXPECT {
    DEVICE              *dptr;                          /* Device (for Debug) */
    uint32              dbit;                           /* Debugging Bit */
    EXPTAB              *rules;                         /* match rules */
    int32               size;                           /* count of match rules */
    uint32              after;                          /* delay before halting */
    uint8               *buf;                           /* buffer of output data which has produced */
    uint32              buf_ins;                        /* buffer insertion point for the next output data */
    uint32              buf_size;                       /* buffer size */
    uint32              buf_data;                       /* count of data in buffer */
    };

/* Send Context */

struct SEND {
    uint32              delay;                          /* instruction delay between sent data */
#define SEND_DEFAULT_DELAY  1000                        /* default delay instruction count */
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
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







+
+
+
+




-
+

-
+
+
+

-
+
+
+
+
+
+

-
+
+
+
+
+
+

-
+
+
+
+
+
+

-
+
+
+
+
+
+

-
+
+
+
+
+
+

-
+
+
+
+
+
+

-
+
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-








   They are dependent on the declaration order of the fields 
   of the structures they exist to populate.

 */

#define UDATA(act,fl,cap) NULL,act,NULL,NULL,NULL,0,0,(fl),0,(cap),0,NULL,0,0

/* Internal use ONLY (see below) Generic Register declaration for all fields */
#define _REGDATANF(nm,loc,rdx,wd,off,dep,desc,flds,qptr,siz) \
    nm, (loc), (rdx), (wd), (off), (dep), (desc), (flds), (qptr), (siz)

#if defined (__STDC__) || defined (_WIN32) /* Variants which depend on how macro arguments are convered to strings */
/* Generic Register declaration for all fields.  
   If the register structure is extended, this macro will be retained and a 
   new macro will be provided that populates the new register structure */
   new internal macro will be provided that populates the new register structure */
#define REGDATA(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \
    #nm, &(loc), (rdx), (wd), (off), (dep), (desc), (flds), (fl), (qptr), (siz)
    _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,flds,qptr,siz),(fl)
#define REGDATAC(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \
    _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,flds,qptr,siz),(fl)
/* Right Justified Octal Register Data */
#define ORDATA(nm,loc,wd) #nm, &(loc), 8, (wd), 0, 1, NULL, NULL
#define ORDATA(nm,loc,wd) \
    _REGDATANF(#nm,&(loc),8,wd,0,1,NULL,NULL,0,0)
#define ORDATAD(nm,loc,wd,desc) \
    _REGDATANF(#nm,&(loc),8,wd,0,1,desc,NULL,0,0)
#define ORDATADF(nm,loc,wd,desc,flds) \
    _REGDATANF(#nm,&(loc),8,wd,0,1,desc,flds,0,0)
/* Right Justified Decimal Register Data */
#define DRDATA(nm,loc,wd) #nm, &(loc), 10, (wd), 0, 1, NULL, NULL
#define DRDATA(nm,loc,wd) \
    _REGDATANF(#nm,&(loc),10,wd,0,1,NULL,NULL,0,0)
#define DRDATAD(nm,loc,wd,desc) \
    _REGDATANF(#nm,&(loc),10,wd,0,1,desc,NULL,0,0)
#define DRDATADF(nm,loc,wd,desc,flds) \
    _REGDATANF(#nm,&(loc),10,wd,0,1,desc,flds,0,0)
/* Right Justified Hexadecimal Register Data */
#define HRDATA(nm,loc,wd) #nm, &(loc), 16, (wd), 0, 1, NULL, NULL
#define HRDATA(nm,loc,wd) \
    _REGDATANF(#nm,&(loc),16,wd,0,1,NULL,NULL,0,0)
#define HRDATAD(nm,loc,wd,desc) \
    _REGDATANF(#nm,&(loc),16,wd,0,1,desc,NULL,0,0)
#define HRDATADF(nm,loc,wd,desc,flds) \
    _REGDATANF(#nm,&(loc),16,wd,0,1,desc,flds,0,0)
/* Right Justified Binary Register Data */
#define BINRDATA(nm,loc,wd) #nm, &(loc), 2, (wd), 0, 1, NULL, NULL
#define BINRDATA(nm,loc,wd) \
    _REGDATANF(#nm,&(loc),2,wd,0,1,NULL,NULL,0,0)
#define BINRDATAD(nm,loc,wd,desc) \
    _REGDATANF(#nm,&(loc),2,wd,0,1,desc,NULL,0,0)
#define BINRDATADF(nm,loc,wd,desc,flds) \
    _REGDATANF(#nm,&(loc),2,wd,0,1,desc,flds,0,0)
/* One-bit binary flag at an arbitrary offset in a 32-bit word Register */
#define FLDATA(nm,loc,pos) #nm, &(loc), 2, 1, (pos), 1, NULL, NULL
#define FLDATA(nm,loc,pos) \
    _REGDATANF(#nm,&(loc),2,1,pos,1,NULL,NULL,0,0)
#define FLDATAD(nm,loc,pos,desc) \
    _REGDATANF(#nm,&(loc),2,1,pos,1,desc,NULL,0,0)
#define FLDATADF(nm,loc,pos,desc,flds) \
    _REGDATANF(#nm,&(loc),2,1,pos,1,desc,flds,0,0)
/* Arbitrary location and Radix Register */
#define GRDATA(nm,loc,rdx,wd,pos) #nm, &(loc), (rdx), (wd), (pos), 1, NULL, NULL
#define GRDATA(nm,loc,rdx,wd,pos) \
    _REGDATANF(#nm,&(loc),rdx,wd,pos,1,NULL,NULL,0,0)
#define GRDATAD(nm,loc,rdx,wd,pos,desc) \
    _REGDATANF(#nm,&(loc),rdx,wd,pos,1,desc,NULL,0,0)
#define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) \
    _REGDATANF(#nm,&(loc),rdx,wd,pos,1,desc,flds,0,0)
/* Arrayed register whose data is kept in a standard C array Register */
#define BRDATA(nm,loc,rdx,wd,dep) #nm, (loc), (rdx), (wd), 0, (dep), NULL, NULL
#define BRDATA(nm,loc,rdx,wd,dep) \
/* Same as above, but with additional description initializer */
#define ORDATAD(nm,loc,wd,desc) #nm, &(loc), 8, (wd), 0, 1, (desc), NULL
#define DRDATAD(nm,loc,wd,desc) #nm, &(loc), 10, (wd), 0, 1, (desc), NULL
#define HRDATAD(nm,loc,wd,desc) #nm, &(loc), 16, (wd), 0, 1, (desc), NULL
#define BINRDATAD(nm,loc,wd,desc) #nm, &(loc), 2, (wd), 0, 1, (desc), NULL
#define FLDATAD(nm,loc,pos,desc) #nm, &(loc), 2, 1, (pos), 1, (desc), NULL
#define GRDATAD(nm,loc,rdx,wd,pos,desc) #nm, &(loc), (rdx), (wd), (pos), 1, (desc), NULL
#define BRDATAD(nm,loc,rdx,wd,dep,desc) #nm, (loc), (rdx), (wd), 0, (dep), (desc), NULL
    _REGDATANF(#nm,loc,rdx,wd,0,dep,NULL,NULL,0,0)
#define BRDATAD(nm,loc,rdx,wd,dep,desc) \
/* Same as above, but with additional description initializer, and bitfields */
#define ORDATADF(nm,loc,wd,desc,flds) #nm, &(loc), 8, (wd), 0, 1, (desc), (flds)
#define DRDATADF(nm,loc,wd,desc,flds) #nm, &(loc), 10, (wd), 0, 1, (desc), (flds)
#define HRDATADF(nm,loc,wd,desc,flds) #nm, &(loc), 16, (wd), 0, 1, (desc), (flds)
#define BINRDATADF(nm,loc,wd) #nm, &(loc), 2, (wd), 0, 1, NULL, NULL
#define FLDATADF(nm,loc,pos,desc,flds) #nm, &(loc), 2, 1, (pos), 1, (desc), (flds)
#define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) #nm, &(loc), (rdx), (wd), (pos), 1, (desc), (flds)
#define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) #nm, (loc), (rdx), (wd), 0, (dep), (desc), (flds)
    _REGDATANF(#nm,loc,rdx,wd,0,dep,desc,NULL,0,0)
#define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) \
    _REGDATANF(#nm,loc,rdx,wd,0,dep,desc,flds,0,0)
/* Arrayed register whose data is part of the UNIT structure */
#define URDATA(nm,loc,rdx,wd,off,dep,fl) \
    _REGDATANF(#nm,&(loc),rdx,wd,off,dep,NULL,NULL,0,0),((fl) | REG_UNIT)
#define URDATAD(nm,loc,rdx,wd,off,dep,fl,desc) \
    _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,NULL,0,0),((fl) | REG_UNIT)
#define URDATADF(nm,loc,rdx,wd,off,dep,fl,desc,flds) \
    _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,flds,0,0),((fl) | REG_UNIT)
/* Arrayed register whose data is part of an arbitrary structure */
#define STRDATA(nm,loc,rdx,wd,off,dep,siz,fl) \
    _REGDATANF(#nm,&(loc),rdx,wd,off,dep,NULL,NULL,0,siz),((fl) | REG_STRUCT)
#define STRDATAD(nm,loc,rdx,wd,off,dep,siz,fl,desc) \
    _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,NULL,0,siz),((fl) | REG_STRUCT)
#define STRDATADF(nm,loc,rdx,wd,off,dep,siz,fl,desc,flds) \
    _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,flds,0,siz),((fl) | REG_STRUCT)
#define BIT(nm)              {#nm, 0xffffffff, 1}             /* Single Bit definition */
#define BITNC                {"",  0xffffffff, 1}             /* Don't care Bit definition */
#define BITF(nm,sz)          {#nm, 0xffffffff, sz}            /* Bit Field definition */
#define BITNCF(sz)           {"",  0xffffffff, sz}            /* Don't care Bit Field definition */
#define BITFFMT(nm,sz,fmt)   {#nm, 0xffffffff, sz, NULL, #fmt}/* Bit Field definition with Output format */
#define BITFNAM(nm,sz,names) {#nm, 0xffffffff, sz, names}     /* Bit Field definition with value->name map */
#else /* For non-STD-C compiler which can't stringify macro arguments with # */
/* Generic Register declaration for all fields.  
   If the register structure is extended, this macro will be retained and a 
   new macro will be provided that populates the new register structure */
#define REGDATA(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \
    "nm", &(loc), (rdx), (wd), (off), (dep), (desc), (flds), (fl), (qptr), (siz)
#define ORDATA(nm,loc,wd) "nm", &(loc), 8, (wd), 0, 1, NULL, NULL
#define DRDATA(nm,loc,wd) "nm", &(loc), 10, (wd), 0, 1, NULL, NULL
#define HRDATA(nm,loc,wd) "nm", &(loc), 16, (wd), 0, 1, NULL, NULL
#define BINRDATA(nm,loc,wd) "nm", &(loc), 2, (wd), 0, 1, NULL, NULL
#define FLDATA(nm,loc,pos) "nm", &(loc), 2, 1, (pos), 1, NULL, NULL
    _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,flds,qptr,siz),(fl)
#define REGDATAC(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \
    _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,flds,qptr,siz),(fl)
/* Right Justified Octal Register Data */
#define ORDATA(nm,loc,wd) \
    _REGDATANF("nm",&(loc),8,wd,0,1,NULL,NULL,0,0)
#define ORDATAD(nm,loc,wd,desc) \
    _REGDATANF("nm",&(loc),8,wd,0,1,desc,NULL,0,0)
#define ORDATADF(nm,loc,wd,desc,flds) \
    _REGDATANF("nm",&(loc),8,wd,0,1,desc,flds,0,0)
/* Right Justified Decimal Register Data */
#define DRDATA(nm,loc,wd) \
    _REGDATANF("nm",&(loc),10,wd,0,1,NULL,NULL,0,0)
#define DRDATAD(nm,loc,wd,desc) \
    _REGDATANF("nm",&(loc),10,wd,0,1,desc,NULL,0,0)
#define DRDATADF(nm,loc,wd,desc,flds) \
    _REGDATANF("nm",&(loc),10,wd,0,1,desc,flds,0,0)
/* Right Justified Hexadecimal Register Data */
#define HRDATA(nm,loc,wd) \
    _REGDATANF("nm",&(loc),16,wd,0,1,NULL,NULL,0,0)
#define HRDATAD(nm,loc,wd,desc) \
    _REGDATANF("nm",&(loc),16,wd,0,1,desc,NULL,0,0)
#define HRDATADF(nm,loc,wd,desc,flds) \
    _REGDATANF("nm",&(loc),16,wd,0,1,desc,flds,0,0)
/* Right Justified Binary Register Data */
#define BINRDATA(nm,loc,wd) \
    _REGDATANF("nm",&(loc),2,wd,0,1,NULL,NULL,0,0)
#define GRDATA(nm,loc,rdx,wd,pos) "nm", &(loc), (rdx), (wd), (pos), 1, NULL, NULL
#define BRDATA(nm,loc,rdx,wd,dep) "nm", (loc), (rdx), (wd), 0, (dep), NULL, NULL
#define ORDATAD(nm,loc,wd,desc) "nm", &(loc), 8, (wd), 0, 1, (desc), NULL
#define DRDATAD(nm,loc,wd,desc) "nm", &(loc), 10, (wd), 0, 1, (desc), NULL
#define HRDATAD(nm,loc,wd,desc) "nm", &(loc), 16, (wd), 0, 1, (desc), NULL
#define BINRDATAD(nm,loc,wd,desc) "nm", &(loc), 2, (wd), 0, 1, (desc), NULL
#define FLDATAD(nm,loc,pos,desc) "nm", &(loc), 2, 1, (pos), 1, (desc), NULL
#define GRDATAD(nm,loc,rdx,wd,pos,desc) "nm", &(loc), (rdx), (wd), (pos), 1, (desc), NULL
#define BRDATAD(nm,loc,rdx,wd,dep,desc) "nm", (loc), (rdx), (wd), 0, (dep), (desc), NULL
#define ORDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 8, (wd), 0, 1, (desc), (flds)
#define DRDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 10, (wd), 0, 1, (desc), (flds)
#define HRDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 16, (wd), 0, 1, (desc), (flds)
#define BINRDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 2, (wd), 0, 1, (desc), (flds)
#define FLDATADF(nm,loc,pos,desc,flds) "nm", &(loc), 2, 1, (pos), 1, (desc), (flds)
#define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) "nm", &(loc), (rdx), (wd), (pos), 1, (desc), (flds)
#define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) "nm", (loc), (rdx), (wd), 0, (dep), (desc), (flds)
#define BINRDATAD(nm,loc,wd,desc) \
    _REGDATANF("nm",&(loc),2,wd,0,1,desc,NULL,0,0)
#define BINRDATADF(nm,loc,wd,desc,flds) \
    _REGDATANF("nm",&(loc),2,wd,0,1,desc,flds,0,0)
/* One-bit binary flag at an arbitrary offset in a 32-bit word Register */
#define FLDATA(nm,loc,pos) \
    _REGDATANF("nm",&(loc),2,1,pos,1,NULL,NULL,0,0)
#define FLDATAD(nm,loc,pos,desc) \
    _REGDATANF("nm",&(loc),2,1,pos,1,desc,NULL,0,0)
#define FLDATADF(nm,loc,pos,desc,flds) \
    _REGDATANF("nm",&(loc),2,1,pos,1,desc,flds,0,0)
/* Arbitrary location and Radix Register */
#define GRDATA(nm,loc,rdx,wd,pos) \
    _REGDATANF("nm",&(loc),rdx,wd,pos,1,NULL,NULL,0,0)
#define GRDATAD(nm,loc,rdx,wd,pos,desc) \
    _REGDATANF("nm",&(loc),rdx,wd,pos,1,desc,NULL,0,0)
#define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) \
    _REGDATANF("nm",&(loc),rdx,wd,pos,1,desc,flds,0,0)
/* Arrayed register whose data is kept in a standard C array Register */
#define BRDATA(nm,loc,rdx,wd,dep) \
    _REGDATANF("nm",loc,rdx,wd,0,dep,NULL,NULL,0,0)
#define BRDATAD(nm,loc,rdx,wd,dep,desc) \
    _REGDATANF("nm",loc,rdx,wd,0,dep,desc,NULL,0,0)
#define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) \
    _REGDATANF("nm",loc,rdx,wd,0,dep,desc,flds,0,0)
/* Arrayed register whose data is part of the UNIT structure */
#define URDATA(nm,loc,rdx,wd,off,dep,fl) \
    _REGDATANF("nm",&(loc),rdx,wd,off,dep,NULL,NULL,0,0),((fl) | REG_UNIT)
#define URDATAD(nm,loc,rdx,wd,off,dep,fl,desc) \
    _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,NULL,0,0),((fl) | REG_UNIT)
#define URDATADF(nm,loc,rdx,wd,off,dep,fl,desc,flds) \
    _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,flds,0,0),((fl) | REG_UNIT)
/* Arrayed register whose data is part of an arbitrary structure */
#define STRDATA(nm,loc,rdx,wd,off,dep,siz,fl) \
    _REGDATANF("nm",&(loc),rdx,wd,off,dep,NULL,NULL,0,siz),((fl) | REG_STRUCT)
#define STRDATAD(nm,loc,rdx,wd,off,dep,siz,fl,desc) \
    _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,NULL,0,siz),((fl) | REG_STRUCT)
#define STRDATADF(nm,loc,rdx,wd,off,dep,siz,fl,desc,flds) \
    _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,flds,0,siz),((fl) | REG_STRUCT)
#define BIT(nm)              {"nm", 0xffffffff, 1}              /* Single Bit definition */
#define BITNC                {"",   0xffffffff, 1}              /* Don't care Bit definition */
#define BITF(nm,sz)          {"nm", 0xffffffff, sz}             /* Bit Field definition */
#define BITNCF(sz)           {"",   0xffffffff, sz}             /* Don't care Bit Field definition */
#define BITFFMT(nm,sz,fmt)   {"nm", 0xffffffff, sz, NULL, "fmt"}/* Bit Field definition with Output format */
#define BITFNAM(nm,sz,names) {"nm", 0xffffffff, sz, names}      /* Bit Field definition with value->name map */
#endif
#define ENDBITS {NULL}  /* end of bitfield list */

/* Arrayed register whose data is part of the UNIT structure */
#define URDATA(nm,loc,rdx,wd,off,dep,fl) \
    REGDATA(nm,(loc),(rdx),(wd),(off),(dep),NULL,NULL,((fl) | REG_UNIT),0,0)
/* Arrayed register whose data is part of an arbitrary structure */
#define STRDATA(nm,loc,rdx,wd,off,dep,siz,fl) \
    REGDATA(nm,(loc),(rdx),(wd),(off),(dep),NULL,NULL,((fl) | REG_STRUCT),0,(siz))
/* Same as above, but with additional description initializer */
#define URDATAD(nm,loc,rdx,wd,off,dep,fl,desc) \
    REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),NULL,((fl) | REG_UNIT),0,0)
#define STRDATAD(nm,loc,rdx,wd,off,dep,siz,fl,desc) \
    REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),NULL,((fl) | REG_STRUCT),0,(siz))
/* Same as above, but with additional description initializer, and bitfields */
#define URDATADF(nm,loc,rdx,wd,off,dep,fl,desc,flds) \
    REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),(flds),((fl) | REG_UNIT),0,0)
#define STRDATADF(nm,loc,rdx,wd,off,dep,siz,fl,desc,flds) \
    REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),(flds),((fl) | REG_STRUCT),0,(siz))

/* Function prototypes */

#include "scp.h"
#include "sim_console.h"
#include "sim_timer.h"
#include "sim_fio.h"
Changes to src/sim_disk.c.
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
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







-
-
+
-


















-
-
+















-
-
-
-
-
+
+
+
+
-










-
-
+







    /* First open the source of the copy operation */
    r = sim_disk_attach (uptr, cptr, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize, completion_delay);
    sim_quiet = saved_sim_quiet;
    if (r != SCPE_OK) {
        sim_switches = saved_sim_switches;
        return sim_messagef (r, "Can't open source VHD: %s\n", cptr);
        }
    if (!sim_quiet) {
        sim_printf ("%s%d: creating new virtual disk '%s'\n", sim_dname (dptr), (int)(uptr-dptr->units), gbuf);
    sim_messagef (SCPE_OK, "%s%d: creating new virtual disk '%s'\n", sim_dname (dptr), (int)(uptr-dptr->units), gbuf);
        }
    capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */
    vhd = sim_vhd_disk_create (gbuf, ((t_offset)uptr->capac)*capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1));
    if (!vhd) {
        return sim_messagef (r, "%s%d: can't create virtual disk '%s'\n", sim_dname (dptr), (int)(uptr-dptr->units), gbuf);
        }
    else {
        uint8 *copy_buf = (uint8*) malloc (1024*1024);
        t_lba lba;
        t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size);
        t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1)));
        t_seccnt sects = sectors_per_buffer;

        if (!copy_buf) {
            sim_vhd_disk_close(vhd);
            (void)remove (gbuf);
            return SCPE_MEM;
            }
        for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) {
            if (!sim_quiet)
                sim_printf ("%s%d: Copied %dMB.  %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors));
            sim_messagef (SCPE_OK, "%s%d: Copied %dMB.  %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors));
            sects = sectors_per_buffer;
            if (lba + sects > total_sectors)
                sects = total_sectors - lba;
            r = sim_disk_rdsect (uptr, lba, copy_buf, NULL, sects);
            if (r == SCPE_OK) {
                uint32 saved_unit_flags = uptr->flags;
                FILE *save_unit_fileref = uptr->fileref;

                sim_disk_set_fmt (uptr, 0, "VHD", NULL);
                uptr->fileref = vhd;
                r = sim_disk_wrsect (uptr, lba, copy_buf, NULL, sects);
                uptr->fileref = save_unit_fileref;
                uptr->flags = saved_unit_flags;
                }
            }
        if (!sim_quiet) {
            if (r == SCPE_OK)
                sim_printf ("\n%s%d: Copied %dMB. Done.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_offset)lba*sector_size)/1000000));
            else
                sim_printf ("\n%s%d: Error copying: %s.\n", sim_dname (dptr), (int)(uptr-dptr->units), sim_error_text (r));
        if (r == SCPE_OK)
            sim_messagef (SCPE_OK, "\n%s%d: Copied %dMB. Done.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_offset)lba*sector_size)/1000000));
        else
            sim_messagef (r, "\n%s%d: Error copying: %s.\n", sim_dname (dptr), (int)(uptr-dptr->units), sim_error_text (r));
            }
        if ((r == SCPE_OK) && (sim_switches & SWMASK ('V'))) {
            uint8 *verify_buf = (uint8*) malloc (1024*1024);

            if (!verify_buf) {
                sim_vhd_disk_close(vhd);
                (void)remove (gbuf);
                free (copy_buf);
                return SCPE_MEM;
                }
            for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) {
                if (!sim_quiet)
                    sim_printf ("%s%d: Verified %dMB.  %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors));
                sim_messagef (SCPE_OK, "%s%d: Verified %dMB.  %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors));
                sects = sectors_per_buffer;
                if (lba + sects > total_sectors)
                    sects = total_sectors - lba;
                r = sim_disk_rdsect (uptr, lba, copy_buf, NULL, sects);
                if (r == SCPE_OK) {
                    uint32 saved_unit_flags = uptr->flags;
                    FILE *save_unit_fileref = uptr->fileref;
1435
1436
1437
1438
1439
1440
1441
1442

1443
1444
1445
1446
1447
1448
1449
1429
1430
1431
1432
1433
1434
1435

1436
1437
1438
1439
1440
1441
1442
1443







-
+







                        if (0 != memcmp (copy_buf, verify_buf, 1024*1024))
                            r = SCPE_IOERR;
                        }
                    }
                }
            if (!sim_quiet) {
                if (r == SCPE_OK)
                    sim_printf ("\n%s%d: Verified %dMB. Done.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_offset)lba*sector_size)/1000000));
                    sim_messagef (r, "\n%s%d: Verified %dMB. Done.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_offset)lba*sector_size)/1000000));
                else {
                    t_lba i;
                    uint32 save_dctrl = dptr->dctrl;
                    FILE *save_sim_deb = sim_deb;

                    for (i = 0; i < (1024*1024/sector_size); ++i)
                        if (0 != memcmp (copy_buf+i*sector_size, verify_buf+i*sector_size, sector_size))
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
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







-
-
+
-











-
-
+










-
-
+







    if (((uptr->flags & UNIT_ROABLE) == 0) &&           /* allowed? */
        ((uptr->flags & UNIT_RO) == 0))
        return _err_return (uptr, SCPE_NORO);           /* no, error */
    uptr->fileref = open_function (cptr, "rb");         /* open rd only */
    if (uptr->fileref == NULL)                          /* open fail? */
        return _err_return (uptr, SCPE_OPENERR);        /* yes, error */
    uptr->flags = uptr->flags | UNIT_RO;                /* set rd only */
    if (!sim_quiet) {
        sim_printf ("%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units));
    sim_messagef (SCPE_OK, "%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units));
        }
    }
else {                                                  /* normal */
    uptr->fileref = open_function (cptr, "rb+");        /* open r/w */
    if (uptr->fileref == NULL) {                        /* open fail? */
        if ((errno == EROFS) || (errno == EACCES)) {    /* read only? */
            if ((uptr->flags & UNIT_ROABLE) == 0)       /* allowed? */
                return _err_return (uptr, SCPE_NORO);   /* no error */
            uptr->fileref = open_function (cptr, "rb"); /* open rd only */
            if (uptr->fileref == NULL)                  /* open fail? */
                return _err_return (uptr, SCPE_OPENERR);/* yes, error */
            uptr->flags = uptr->flags | UNIT_RO;        /* set rd only */
            if (!sim_quiet)
                sim_printf ("%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units));
            sim_messagef (SCPE_OK, "%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units));
            }
        else {                                          /* doesn't exist */
            if (sim_switches & SWMASK ('E'))            /* must exist? */
                return _err_return (uptr, SCPE_OPENERR); /* yes, error */
            if (create_function)
                uptr->fileref = create_function (cptr, ((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1));/* create new file */
            else
                uptr->fileref = open_function (cptr, "wb+");/* open new file */
            if (uptr->fileref == NULL)                  /* open fail? */
                return _err_return (uptr, SCPE_OPENERR);/* yes, error */
            if (!sim_quiet)
                sim_printf ("%s%d: creating new file\n", sim_dname (dptr), (int)(uptr-dptr->units));
            sim_messagef (SCPE_OK, "%s%d: creating new file\n", sim_dname (dptr), (int)(uptr-dptr->units));
            created = TRUE;
            }
        }                                               /* end if null */
    }                                                   /* end else */
if (DK_GET_FMT (uptr) == DKUF_F_VHD) {
    if ((created) && dtype)
        sim_vhd_disk_set_dtype (uptr->fileref, dtype);
1651
1652
1653
1654
1655
1656
1657
1658
1659

1660
1661
1662

1663
1664
1665
1666
1667
1668
1669
1641
1642
1643
1644
1645
1646
1647


1648
1649


1650
1651
1652
1653
1654
1655
1656
1657







-
-
+

-
-
+







            r = sim_disk_wrsect (uptr, lba, init_buf, NULL, sects);
            if (r != SCPE_OK) {
                free (init_buf);
                sim_disk_detach (uptr);                         /* report error now */
                (void)remove (cptr);                            /* remove the created file */
                return SCPE_OPENERR;
                }
            if (!sim_quiet)
                sim_printf ("%s%d: Initialized To Sector Address %dMB.  %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors));
            sim_messagef (SCPE_OK, "%s%d: Initialized To Sector Address %dMB.  %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors));
            }
        if (!sim_quiet)
            sim_printf ("%s%d: Initialized To Sector Address %dMB.  100%% complete.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000));
        sim_messagef (SCPE_OK, "%s%d: Initialized To Sector Address %dMB.  100%% complete.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000));
        free (init_buf);
        }
    if (pdp11tracksize)
        sim_disk_pdp11_bad_block (uptr, pdp11tracksize, sector_size/sizeof(uint16));
    }

if (sim_switches & SWMASK ('K')) {
1704
1705
1706
1707
1708
1709
1710
1711
1712

1713
1714
1715

1716
1717
1718
1719
1720
1721
1722
1692
1693
1694
1695
1696
1697
1698


1699
1700


1701
1702
1703
1704
1705
1706
1707
1708







-
-
+

-
-
+







                    sim_deb = stdout;
                    sim_disk_data_trace (uptr, verify_buf+sect*sector_size, lba+sect, sector_size,    "Found", TRUE, 1);
                    dptr->dctrl = save_dctrl;
                    sim_deb = save_sim_deb;
                    }
                }
            }
        if (!sim_quiet)
            sim_printf ("%s%d: Verified containing Sector Address %dMB.  %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors));
        sim_messagef (SCPE_OK, "%s%d: Verified containing Sector Address %dMB.  %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors));
        }
    if (!sim_quiet)
        sim_printf ("%s%d: Verified containing Sector Address %dMB.  100%% complete.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000));
    sim_messagef (SCPE_OK, "%s%d: Verified containing Sector Address %dMB.  100%% complete.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000));
    free (verify_buf);
    uptr->dynflags |= UNIT_DISK_CHK;
    }

filesystem_capac = get_filesystem_size (uptr);
capac = size_function (uptr->fileref);
if (capac && (capac != (t_offset)-1)) {
2153
2154
2155
2156
2157
2158
2159
2160

2161
2162
2163
2164
2165
2166
2167
2139
2140
2141
2142
2143
2144
2145

2146
2147
2148
2149
2150
2151
2152
2153







-
+







    }
if ((dwStatus >= ERROR_INVALID_STARTING_CODESEG) && (dwStatus <= ERROR_INFLOOP_IN_RELOC_CHAIN)) {
    errno = ENOEXEC;
    return;
    }
errno = EINVAL;
}
#if defined(__GNUC__)
#if defined(__GNUC__) && defined(HAVE_NTDDDISK_H)
#include <ddk/ntddstor.h>
#include <ddk/ntdddisk.h>
#else
#include <winioctl.h>
#endif

#if defined(__cplusplus)
3639
3640
3641
3642
3643
3644
3645
3646
3647

3648
3649
3650
3651
3652
3653
3654
3625
3626
3627
3628
3629
3630
3631


3632
3633
3634
3635
3636
3637
3638
3639







-
-
+







    BitMapBytes = (7+(NtoHl (hVHD->Dynamic.BlockSize)/SectorSize))/8;
    BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize;
    for (BlockNumber=BlocksToMerge=0; BlockNumber< NtoHl (hVHD->Dynamic.MaxTableEntries); ++BlockNumber) {
        if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY)
            continue;
        ++BlocksToMerge;
        }
    if (!sim_quiet)
        sim_printf ("Merging %s\ninto %s\n", szVHDPath, hVHD->ParentVHDPath);
    sim_messagef (SCPE_OK, "Merging %s\ninto %s\n", szVHDPath, hVHD->ParentVHDPath);
    for (BlockNumber=NeededBlock=0; BlockNumber < NtoHl (hVHD->Dynamic.MaxTableEntries); ++BlockNumber) {
        uint32 BlockSectors = SectorsPerBlock;

        if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY)
            continue;
        ++NeededBlock;
        BlockOffset = SectorSize*((uint64)(NtoHl (hVHD->BAT[BlockNumber]) + BitMapSectors));
3663
3664
3665
3666
3667
3668
3669
3670
3671

3672
3673
3674
3675
3676
3677
3678
3679
3680

3681
3682
3683
3684
3685
3686
3687
3648
3649
3650
3651
3652
3653
3654


3655
3656
3657
3658
3659
3660
3661
3662


3663
3664
3665
3666
3667
3668
3669
3670







-
-
+







-
-
+







        if (WriteVirtualDiskSectors (Parent,
                                     (uint8*)BlockData,
                                     BlockSectors,
                                     &SectorsWritten,
                                     SectorSize,
                                     SectorsPerBlock*BlockNumber))
            break;
        if (!sim_quiet)
            sim_printf ("Merged %dMB.  %d%% complete.\r", (int)((((float)NeededBlock)*SectorsPerBlock)*SectorSize/1000000), (int)((((float)NeededBlock)*100)/BlocksToMerge));
        sim_messagef (SCPE_OK, "Merged %dMB.  %d%% complete.\r", (int)((((float)NeededBlock)*SectorsPerBlock)*SectorSize/1000000), (int)((((float)NeededBlock)*100)/BlocksToMerge));
        hVHD->BAT[BlockNumber] = VHD_BAT_FREE_ENTRY;
        }
    if (BlockNumber < NtoHl (hVHD->Dynamic.MaxTableEntries)) {
        Status = errno;
        }
    else {
        Status = 0;
        if (!sim_quiet)
            sim_printf ("Merged %dMB.  100%% complete.\n", (int)((((float)NeededBlock)*SectorsPerBlock)*SectorSize/1000000));
        sim_messagef (SCPE_OK, "Merged %dMB.  100%% complete.\n", (int)((((float)NeededBlock)*SectorsPerBlock)*SectorSize/1000000));
        fclose (hVHD->File);
        hVHD->File = NULL;
        (void)remove (szVHDPath);
        *ParentVHD = (char*) malloc (strlen (hVHD->ParentVHDPath)+1);
        strcpy (*ParentVHD, hVHD->ParentVHDPath);
        }
Cleanup_Return:
Changes to src/sim_ether.c.
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
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







-
+



















-
+







  size_t n;
  int i, found;

  found = 0;
  n = strlen(name);
  for (i=0; i<count && !found; i++) {
    if ((n == strlen(list[i].name)) &&
        (sim_strncasecmp(name, list[i].name, n) == 0)) {
        (strncasecmp(name, list[i].name, n) == 0)) {
      found = 1;
      strcpy(temp, list[i].name); /* only case might be different */
      strcpy(desc, list[i].desc);
    }
  }
  return (found ? temp : NULL);
}

char* eth_getdesc_byname(char* name, char* temp)
{
  ETH_LIST  list[ETH_MAX_DEVICE];
  int count = eth_devices(ETH_MAX_DEVICE, list);
  size_t n;
  int i, found;

  found = 0;
  n = strlen(name);
  for (i=0; i<count && !found; i++) {
    if ((n == strlen(list[i].name)) &&
        (sim_strncasecmp(name, list[i].name, n) == 0)) {
        (strncasecmp(name, list[i].name, n) == 0)) {
      found = 1;
      strcpy(temp, list[i].desc);
    }
  }
  return (found ? temp : NULL);
}

903
904
905
906
907
908
909
910

911
912
913
914
915
916
917
903
904
905
906
907
908
909

910
911
912
913
914
915
916
917







-
+








  /* set information in (new) tail item */
  item = &que->item[que->tail];
  item->type = type;
  item->packet.len = len;
  item->packet.used = used;
  item->packet.crc_len = crc_len;
  if (MAX (len, crc_len) <= sizeof (item->packet.msg) - ETH_CRC_SIZE) {
  if (MAX (len, crc_len) <= sizeof (item->packet.msg)) {
    memcpy(item->packet.msg, data, ((len > crc_len) ? len : crc_len));
    if (crc_data && (crc_len > len))
      memcpy(&item->packet.msg[len], crc_data, ETH_CRC_SIZE);
    }
  else {
    item->packet.oversize = (uint8 *)realloc (item->packet.oversize, ((len > crc_len) ? len : crc_len));
    memcpy(item->packet.oversize, data, ((len > crc_len) ? len : crc_len));
1134
1135
1136
1137
1138
1139
1140
1141
1142


1143
1144
1145
1146
1147

1148
1149
1150
1151
1152
1153
1154
1134
1135
1136
1137
1138
1139
1140


1141
1142
1143
1144
1145
1146

1147
1148
1149
1150
1151
1152
1153
1154







-
-
+
+




-
+







    case 0:                  /* not loaded */
            /* attempt to load DLL */
#ifdef _WIN32
      if (1) {
        BOOL(WINAPI *p_SetDllDirectory)(LPCTSTR);
        UINT(WINAPI *p_GetSystemDirectory)(LPTSTR lpBuffer, UINT uSize);

        p_SetDllDirectory = (BOOL(WINAPI *)(LPCTSTR)) GetProcAddress(GetModuleHandle("kernel32.dll"), "SetDllDirectoryA");
        p_GetSystemDirectory = (UINT(WINAPI *)(LPTSTR, UINT)) GetProcAddress(GetModuleHandle("kernel32.dll"), "GetSystemDirectoryA");
        p_SetDllDirectory = (BOOL(WINAPI *)(LPCTSTR)) GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA");
        p_GetSystemDirectory = (UINT(WINAPI *)(LPTSTR, UINT)) GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetSystemDirectoryA");
        if (p_SetDllDirectory && p_GetSystemDirectory) {
          char npcap_path[512] = "";

          if (p_GetSystemDirectory (npcap_path, sizeof(npcap_path) - 7))
            sim_strlcat (npcap_path, "\\Npcap", sizeof(npcap_path));
            strlcat (npcap_path, "\\Npcap", sizeof(npcap_path));
          if (p_SetDllDirectory(npcap_path))
            hLib = LoadLibraryA(lib_name);
          p_SetDllDirectory (NULL);
          }
        if (hLib == NULL)
          hLib = LoadLibraryA(lib_name);
        }
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1699
1700
1701
1702
1703
1704
1705


1706
1707
1708
1709
1710
1711
1712







-
-







memset(&header, 0, sizeof(header));
header.caplen = header.len = len;
_eth_callback((u_char *)opaque, &header, buf);
}
#endif

#if defined (USE_READER_THREAD)
#include <pthread.h>

static void *
_eth_reader(void *arg)
{
ETH_DEV* volatile dev = (ETH_DEV*)arg;
int status = 0;
int sel_ret = 0;
int do_select = 0;
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3611
3612
3613
3614
3615
3616
3617

3618
3619
3620
3621
3622
3623
3624







-








t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses,
                       ETH_BOOL all_multicast, ETH_BOOL promiscuous, 
                       ETH_MULTIHASH* const hash)
{
int i;
char buf[116+66*ETH_FILTER_MAX];
char errbuf[PCAP_ERRBUF_SIZE];
char mac[20];
char* buf2;
t_stat status;
#ifdef USE_BPF
struct bpf_program bpf;
#endif

3758
3759
3760
3761
3762
3763
3764

3765
3766
3767
3768
3769
3770
3771
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769







+








/* get netmask, which is a required argument for compiling.  The value, 
   in our case isn't actually interesting since the filters we generate 
   aren't referencing IP fields, networks or values */

#ifdef USE_BPF
if (dev->eth_api == ETH_API_PCAP) {
  char errbuf[PCAP_ERRBUF_SIZE];
  bpf_u_int32  bpf_subnet, bpf_netmask;

  if (pcap_lookupnet(dev->name, &bpf_subnet, &bpf_netmask, errbuf)<0)
    bpf_netmask = 0;
  /* compile filter string */
  if ((status = pcap_compile((pcap_t*)dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) {
    sprintf(errbuf, "%s", pcap_geterr((pcap_t*)dev->handle));
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831


3832
3833
3834
3835
3836
3837
3838
3818
3819
3820
3821
3822
3823
3824

3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837







-




+
+







     returned by pcap_findalldevs.

*/
int eth_host_devices(int used, int max, ETH_LIST* list)
{
pcap_t* conn = NULL;
int i, j, datalink = 0;
char errbuf[PCAP_ERRBUF_SIZE];

for (i=0; i<used; ++i) {
  /* Cull any non-ethernet interface types */
#if defined(HAVE_PCAP_NETWORK)
  char errbuf[PCAP_ERRBUF_SIZE];

  conn = pcap_open_live(list[i].name, ETH_MAX_PACKET, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf);
  if (NULL != conn)
    datalink = pcap_datalink(conn), pcap_close(conn);
  list[i].eth_api = ETH_API_PCAP;
#endif
  if ((NULL == conn) || (datalink != DLT_EN10MB)) {
    for (j=i; j<used-1; ++j)
3921
3922
3923
3924
3925
3926
3927
3928

3929
3930
3931
3932
3933
3934
3935
3920
3921
3922
3923
3924
3925
3926

3927
3928
3929
3930
3931
3932
3933
3934







-
+








return used;
}

int eth_devices(int max, ETH_LIST* list)
{
int i = 0;
char errbuf[PCAP_ERRBUF_SIZE];
char errbuf[PCAP_ERRBUF_SIZE] = "";
#ifndef DONT_USE_PCAP_FINDALLDEVS
pcap_if_t* alldevs;
pcap_if_t* dev;

memset(list, 0, max*sizeof(*list));
errbuf[0] = '\0';
/* retrieve the device list */
Changes to src/sim_ether.h.
100
101
102
103
104
105
106

107
108
109
110
111
112
113
114


115
116
117
118
119
120
121
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







+








+
+







#define PCAP_READ_TIMEOUT -1
#else
#define PCAP_READ_TIMEOUT  1
#endif

/* set related values to have correct relationships */
#if defined (USE_READER_THREAD)
#include <pthread.h>
#if defined (USE_SETNONBLOCK)
#undef USE_SETNONBLOCK
#endif /* USE_SETNONBLOCK */
#undef PCAP_READ_TIMEOUT
#define PCAP_READ_TIMEOUT 15
#if (!defined (xBSD) && !defined(_WIN32) && !defined(VMS) && !defined(__CYGWIN__)) || defined (HAVE_TAP_NETWORK) || defined (HAVE_VDE_NETWORK)
#define MUST_DO_SELECT 1
#endif
#else
#include <time.h>
#endif /* USE_READER_THREAD */

/* give priority to USE_NETWORK over USE_SHARED */
#if defined(USE_NETWORK) && defined(USE_SHARED)
#undef USE_SHARED
#endif
/* USE_SHARED only works on Windows or if HAVE_DLOPEN */
Changes to src/sim_fio.c.
371
372
373
374
375
376
377




























378
379
380
381
382
383
384
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








int sim_fseek (FILE *st, t_addr offset, int whence)
{
return sim_fseeko (st, (t_offset)offset, whence);
}

#if defined(_WIN32)
static const char *
GetErrorText(DWORD dwError)
{
static char szMsgBuffer[2048];
DWORD dwStatus;

dwStatus = FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM|
                           FORMAT_MESSAGE_IGNORE_INSERTS,     //  __in      DWORD dwFlags,
                           NULL,                              //  __in_opt  LPCVOID lpSource,
                           dwError,                           //  __in      DWORD dwMessageId,
                           0,                                 //  __in      DWORD dwLanguageId,
                           szMsgBuffer,                       //  __out     LPTSTR lpBuffer,
                           sizeof (szMsgBuffer) -1,           //  __in      DWORD nSize,
                           NULL);                             //  __in_opt  va_list *Arguments
if (0 == dwStatus)
    snprintf(szMsgBuffer, sizeof(szMsgBuffer) - 1, "Error Code: 0x%lX", dwError);
while (sim_isspace (szMsgBuffer[strlen (szMsgBuffer)-1]))
    szMsgBuffer[strlen (szMsgBuffer) - 1] = '\0';
return szMsgBuffer;
}

t_stat sim_copyfile (const char *source_file, const char *dest_file, t_bool overwrite_existing)
{
if (CopyFileA (source_file, dest_file, !overwrite_existing))
    return SCPE_OK;
return sim_messagef (SCPE_ARG, "Error Copying '%s' to '%s': %s\n", source_file, dest_file, GetErrorText (GetLastError ()));
}

#include <io.h>
int sim_set_fsize (FILE *fptr, t_addr size)
{
return _chsize(_fileno(fptr), (long)size);
}

int sim_set_fifo_nonblock (FILE *fptr)
435
436
437
438
439
440
441
















































442
443
444
445
446
447
448
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







int sim_set_fsize (FILE *fptr, t_addr size)
{
return ftruncate(fileno(fptr), (off_t)size);
}

#include <sys/stat.h>
#include <fcntl.h>
#if HAVE_UTIME
#include <utime.h>
#endif

t_stat sim_copyfile (const char *source_file, const char *dest_file, t_bool overwrite_existing)
{
FILE *fIn = NULL, *fOut = NULL;
t_stat st = SCPE_OK;
char *buf = NULL;
size_t bytes;

fIn = sim_fopen (source_file, "rb");
if (!fIn) {
    st = sim_messagef (SCPE_ARG, "Can't open '%s' for input: %s\n", source_file, strerror (errno));
    goto Cleanup_Return;
    }
fOut = sim_fopen (dest_file, "wb");
if (!fOut) {
    st = sim_messagef (SCPE_ARG, "Can't open '%s' for output: %s\n", dest_file, strerror (errno));
    goto Cleanup_Return;
    }
buf = (char *)malloc (BUFSIZ);
while ((bytes = fread (buf, 1, BUFSIZ, fIn)))
    fwrite (buf, 1, bytes, fOut);
Cleanup_Return:
free (buf);
if (fIn)
    fclose (fIn);
if (fOut)
    fclose (fOut);
#if defined(HAVE_UTIME)
if (st == SCPE_OK) {
    struct stat statb;

    if (!stat (source_file, &statb)) {
        struct utimbuf utim;

        utim.actime = statb.st_atime;
        utim.modtime = statb.st_mtime;
        if (utime (dest_file, &utim))
            st = SCPE_IOERR;
        }
    else
        st = SCPE_IOERR;
    }
#endif
return st;
}

int sim_set_fifo_nonblock (FILE *fptr)
{
struct stat stbuf;

if (!fptr || fstat (fileno(fptr), &stbuf))
    return -1;
519
520
521
522
523
524
525

















595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
    munmap (shmem->shm_base, shmem->shm_size);
if (shmem->shm_fd != -1)
    close (shmem->shm_fd);
free (shmem);
}

#endif

#if defined(__VAX)
/* 
 * We privide a 'basic' snprintf, which 'might' overrun a buffer, but
 * the actual use cases don't on other platforms and none of the callers
 * care about the function return value.
 */
int sim_vax_snprintf(char *buf, size_t buf_size, const char *fmt, ...)
{
va_list arglist;

va_start (arglist, fmt);
vsprintf (buf, fmt, arglist);
va_end (arglist);
return 0;
}
#endif
Changes to src/sim_fio.h.
62
63
64
65
66
67
68

69
70
71
72
73
74
75
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76







+







size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr);
size_t sim_fwrite (const void *bptr, size_t size, size_t count, FILE *fptr);
uint32 sim_fsize (FILE *fptr);
uint32 sim_fsize_name (const char *fname);
t_offset sim_ftell (FILE *st);
t_offset sim_fsize_ex (FILE *fptr);
t_offset sim_fsize_name_ex (const char *fname);
t_stat sim_copyfile (const char *source_file, const char *dest_file, t_bool overwrite_existing);
void sim_buf_swap_data (void *bptr, size_t size, size_t count);
void sim_buf_copy_swapped (void *dptr, const void *bptr, size_t size, size_t count);
typedef struct SHMEM SHMEM;
t_stat sim_shmem_open (const char *name, size_t size, SHMEM **shmem, void **addr);
void sim_shmem_close (SHMEM *shmem);

extern t_bool sim_taddr_64;         /* t_addr is > 32b and Large File Support available */
Changes to src/sim_frontpanel.h.
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
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







-
+











-
+







extern "C" {
#endif

#include <stdlib.h>

#if !defined(__VAX)         /* Unsupported platform */

#define SIM_FRONTPANEL_VERSION   4
#define SIM_FRONTPANEL_VERSION   8

/**

    sim_panel_start_simulator       A starts a simulator with a particular 
                                    configuration

        sim_path            the path to the simulator binary
        sim_config          the configuration to run the simulator with
        device_panel_count  the number of sub panels for connected devices

    Note 1: - The path specified must be either a fully specified path or 
              it could be merey the simulator name if the simulator binary
              it could be merely the simulator name if the simulator binary
              is located in the current PATH.
            - The simulator binary must be built from the same version 
              simh source code that the frontpanel API was acquired fron 
              (the API and the simh framework must speak the same language) 

    Note 2: - Configuration file specified should contain device setup 
              statements (enable, disable, CPU types and attach commands).
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
190
191
192
193
194
195
196

197
198
199
200
201
202
203
204







-
+








/**

    A panel application has a choice of two different methods of getting 
    the values contained in the set of registers it has declared interest in via
    the sim_panel_add_register APIs.
    
       1)  The values can be polled (when ever it is desired) by calling
       1)  The values can be polled (whenever it is desired) by calling
           sim_panel_get_registers().
       2)  The panel can call sim_panel_set_display_callback_interval() to 
           specify a callback routine and a periodic rate that the callback 
           routine should be called.  The panel API will make a best effort 
           to deliver the current register state at the desired rate.


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







-
+
+
+
+


+


+






+
+
+
+
+
+




-









+
+









+
+
+







                                         void *context, 
                                         int usecs_between_callbacks);

/**

    When a front panel application wants to get averaged bit sample
    values, it must first declare the sampling parameters that will
    be used while collecting the bit values.
    be used while collecting the bit values.  The dithering 
    percentage must be 25% or less and when non 0 causes the sample
    frequency to vary by plus or minus a random percentage value up 
    to the specified value.

   sim_panel_set_sampling_parameters 
   sim_panel_set_sampling_parameters_ex 

        sample_frequency    cycles/instructions between sample captures
        sample_dither_pct   percentage of sample_frequency to vary randomly
        sample_depth        how many samples to accumulate in the rolling
                            average for each bit sample.  Returned bit
                            sample values will range from 0 thru this 
                            value.
 */

int
sim_panel_set_sampling_parameters_ex (PANEL *panel,
                                      unsigned int sample_frequency,
                                      unsigned int sample_dither_pct,
                                      unsigned int sample_depth);

int
sim_panel_set_sampling_parameters (PANEL *panel,
                                   unsigned int sample_frequency,
                                   unsigned int sample_depth);

/**

    When a front panel application needs to change the running
    state of a simulator one of the following routines should 
    be called:  
    
    sim_panel_exec_halt     - Stop instruction execution
    sim_panel_exec_boot     - Boot a simulator from a specific device
    sim_panel_exec_run      - Start/Resume a simulator running instructions
    sim_panel_exec_start    - Start a simulator running instructions 
                              after resetting all devices
    sim_panel_exec_step     - Have a simulator execute a single step
 */

int
sim_panel_exec_halt (PANEL *panel);

int
sim_panel_exec_boot (PANEL *panel, const char *device);

int
sim_panel_exec_start (PANEL *panel);

int
sim_panel_exec_run (PANEL *panel);

int
sim_panel_exec_step (PANEL *panel);

/**
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
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







-
+
+
+












-


+


-
-
-




+
+




+
+
+










sim_panel_get_state (PANEL *panel);

/**

    All APIs routines which return an int return 0 for 
    success and -1 for an error.  

    An API which returns an error (-1), will not change the panel state.
    An API which returns an error (-1), will not change the panel state
    except to possibly set the panel state to Error if the panel
    condition is no longer useful.
    
    sim_panel_get_error     - the details of the most recent error
    sim_panel_clear_error   - clears the error buffer
 */

const char *sim_panel_get_error (void);
void sim_panel_clear_error (void);

/**

    The panek<->simulator wire protocol can be traced if protocol problems arise.
    
    sim_panel_set_debug_file    - Specifies the log file to record debug traffic
    sim_panel_set_debug_mode    - Specifies the debug detail to be recorded
    sim_panel_flush_debug       - Flushes debug output to disk
    sim_panel_debug       -       Write message to the debug file

 */
void
sim_panel_set_debug_file (PANEL *panel, const char *debug_file);

#define DBG_XMT         1   /* Transmit Data */
#define DBG_RCV         2   /* Receive Data */
#define DBG_REQ         4   /* Request Data */
#define DBG_RSP         8   /* Response Data */
#define DBG_THR        16   /* Thread Activities */
#define DBG_APP        32   /* Application Activities */

void
sim_panel_set_debug_mode (PANEL *panel, int debug_bits);

void
sim_panel_debug (PANEL *panel, const char *fmt, ...);

void
sim_panel_flush_debug (PANEL *panel);

#endif /* !defined(__VAX) */

#ifdef  __cplusplus
}
#endif

#endif /* SIM_FRONTPANEL_H_ */
Changes to src/sim_serial.c.
1
2
3

4
5
6
7
8
9
10
1
2

3
4
5
6
7
8
9
10


-
+







/* sim_serial.c: OS-dependent serial port routines

   Copyright (c) 2008, J. David Bryan
   Copyright (c) 2008, J. David Bryan, Mark Pizzolato

   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:
23
24
25
26
27
28
29

30
31
32
33
34
35
36
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37







+







   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from the author.

   The author gratefully acknowledges the assistance of Holger Veit with the
   UNIX-specific code and testing.

   07-Oct-08    JDB     [serial] Created file
   22-Apr-12    MP      Adapted from code originally written by J. David Bryan


   This module provides OS-dependent routines to access serial ports on the host
   machine.  The terminal multiplexer library uses these routines to provide
   serial connections to simulated terminal interfaces.

   Currently, the module supports Windows and UNIX.  Use on other systems
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
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







-
+


















-
+







size_t n;
int i, found;

found = 0;
n = strlen(name);
for (i=0; i<count && !found; i++) {
    if ((n == strlen(list[i].name)) &&
        (sim_strncasecmp(name, list[i].name, n) == 0)) {
        (strncasecmp(name, list[i].name, n) == 0)) {
        found = 1;
        strcpy(temp, list[i].name); /* only case might be different */
        }
    }
return (found ? temp : NULL);
}

char* sim_serial_getdesc_byname (char* name, char* temp)
{
SERIAL_LIST  list[SER_MAX_DEVICE];
int count = sim_serial_devices(SER_MAX_DEVICE, list);
size_t n;
int i, found;

found = 0;
n = strlen(name);
for (i=0; i<count && !found; i++) {
    if ((n == strlen(list[i].name)) &&
        (sim_strncasecmp(name, list[i].name, n) == 0)) {
        (strncasecmp(name, list[i].name, n) == 0)) {
        found = 1;
        strcpy(temp, list[i].desc);
        }
    }
  return (found ? temp : NULL);
}

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
876
877
878
879
880
881
882


883
884
885
886
887






888
889
890
891
892
893
894







-
-





-
-
-
-
-
-







   "Count" characters are written from "buffer" to the serial port.  The actual
   number of characters written to the port is returned.  If an error occurred
   on writing, -1 is returned.
*/

int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
{
if (WaitForSingleObject (port->oWriteReady.hEvent, 0) == WAIT_TIMEOUT)
    return 0;
if ((!WriteFile (port->hPort, (LPVOID) buffer,   /* write the buffer to the serial port */
                 (DWORD) count, NULL, &port->oWriteSync)) &&
    (GetLastError () != ERROR_IO_PENDING)) {
    sim_error_serial ("WriteFile",              /* function failed; report unexpected error */
                      (int) GetLastError ());
    return -1;                                  /* return failure to caller */
    }
if ((!WaitCommEvent (port->hPort, &port->dwEvtMask, &port->oWriteReady)) &&
    (GetLastError () != ERROR_IO_PENDING)) {
    sim_error_serial ("WaitCommEvent",          /* function failed; report unexpected error */
                      (int) GetLastError ());
    return -1;                                  /* return failure to caller */
    }
return count;                                   /* return number of characters written/queued */
}


/* Close a serial port.
Changes to src/sim_serial.h.
1
2
3

4
5
6
7
8
9
10
1
2

3
4
5
6
7
8
9
10


-
+







/* sim_serial.h: OS-dependent serial port routines header file

   Copyright (c) 2008, J. David Bryan
   Copyright (c) 2008, J. David Bryan, Mark Pizzolato

   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:
20
21
22
23
24
25
26


27
28
29
30
31
32
33
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35







+
+







   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the name of the author shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from the author.

   07-Oct-08    JDB     [serial] Created file
   22-Apr-12    MP      Adapted from code originally written by J. David Bryan

*/


#ifndef SIM_SERIAL_H_
#define SIM_SERIAL_H_    0

#ifdef  __cplusplus
Changes to src/sim_tape.c.
64
65
66
67
68
69
70


71
72
73
74
75
76
77
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79







+
+







   sim_tape_wrrecf      write tape record forward
   sim_tape_sprecf      space tape record forward
   sim_tape_sprecr      space tape record reverse
   sim_tape_wrtmk       write tape mark
   sim_tape_wreom       erase remainder of tape
   sim_tape_wreomrw     erase remainder of tape & rewind
   sim_tape_wrgap       write erase gap
   sim_tape_errecf      erase record forward
   sim_tape_errecr      erase record reverse
   sim_tape_sprecsf     space records forward
   sim_tape_spfilef     space files forward 
   sim_tape_sprecsr     space records reverse
   sim_tape_spfiler     space files reverse
   sim_tape_position    generalized position
   sim_tape_rewind      rewind
   sim_tape_reset       reset unit
316
317
318
319
320
321
322



323

324


325
326




327
328
329
330
331
332
333
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







+
+
+
-
+

+
+


+
+
+
+







TAPE_PCALLBACK callback = ctx->callback;

sim_debug (ctx->dbit, ctx->dptr, "_tape_completion_dispatch(unit=%d, top=%d, callback=%p)\n", (int)(uptr-ctx->dptr->units), ctx->io_top, ctx->callback);

if (ctx->io_top != TOP_DONE)
    abort();                                            /* horribly wrong, stop */

if (ctx->asynch_io)
    pthread_mutex_lock (&ctx->io_lock);

if (ctx->callback && ctx->io_top == TOP_DONE) {
if (ctx->callback) {
    ctx->callback = NULL;
    if (ctx->asynch_io)
        pthread_mutex_unlock (&ctx->io_lock);
    callback (uptr, ctx->io_status);
    }
else {
    if (ctx->asynch_io)
        pthread_mutex_unlock (&ctx->io_lock);
    }
}

static t_bool _tape_is_active (UNIT *uptr)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

if (ctx) {
363
364
365
366
367
368
369
370

371
372
373
374
375
376
377
378
374
375
376
377
378
379
380

381

382
383
384
385
386
387
388







-
+
-









/* Enable asynchronous operation */

t_stat sim_tape_set_async (UNIT *uptr, int latency)
{
#if !defined(SIM_ASYNCH_IO)
sim_printf ("Tape: can't operate asynchronously\r\n");
return sim_messagef (SCPE_NOFNC, "Tape: can't operate asynchronously\r\n");
return SCPE_NOFNC;
#else
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
pthread_attr_t attr;

ctx->asynch_io = sim_asynch_enabled;
ctx->asynch_io_latency = latency;
if (ctx->asynch_io) {
442
443
444
445
446
447
448
449

450
451
452
453
454
455
456
452
453
454
455
456
457
458

459
460
461
462
463
464
465
466







-
+








t_stat sim_tape_attach (UNIT *uptr, CONST char *cptr)
{
DEVICE *dptr;

if ((dptr = find_dev_from_unit (uptr)) == NULL)
    return SCPE_NOATT;
return sim_tape_attach_ex (uptr, cptr, (dptr->flags & DEV_DEBUG) ? 0xFFFFFFFF : 0, 0);
return sim_tape_attach_ex (uptr, cptr, ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) ? 0xFFFFFFFF : 0, 0);
}

t_stat sim_tape_attach_ex (UNIT *uptr, const char *cptr, uint32 dbit, int completion_delay)
{
struct tape_context *ctx;
uint32 objc;
DEVICE *dptr;
603
604
605
606
607
608
609
610

611
612
613
614
615
616
617
613
614
615
616
617
618
619

620
621
622
623
624
625
626
627







-
+








if (ctx == NULL)
    return;
if (sim_deb && (ctx->dptr->dctrl & reason))
    sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), "", len, txt, reason);
}

/* Read record length forward (internal routine)
/* Read record length forward (internal routine).

   Inputs:
        uptr    =       pointer to tape unit
        bc      =       pointer to returned record length
   Outputs:
        status  =       operation status

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







-
+










-
+














-
+
+





-
+







+
+
+
+
-
+













-
+









-
-
-
-
+
+
+



-
-
-
+
+
+





-
-

-
-
-
+
+
+
+
+
+









+










-
+

-
+



















-
+







-
+





-
+

-
+











-
+

-
+






-
+







-
-
-
+
+
+
+
+
+
+
+

-
-
+
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+




-
-
+
+






+


-
+

-
+

-
+
-

+
-
-
-
-
+
+
+
+
+
+





+


-
+
+

-
+

-
+


-
+

-
+


+
+
-
-
-
-
-
+
+
+
+
+
+



-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+








   This routine is called to set up a record read or spacing in the forward
   direction.  On return, status is MTSE_OK and the tape is positioned at the
   first data byte if a record was encountered, or status is an MTSE error code
   giving the reason that the operation did not succeed and the tape position is
   as indicated above.

   The ANSI standards for magnetic tape recording (X3.32, X3.39, and X3.54) and
   The ANSI standards for magnetic tape recording (X3.22, X3.39, and X3.54) and
   the equivalent ECMA standard (ECMA-62) specify a maximum erase gap length of
   25 feet (7.6 meters).  While gaps of any length may be written, gaps longer
   than this are non-standard and may indicate that an unrecorded or erased tape
   is being read.

   If the tape density has been set via a previous "sim_tape_set_dens" call,
   then the length is monitored when skipping over erase gaps.  If the length
   reaches 25 feet, motion is terminated, and MTSE_RUNAWAY status is returned.
   Runaway status is also returned if an end-of-medium marker or the physical
   end of file is encountered while spacing over a gap; however, MTSE_EOM is
   returned if the tape is positioned at the EOM on entry.
   returned if the tape is positioned at the EOM or EOF on entry.

   If the density has not been set, then a gap of any length is skipped, and
   MTSE_RUNAWAY status is never returned.  In effect, erase gaps present in the
   tape image file will be transparent to the caller.

   Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format.
   Because gaps may be partially overwritten with data records, gap metadata
   must be examined marker-by-marker.  To reduce the number of file read calls,
   a buffer of metadata elements is used.  The buffer size is initially
   established at 256 elements but may be set to any size desired.  To avoid a
   large read for the typical case where an erase gap is not present, the first
   read is of a single metadatum marker.  If that is a gap marker, then
   additional buffered reads are performed.

   See the notes at "sim_tape_wrgap" regarding the erase gap implementation.
   See the notes at "tape_erase_fwd" regarding the erase gap implementation.


   Implementation notes:

    1. For programming convenience, erase gap processing is performed for both
       SIMH standard and E11 tape formats, although the latter will never
       contain erase gaps, as the "sim_tape_wrgap" call takes no action for the
       contain erase gaps, as the "tape_erase_fwd" call takes no action for the
       E11 format.

    2. The "feof" call cannot return a non-zero value on the first pass through
       the loop, because the "sim_fseek" call resets the internal end-of-file
       indicator.  Subsequent passes only occur if an erase gap is present, so
       a non-zero return indicates an EOF was seen while reading through a gap.

    3. The "runaway_counter" cannot decrement to zero (or below) in the presence
       of an error that terminates the gap-search loop.  Therefore, the test
       after the loop exit need not check for error status.

    3. The dynamic start/stop test of the HP 3000 magnetic tape diagnostic
    4. The dynamic start/stop test of the HP 3000 magnetic tape diagnostic
       heavily exercises the erase gap scanning code.  Sample test execution
       times for various buffer sizes on a 2 GHz host platform are:

         buffer size    execution time
         (elements)     (CPU seconds)
         -----------    --------------
               1             7200
              32              783
             128              237
             256              203
             512              186
            1024              171

    4. Because an erase gap may precede the logical end-of-medium, represented
    5. Because an erase gap may precede the logical end-of-medium, represented
       either by the physical end-of-file or by an EOM marker, the "position not
       updated" flag is set only if the tape is positioned at the EOM when the
       routine is entered.  If at least one gap marker precedes the EOM, then
       the PNU flag is not set.  This ensures that a backspace-and-retry
       sequence will work correctly in both cases.
*/

static t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
uint8 c;
t_bool all_eof;
uint32 f = MT_GET_FMT (uptr);
uint8    c;
t_bool   all_eof;
uint32   f = MT_GET_FMT (uptr);
t_mtrlnt sbc;
t_tpclnt tpcbc;
t_mtrlnt buffer [256];                                  /* local tape buffer */
uint32 bufcntr, bufcap;                                 /* buffer counter and capacity */
int32 runaway_counter, sizeof_gap;                      /* bytes remaining before runaway and bytes per gap */
t_stat r = MTSE_OK;
uint32   bufcntr, bufcap;                               /* buffer counter and capacity */
int32    runaway_counter, sizeof_gap;                   /* bytes remaining before runaway and bytes per gap */
t_stat   status = MTSE_OK;

MT_CLR_PNU (uptr);                                      /* clear the position-not-updated flag */

if ((uptr->flags & UNIT_ATT) == 0)                      /* if the unit is not attached */
    return MTSE_UNATT;                                  /*   then quit with an error */
if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */

sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* set the initial tape position */

switch (f) {                                            /* the read method depends on the tape format */
if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) {   /* set the initial tape position; if it fails */
    MT_SET_PNU (uptr);                                  /*   then set position not updated */
    status = sim_tape_ioerr (uptr);                     /*     and quit with I/O error status */
    }

else switch (f) {                                       /* otherwise the read method depends on the tape format */

    case MTUF_F_STD:
    case MTUF_F_E11:
        runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set the largest legal gap size in bytes */

        if (runaway_counter == 0) {                     /* if tape density has not been not set */
            sizeof_gap = 0;                             /*   then disable runaway detection */
            runaway_counter = INT_MAX;                  /*     to allow gaps of any size */
            }

        else                                            /* otherwise */
            sizeof_gap = sizeof (t_mtrlnt);             /*   set the size of the gap */

        bufcntr = 0;                                    /* force an initial read */
        bufcap = 0;                                     /*   but of just one metadata marker */

        do {                                            /* loop until a record, gap, or error is seen */
            if (bufcntr == bufcap) {                    /* if the buffer is empty then refill it */
                if (feof (uptr->fileref)) {             /* if we hit the EOF while reading a gap */
                    if (sizeof_gap > 0)                 /*   then if detection is enabled */
                        r = MTSE_RUNAWAY;               /*     then report a tape runaway */
                        status = MTSE_RUNAWAY;          /*     then report a tape runaway */
                    else                                /*   otherwise report the physical EOF */
                        r = MTSE_EOM;                   /*     as the end-of-medium */
                        status = MTSE_EOM;              /*     as the end-of-medium */
                    break;
                    }

                else if (bufcap == 0)                   /* otherwise if this is the initial read */
                    bufcap = 1;                         /*   then start with just one marker */

                else                                    /* otherwise reset the capacity */
                    bufcap = sizeof (buffer)            /*   to the full size of the buffer */
                               / sizeof (buffer [0]);

                bufcap = sim_fread (buffer,             /* fill the buffer */
                                    sizeof (t_mtrlnt),  /*   with tape metadata */
                                    bufcap,
                                    uptr->fileref);

                if (ferror (uptr->fileref)) {           /* if a file I/O error occurred */
                    if (bufcntr == 0)                   /*   then if this is the initial read */
                        MT_SET_PNU (uptr);              /*     then set position not updated */

                    r = sim_tape_ioerr (uptr);          /* report the error and quit */
                    status = sim_tape_ioerr (uptr);     /* report the error and quit */
                    break;
                    }

                else if (bufcap == 0                    /* otherwise if positioned at the physical EOF */
                  || buffer [0] == MTR_EOM)             /*   or at the logical EOM */
                    if (bufcntr == 0) {                 /*     then if this is the initial read */
                        MT_SET_PNU (uptr);              /*       then set position not updated */
                        r = MTSE_EOM;                   /*         and report the end-of-medium and quit */
                        status = MTSE_EOM;              /*         and report the end-of-medium and quit */
                        break;
                        }

                    else {                              /*     otherwise some gap has already been skipped */
                        if (sizeof_gap > 0)             /*       so if detection is enabled */
                            r = MTSE_RUNAWAY;           /*         then report a tape runaway */
                            status = MTSE_RUNAWAY;      /*         then report a tape runaway */
                        else                            /*       otherwise report the physical EOF */
                            r = MTSE_EOM;               /*         as the end-of-medium */
                            status = MTSE_EOM;          /*         as the end-of-medium */
                        break;
                        }

                else                                    /* otherwise reset the index */
                    bufcntr = 0;                        /*   to the start of the buffer */
                }

            *bc = buffer [bufcntr++];                   /* store the metadata marker value */

            if (*bc == MTR_EOM) {                       /* if an end-of-medium marker is seen */
                if (sizeof_gap > 0)                     /*   then if detection is enabled */
                    r = MTSE_RUNAWAY;                   /*     then report a tape runaway */
                    status = MTSE_RUNAWAY;              /*     then report a tape runaway */
                else                                    /*   otherwise report the physical EOF */
                    r = MTSE_EOM;                       /*     as the end-of-medium */
                    status = MTSE_EOM;                  /*     as the end-of-medium */
                break;
                }

            uptr->pos = uptr->pos + sizeof (t_mtrlnt);  /* space over the marker */

            if (*bc == MTR_TMK) {                       /* if the value is a tape mark */
                r = MTSE_TMK;                           /*   then quit with tape mark status */
                status = MTSE_TMK;                      /*   then quit with tape mark status */
                break;
                }

            else if (*bc == MTR_GAP)                    /* otherwise if the value is a full gap */
                runaway_counter -= sizeof_gap;          /*   then decrement the gap counter */

            else if (*bc == MTR_FHGAP) {                        /* otherwise if the value if a half gap */
                uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2;  /*   then back up */
                sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /*     to resync */
                bufcntr = bufcap;                               /* mark the buffer as invalid to force a read */
                uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2;  /*   then back up and resync */

                if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) {   /* set the tape position; if it fails */
                    status = sim_tape_ioerr (uptr);                     /*   then quit with I/O error status */
                    break;
                    }

                bufcntr = bufcap;                       /* mark the buffer as invalid to force a read */

                *bc = MTR_GAP;                                  /* reset the marker */
                runaway_counter -= sizeof_gap / 2;              /*   and decrement the gap counter */
                *bc = MTR_GAP;                          /* reset the marker */
                runaway_counter -= sizeof_gap / 2;      /*   and decrement the gap counter */
                }

            else {                                                  /* otherwise it's a record marker */
                if (bufcntr < bufcap)                               /* if the position is within the buffer */
                    sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /*   then seek to the data area */

                sbc = MTR_L (*bc);                                  /* extract the record length */
                uptr->pos = uptr->pos + sizeof (t_mtrlnt)           /* position to the start */
                  + (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc);       /*   of the record */
            else {                                                      /* otherwise it's a record marker */
                if (bufcntr < bufcap                                    /* if the position is within the buffer */
                  && sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) {  /*   then seek to the data area; if it fails */
                    status = sim_tape_ioerr (uptr);                     /*     then quit with I/O error status */
                    break;
                    }

                sbc = MTR_L (*bc);                              /* extract the record length */
                uptr->pos = uptr->pos + sizeof (t_mtrlnt)       /* position to the start */
                  + (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc);   /*   of the record */
                }
            }
        while (*bc == MTR_GAP && runaway_counter > 0);  /* continue until data or runaway occurs */

        if (r == MTSE_OK && runaway_counter <= 0)       /* if a tape runaway occurred */
            r = MTSE_RUNAWAY;                           /*   then report it */
        if (runaway_counter <= 0)                       /* if a tape runaway occurred */
            status = MTSE_RUNAWAY;                      /*   then report it */

        break;                                          /* otherwise the operation succeeded */

    case MTUF_F_TPC:
        sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
        *bc = tpcbc;                                    /* save rec lnt */

        if (ferror (uptr->fileref)) {                   /* error? */
            MT_SET_PNU (uptr);                          /* pos not upd */
            return sim_tape_ioerr (uptr);
            status = sim_tape_ioerr (uptr);
            }
        if (feof (uptr->fileref)) {                     /* eof? */
        else if (feof (uptr->fileref)) {                /* eof? */
            MT_SET_PNU (uptr);                          /* pos not upd */
            r = MTSE_EOM;
            status = MTSE_EOM;
            break;
            }
        else {
        uptr->pos = uptr->pos + sizeof (t_tpclnt);      /* spc over reclnt */
        if (tpcbc == TPC_TMK)                           /* tape mark? */
            r = MTSE_TMK;
        uptr->pos = uptr->pos + ((tpcbc + 1) & ~1);     /* spc over record */
            uptr->pos = uptr->pos + sizeof (t_tpclnt);  /* spc over reclnt */
            if (tpcbc == TPC_TMK)                       /* tape mark? */
                status = MTSE_TMK;
            else
                uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */
            }
        break;

    case MTUF_F_P7B:
        for (sbc = 0, all_eof = 1; ; sbc++) {           /* loop thru record */
            sim_fread (&c, sizeof (uint8), 1, uptr->fileref);

            if (ferror (uptr->fileref)) {               /* error? */
                MT_SET_PNU (uptr);                      /* pos not upd */
                return sim_tape_ioerr (uptr);
                status = sim_tape_ioerr (uptr);
                break;
                }
            if (feof (uptr->fileref)) {                 /* eof? */
            else if (feof (uptr->fileref)) {            /* eof? */
                if (sbc == 0)                           /* no data? eom */
                    return MTSE_EOM;
                    status = MTSE_EOM;
                break;                                  /* treat like eor */
                }
            if ((sbc != 0) && (c & P7B_SOR))            /* next record? */
            else if ((sbc != 0) && (c & P7B_SOR))       /* next record? */
                break;
            if ((c & P7B_DPAR) != P7B_EOF)
            else if ((c & P7B_DPAR) != P7B_EOF)
                all_eof = 0;
            }

        if (status == MTSE_OK) {
        *bc = sbc;                                      /* save rec lnt */
        sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
        uptr->pos = uptr->pos + sbc;                    /* spc over record */
        if (all_eof)                                    /* tape mark? */
            r = MTSE_TMK;
            *bc = sbc;                                      /* save rec lnt */
            sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
            uptr->pos = uptr->pos + sbc;                    /* spc over record */
            if (all_eof)                                    /* tape mark? */
                status = MTSE_TMK;
            }
        break;

    default:
        return MTSE_FMT;
        }
sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos);
return r;
        status = MTSE_FMT;
    }

return status;
}

static t_stat sim_tape_rdrlfwd (UNIT *uptr, t_mtrlnt *bc)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat status;

if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */

status = sim_tape_rdlntf (uptr, bc);                    /* read the record length */

sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", status, *bc, uptr->pos);

return status;
}

/* Read record length reverse (internal routine)
/* Read record length reverse (internal routine).

   Inputs:
        uptr    =       pointer to tape unit
        bc      =       pointer to returned record length
   Outputs:
        status  =       operation status

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







+
+
+
+
+
+
+
+
+
-
-
+
+




-
-
-
-
-
+
+
+
+



-
-
-
+
+
+





-
-


-
+

-
+



-
+










-
+

-
+


-
+



+
+
+
-
+



+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+


-
-
+


-
-
-
-







-
+



















+
-
-
+
+
+
+
+
+




-
-
+
+








+

-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
+
+






+
-
-
-
-
+
+
+
+
+
+


+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+








   This routine is called to set up a record read or spacing in the reverse
   direction.  On return, status is MTSE_OK and the tape is positioned at the
   first data byte if a record was encountered, or status is an MTSE error code
   giving the reason that the operation did not succeed and the tape position is
   as indicated above.


   Implementation notes:

    1. The "sim_fread" call cannot return 0 in the absence of an error
       condition.  The preceding "sim_tape_bot" test ensures that "pos" >= 4, so
       "sim_fseek" will back up at least that far, so "sim_fread" will read at
       least one element.  If the call returns zero, an error must have
       occurred, so the "ferror" call must succeed.

   See the notes at "sim_tape_rdlntf" and "sim_tape_wrgap" regarding tape
   runaway and the erase gap implementation, respectively.
    2. See the notes at "sim_tape_rdlntf" and "tape_erase_fwd" regarding tape
       runaway and the erase gap implementation, respectively.
*/

static t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
uint8 c;
t_bool all_eof;
uint32 f = MT_GET_FMT (uptr);
t_addr ppos;
uint8    c;
t_bool   all_eof;
uint32   f = MT_GET_FMT (uptr);
t_addr   ppos;
t_mtrlnt sbc;
t_tpclnt tpcbc;
t_mtrlnt buffer [256];                                  /* local tape buffer */
uint32 bufcntr, bufcap;                                 /* buffer counter and capacity */
int32 runaway_counter, sizeof_gap;                      /* bytes remaining before runaway and bytes per gap */
t_stat r = MTSE_OK;
uint32   bufcntr, bufcap;                               /* buffer counter and capacity */
int32    runaway_counter, sizeof_gap;                   /* bytes remaining before runaway and bytes per gap */
t_stat   status = MTSE_OK;

MT_CLR_PNU (uptr);                                      /* clear the position-not-updated flag */

if ((uptr->flags & UNIT_ATT) == 0)                      /* if the unit is not attached */
    return MTSE_UNATT;                                  /*   then quit with an error */
if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */

if (sim_tape_bot (uptr))                                /* if the unit is positioned at the BOT */
    return MTSE_BOT;                                    /*   then reading backward is not possible */
    status = MTSE_BOT;                                  /*   then reading backward is not possible */

switch (f) {                                            /* the read method depends on the tape format */
else switch (f) {                                       /* otherwise the read method depends on the tape format */

    case MTUF_F_STD:
    case MTUF_F_E11:
        runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set largest legal gap size in bytes */
        runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set the largest legal gap size in bytes */

        if (runaway_counter == 0) {                     /* if tape density has not been not set */
            sizeof_gap = 0;                             /*   then disable runaway detection */
            runaway_counter = INT_MAX;                  /*     to allow gaps of any size */
            }

        else                                            /* otherwise */
            sizeof_gap = sizeof (t_mtrlnt);             /*   set the size of the gap */

        bufcntr = 0;                                    /* force an initial read */
        bufcap = 1;                                     /*   but of just one metadata marker */
        bufcap = 0;                                     /*   but of just one metadata marker */

        do {                                            /* loop until a record, gap, or error seen */
        do {                                            /* loop until a record, gap, or error is seen */
            if (bufcntr == 0) {                         /* if the buffer is empty then refill it */
                if (sim_tape_bot (uptr)) {              /* if the search has backed into the BOT */
                    r = MTSE_BOT;                       /*   then quit with an error */
                    status = MTSE_BOT;                  /*   then quit with an error */
                    break;
                    }

                else if (bufcap == 0)                   /* otherwise if this is the initial read */
                    bufcap = 1;                         /*   then start with just one marker */

                else if (uptr->pos < sizeof (buffer))   /* if less than a full buffer remains */
                else if (uptr->pos < sizeof (buffer))   /* otherwise if less than a full buffer remains */
                    bufcap = (uint32) uptr->pos         /*   then reduce the capacity accordingly */
                               / sizeof (t_mtrlnt);

                else                                    /* otherwise reset the capacity */
                    bufcap = sizeof (buffer)            /*   to the full size of the buffer */
                               / sizeof (buffer [0]);

                sim_fseek (uptr->fileref,                           /* seek back to the location */
                           uptr->pos - bufcap * sizeof (t_mtrlnt),  /*   corresponding to the start */
                           SEEK_SET);                               /*     of the buffer */

                bufcntr = sim_fread (buffer, sizeof (t_mtrlnt),     /* fill the buffer */
                                     bufcap, uptr->fileref);        /*   with tape metadata */
                if (sim_fseek (uptr->fileref,                           /* seek back to the location */
                               uptr->pos - bufcap * sizeof (t_mtrlnt),  /*   corresponding to the start */
                               SEEK_SET)) {                             /*     of the buffer; if it fails */
                    status = sim_tape_ioerr (uptr);                     /*         and fail with I/O error status */
                    break;
                    }

                bufcntr = sim_fread (buffer, sizeof (t_mtrlnt), /* fill the buffer */
                                     bufcap, uptr->fileref);    /*   with tape metadata */

                if (ferror (uptr->fileref)) {           /* if a file I/O error occurred */
                    MT_SET_PNU (uptr);                  /*   then set position not updated */
                    r = sim_tape_ioerr (uptr);          /*     report the error and quit */
                    status = sim_tape_ioerr (uptr);     /*   then report the error and quit */
                    break;
                    }

                else                                    /* otherwise reset the capacity */
                    bufcap = sizeof (buffer)            /*   to the full size of the buffer */
                               / sizeof (buffer [0]);
                }

            *bc = buffer [--bufcntr];                   /* store the metadata marker value */

            uptr->pos = uptr->pos - sizeof (t_mtrlnt);  /* backspace over the marker */

            if (*bc == MTR_TMK) {                       /* if the marker is a tape mark */
                r = MTSE_TMK;                           /*   then quit with tape mark status */
                status = MTSE_TMK;                      /*   then quit with tape mark status */
                break;
                }

            else if (*bc == MTR_GAP)                    /* otherwise if the marker is a full gap */
                runaway_counter -= sizeof_gap;          /*   then decrement the gap counter */

            else if ((*bc & MTR_M_RHGAP) == MTR_RHGAP           /* otherwise if the marker */
              || *bc == MTR_RRGAP) {                            /*   is a half gap */
                uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2;  /*     then position forward to resync */
                bufcntr = 0;                                    /* mark the buffer as invalid to force a read */

                *bc = MTR_GAP;                                  /* reset the marker */
                runaway_counter -= sizeof_gap / 2;              /*   and decrement the gap counter */
                }

            else {                                              /* otherwise it's a record marker */
                sbc = MTR_L (*bc);                              /* extract the record length */
                uptr->pos = uptr->pos - sizeof (t_mtrlnt)       /* position to the start */
                  - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc);   /*   of the record */

                sim_fseek (uptr->fileref,                       /* seek to the data area */
                           uptr->pos + sizeof (t_mtrlnt), SEEK_SET);
                if (sim_fseek (uptr->fileref,                   /* seek to the start of the data area; if it fails */
                               uptr->pos + sizeof (t_mtrlnt),   /*   then return with I/O error status */
                               SEEK_SET)) {
                    status = sim_tape_ioerr (uptr);
                    break;
                    }
                }
            }
        while (*bc == MTR_GAP && runaway_counter > 0);  /* continue until data or runaway occurs */

        if (r == MTSE_OK && runaway_counter <= 0)       /* if a tape runaway occurred */
            r = MTSE_RUNAWAY;                           /*   then report it */
        if (runaway_counter <= 0)                       /* if a tape runaway occurred */
            status = MTSE_RUNAWAY;                      /*   then report it */

        break;                                          /* otherwise the operation succeeded */

    case MTUF_F_TPC:
        ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */
        sim_fseek (uptr->fileref, ppos, SEEK_SET);      /* position */
        sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);
        *bc = tpcbc;                                    /* save rec lnt */

        if (ferror (uptr->fileref))                     /* error? */
            return sim_tape_ioerr (uptr);
        if (feof (uptr->fileref)) {                     /* eof? */
            r = MTSE_EOM;
            break;
            status = sim_tape_ioerr (uptr);
        else if (feof (uptr->fileref))                  /* eof? */
            status = MTSE_EOM;
        else {
            }
        uptr->pos = ppos;                               /* spc over record */
        if (*bc == MTR_TMK) {                           /* tape mark? */
            r = MTSE_TMK;
            break;
            uptr->pos = ppos;                           /* spc over record */
            if (*bc == MTR_TMK)                         /* tape mark? */
                status = MTSE_TMK;
            else
            }
        sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET);
                sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET);
            }
        break;

    case MTUF_F_P7B:
        for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) {
            sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET);
            sim_fread (&c, sizeof (uint8), 1, uptr->fileref);

            if (ferror (uptr->fileref))                 /* error? */
                return sim_tape_ioerr (uptr);
            if (feof (uptr->fileref)) {                 /* eof? */
                r = MTSE_EOM;
            if (ferror (uptr->fileref)) {               /* error? */
                status = sim_tape_ioerr (uptr);
                break;
                }
            else if (feof (uptr->fileref)) {            /* eof? */
                status = MTSE_EOM;
                break;
                }
            else {
            if ((c & P7B_DPAR) != P7B_EOF)
                all_eof = 0;
            if (c & P7B_SOR)                            /* start of record? */
                break;
            }
        uptr->pos = uptr->pos - sbc;                    /* update position */
        *bc = sbc;                                      /* save rec lnt */
        sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
        if (all_eof)                                    /* tape mark? */
            r = MTSE_TMK;
                if ((c & P7B_DPAR) != P7B_EOF)
                    all_eof = 0;
                if (c & P7B_SOR)                        /* start of record? */
                    break;
                }
            }

        if (status == MTSE_OK) {
            uptr->pos = uptr->pos - sbc;                    /* update position */
            *bc = sbc;                                      /* save rec lnt */
            sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */
            if (all_eof)                                    /* tape mark? */
                status = MTSE_TMK;
            }
        break;

    default:
        return MTSE_FMT;
        status = MTSE_FMT;
        }

return status;
}

static t_stat sim_tape_rdrlrev (UNIT *uptr, t_mtrlnt *bc)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat status;

if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */

status = sim_tape_rdlntr (uptr, bc);                    /* read the record length */

sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos);
return r;
sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", status, *bc, uptr->pos);

return status;
}

/* Read record forward

   Inputs:
        uptr    =       pointer to tape unit
        buf     =       pointer to buffer
1087
1088
1089
1090
1091
1092
1093
1094


1095
1096
1097
1098
1099
1100
1101
1102

1103
1104
1105
1106
1107
1108
1109
1173
1174
1175
1176
1177
1178
1179

1180
1181
1182
1183
1184
1185
1186
1187
1188

1189
1190
1191
1192
1193
1194
1195
1196







-
+
+







-
+







t_stat st;

if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecf(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max);

opos = uptr->pos;                                       /* old position */
if (MTSE_OK != (st = sim_tape_rdlntf (uptr, &tbc)))     /* read rec lnt */
st = sim_tape_rdrlfwd (uptr, &tbc);                     /* read rec lnt */
if (st != MTSE_OK)
    return st;
*bc = rbc = MTR_L (tbc);                                /* strip error flag */
if (rbc > max) {                                        /* rec out of range? */
    MT_SET_PNU (uptr);
    uptr->pos = opos;
    return MTSE_INVRL;
    }
i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */
i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */
if (ferror (uptr->fileref)) {                           /* error? */
    MT_SET_PNU (uptr);
    uptr->pos = opos;
    return sim_tape_ioerr (uptr);
    }
for ( ; i < rbc; i++)                                   /* fill with 0's */
    buf[i] = 0;
1152
1153
1154
1155
1156
1157
1158
1159


1160
1161
1162
1163
1164

1165
1166
1167
1168
1169
1170
1171
1239
1240
1241
1242
1243
1244
1245

1246
1247
1248
1249
1250
1251

1252
1253
1254
1255
1256
1257
1258
1259







-
+
+




-
+







t_mtrlnt i, rbc, tbc;
t_stat st;

if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecr(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max);

if (MTSE_OK != (st = sim_tape_rdlntr (uptr, &tbc)))     /* read rec lnt */
st = sim_tape_rdrlrev (uptr, &tbc);                     /* read rec lnt */
if (st != MTSE_OK)
    return st;
*bc = rbc = MTR_L (tbc);                                /* strip error flag */
if (rbc > max)                                          /* rec out of range? */
    return MTSE_INVRL;
i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */
i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */
if (ferror (uptr->fileref))                             /* error? */
    return sim_tape_ioerr (uptr);
for ( ; i < rbc; i++)                                   /* fill with 0's */
    buf[i] = 0;
if (f == MTUF_F_P7B)                                    /* p7b? strip SOR */
    buf[0] = buf[0] & P7B_DPAR;
sim_tape_data_trace(uptr, buf, rbc, "Record Read Reverse", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
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
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







-

-
-
-
+

-
-
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

+
+
+
+
+


-
-
-
-
+
+
+
+
-









-
-
-
-
-
-
-
-













-
+
-







t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_wreomrw (uptr);
AIO_CALL(TOP_WEMR, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback);
return r;
}

/* Write erase gap

   Inputs:
        uptr    = pointer to tape unit
        gaplen  = length of gap in tenths of an inch
/* Erase a gap in the forward direction (internal routine).

   Outputs:
        status  = operation status
   An erase gap is written in the forward direction on the tape unit specified
   by "uptr" for the number of bytes specified by "bc".  The status of the
   operation is returned, and the file position is altered as follows:

   exit condition       position
   ------------------   ------------------
   unit unattached      unchanged
   unsupported format   unchanged
   write protected      unchanged
   read error           unchanged, PNU set
   write error          unchanged, PNU set
   gap written          updated
     Exit Condition       File Position
     ------------------   ------------------
     unit unattached      unchanged
     unsupported format   unchanged
     write protected      unchanged
     read error           unchanged, PNU set
     write error          unchanged, PNU set
     gap written          updated

   If the requested byte count equals the metadatum size, then the routine
   succeeds only if it can overlay a single metadatum (i.e., a tape mark, an
   end-of-medium marker, or an existing erase gap marker); otherwise, the file
   position is not altered, PNU is set, and MTSE_INVRL (invalid record length)
   status is returned.

   An erase gap is represented in the tape image file by a special metadata
   value.  This value is chosen so that it is still recognizable even if it has
   been "cut in half" by a subsequent data overwrite that does not end on a
   metadatum-sized boundary.  In addition, a range of metadata values are
   reserved for detection in the reverse direction.  Erase gaps are currently
   value repeated throughout the gap.  The value is chosen so that it is still
   recognizable even if it has been "cut in half" by a subsequent data overwrite
   that does not end on a metadatum-sized boundary.  In addition, a range of
   metadata values are reserved for detection in the reverse direction.
   supported only in SIMH (MTUF_F_STD) tape format.

   This implementation supports erasing gaps in the middle of a populated tape
   image and will always produce a valid image.  It also produces valid images
   when overwriting gaps with data records, with one exception: a data write
   that leaves only two bytes of gap remaining will produce an invalid tape.
   This limitation is deemed acceptable, as it is analogous to the existing
   limitation that data records cannot overwrite other data records without
   producing an invalid tape.

   Because SIMH tape images do not carry physical parameters (e.g., recording
   density), overwriting a tape image file containing gap metadata is
   problematic if the density setting is not the same as that used during
   recording.  There is no way to establish a gap of a certain length
   unequivocally in an image file, so this implementation establishes a gap of a
   certain number of bytes that reflect the desired gap length at the tape
   density in bits per inch used during writing.

   To write an erase gap, the implementation uses one of two approaches,
   depending on whether or not the current tape position is at EOM.  Erasing at
   EOM presents no special difficulties; gap metadata markers are written for
   the prescribed number of bytes.  If the tape is not at EOM, then erasing must
   take into account the existing record structure to ensure that a valid tape
   image is maintained.

   The general approach is to erase for the nominal number of bytes but to
   increase that length, if necessary, to ensure that a partially overwritten
   data record at the end of the gap can be altered to maintain validity.
   Because the smallest legal tape record requires space for two metadata
   markers plus two data bytes, an erasure that would leave less than that
   is increased to consume the entire record.  Otherwise, the final record is
   truncated appropriately by rewriting the leading and trailing length words
   truncated by rewriting the leading and trailing length words appropriately.
   appropriately.

   When reading in either direction, gap metadata markers are ignored (skipped)
   until a record length header, EOF marker, EOM marker, or physical EOF is
   encountered.  Thus, tape images containing gap metadata are transparent to
   the calling simulator (unless tape runaway support is enabled -- see the
   notes at "sim_tape_rdlntf" for details).

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







-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+


-
+

-
-
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-









-
-
+
-

-
-
-
-

-
-
-
-
+
+
+
+
+
+
+
+
+

-
+





-
+



-
+






-
-
+
+
+
+
+
+
+
+
+










+
+
+
+
+
+



+
-
+
+
+




-
-
-
-
+
+
+








+






+
-
+
+
+







   detection are:

     0xFFFFFFFE            - primary gap value
     0xFFFEFFFF            - reserved (indicates half-gap in forward reads)
     0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads)
     0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads)

   If the tape density has been set via a previous sim_tape_set_dens call, and
   the tape format is set to SIMH format, then this routine will write a gap of
   the appropriate size.  If the density has not been set, then no action will
   If the current tape format supports erase gaps, then this routine will write
   a gap of the requested size.  If the format does not, then no action will be
   be taken, and either MTSE_IOERR or MTSE_OK status will be returned, depending
   on whether SIMH or another format is selected, respectively.  A simulator
   that calls this routine must set the density beforehand; failure to do so is
   an error.  However, calling while another format is enabled is OK and is
   treated as a no-operation.  This allows a device simulator that supports
   writing erase gaps to use the same code without worrying about the tape
   format currently selected by the user.
   taken, and MTSE_OK status will be returned.  This allows a device simulator
   that supports writing erase gaps to use the same code without worrying about
   the tape format currently selected by the user.  A request for an erase gap
   of zero length also succeeds with no action taken.


   Implementation notes:

    1. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format.
*/

t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen)
static t_stat tape_erase_fwd (UNIT *uptr, t_mtrlnt gap_size)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat st;
size_t   xfer;
t_stat   st;
t_mtrlnt meta, sbc, new_len, rec_size;
t_addr gap_pos = uptr->pos;
uint32 file_size, marker_count, tape_density;
int32 gap_needed;
uint32 gap_alloc = 0;                                   /* gap currently allocated from the tape */
const uint32 format = MT_GET_FMT (uptr);                /* tape format */
const uint32 meta_size = sizeof (t_mtrlnt);             /* bytes per metadatum */
const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2;  /* smallest data record size */
uint32   file_size, marker_count;
int32    gap_needed = (int32) gap_size;                 /* the gap remaining to be allocated from the tape */
uint32   gap_alloc = 0;                                 /* the gap currently allocated from the tape */
const t_addr gap_pos = uptr->pos;                       /* the file position where the gap will start */
const uint32 format = MT_GET_FMT (uptr);                /* the tape format */
const uint32 meta_size = sizeof (t_mtrlnt);             /* the number of bytes per metadatum */
const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2;  /* the smallest data record size */

if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen);

MT_CLR_PNU (uptr);

if ((uptr->flags & UNIT_ATT) == 0)                      /* if the unit is not attached */
    return MTSE_UNATT;                                  /*   then we cannot proceed */

else if (sim_tape_wrp (uptr))                           /* otherwise if the unit is write protected */
    return MTSE_WRP;                                    /*   then we cannot write */

tape_density = bpi [MT_DENS (uptr->dynflags)];          /* get the density of the tape */

else if (gap_size == 0 || format != MTUF_F_STD)         /* otherwise if zero length or gaps aren't supported */
if (format != MTUF_F_STD)                               /* if erase gaps aren't supported by the format */
    return MTSE_OK;                                     /*   then take no action */
else if (tape_density == 0)                             /* otherwise if the density is not set */
    return MTSE_IOERR;                                  /*   then report an I/O error */
else                                                    /* otherwise */
    gap_needed = (gaplen * tape_density) / 10;          /*   determine the gap size needed in bytes */

file_size = sim_fsize (uptr->fileref);                  /* get file size */
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* position tape */

/* Read tape records and allocate to gap until amount required is consumed.
file_size = sim_fsize (uptr->fileref);                  /* get the file size */

if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) {   /* position the tape; if it fails */
    MT_SET_PNU (uptr);                                  /*   then set position not updated */
    return sim_tape_ioerr (uptr);                       /*     and quit with I/O error status */
    }

/* Read tape records and allocate them to the gap until the amount required is
   consumed.

   Read next metadatum from tape:
   Read the next metadatum from tape:
    - EOF or EOM: allocate remainder of bytes needed.
    - TMK or GAP: allocate sizeof(metadatum) bytes.
    - Reverse GAP: allocate sizeof(metadatum) / 2 bytes.
    - Data record: see below.

   Loop until bytes needed = 0.
   Loop until the bytes needed = 0.
*/

do {
    sim_fread (&meta, meta_size, 1, uptr->fileref);     /* read metadatum */
    xfer = sim_fread (&meta, meta_size, 1, uptr->fileref);  /* read a metadatum */

    if (ferror (uptr->fileref)) {                       /* read error? */
        uptr->pos = gap_pos;                            /* restore original position */
        MT_SET_PNU (uptr);                              /* position not updated */
        return sim_tape_ioerr (uptr);                   /* translate error */
        }
    else
        uptr->pos = uptr->pos + meta_size;              /* move tape over datum */

    else if (xfer != 1 && feof (uptr->fileref) == 0) {  /* otherwise if a partial metadatum was read */
        uptr->pos = gap_pos;                            /*   then restore the original position */
        MT_SET_PNU (uptr);                              /* set the position-not-updated flag */
        return MTSE_INVRL;                              /*   and return an invalid record length error */
        }

    else                                                /* otherwise we had a good read */
        uptr->pos = uptr->pos + meta_size;              /*   so move the tape over the datum */

    if (feof (uptr->fileref) || (meta == MTR_EOM)) {    /* at eof or eom? */
        gap_alloc = gap_alloc + gap_needed;             /* allocate remainder */
        gap_needed = 0;
        }

    else if ((meta == MTR_GAP) || (meta == MTR_TMK)) {  /* gap or tape mark? */
        gap_alloc = gap_alloc + meta_size;              /* allocate marker space */
        gap_needed = gap_needed - meta_size;            /* reduce requirement */
        }

    else if (gap_size == meta_size) {                   /* otherwise if the request is for a single metadatum */
        uptr->pos = gap_pos;                            /*   then restore the original position */
        MT_SET_PNU (uptr);                              /* set the position-not-updated flag */
        return MTSE_INVRL;                              /*   and return an invalid record length error */
        }

    else if (meta == MTR_FHGAP) {                       /* half gap? */
        uptr->pos = uptr->pos - meta_size / 2;          /* backup to resync */

        sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */
        if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */
            return sim_tape_ioerr (uptr);                   /*   then quit with I/O error status */

        gap_alloc = gap_alloc + meta_size / 2;          /* allocate marker space */
        gap_needed = gap_needed - meta_size / 2;        /* reduce requirement */
        }

    else if (uptr->pos +
             MTR_L (meta) + meta_size > file_size) {    /* rec len out of range? */
        gap_alloc = gap_alloc + gap_needed;             /* presume overwritten tape */
        gap_needed = 0;                                 /* allocate remainder */
    else if (uptr->pos + MTR_L (meta) + meta_size > file_size) {    /* rec len out of range? */
        gap_alloc = gap_alloc + gap_needed;                         /* presume overwritten tape */
        gap_needed = 0;                                             /* allocate remainder */
        }

/* Allocate a data record:
    - Determine record size in bytes (including metadata)
    - If record size - bytes needed < smallest allowed record size,
      allocate entire record to gap, else allocate needed amount and
      truncate data record to reflect remainder.
*/

    else {                                              /* data record */
        sbc = MTR_L (meta);                             /* get record data length */
        rec_size = ((sbc + 1) & ~1) + meta_size * 2;    /* overall size in bytes */

        if (rec_size < gap_needed + min_rec_size) {         /* rec too small? */
            uptr->pos = uptr->pos - meta_size + rec_size;   /* position past record */

            sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */
            if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */
                return sim_tape_ioerr (uptr);                   /*   then quit with I/O error status */

            gap_alloc = gap_alloc + rec_size;               /* allocate record */
            gap_needed = gap_needed - rec_size;             /* reduce requirement */
            }

        else {                                              /* record size OK */
            uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */
            new_len = MTR_F (meta) | (sbc - gap_needed);    /* truncate to new len */
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
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







-
+





+




+








+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















+
+
+
+
+







+
+
-
+
+











+
+
+
+
+







+
+
-
+
+







                }

            gap_alloc = gap_alloc + gap_needed;             /* allocate remainder */
            gap_needed = 0;
            }
        }
    }
while (gap_needed > 0);
while (gap_needed > 0);                                 /* loop until all of the gap has been allocated */

uptr->pos = gap_pos;                                    /* reposition to gap start */

if (gap_alloc & (meta_size - 1)) {                      /* gap size "odd?" */
    st = sim_tape_wrdata (uptr, MTR_FHGAP);             /* write half gap marker */

    if (st != MTSE_OK) {                                /* write OK? */
        uptr->pos = gap_pos;                            /* restore orig pos */
        return st;                                      /* PNU was set by wrdata */
        }

    uptr->pos = uptr->pos - meta_size / 2;              /* realign position */
    gap_alloc = gap_alloc - 2;                          /* decrease gap to write */
    }

marker_count = gap_alloc / meta_size;                   /* count of gap markers */

do {
    st = sim_tape_wrdata (uptr, MTR_GAP);               /* write gap markers */

    if (st != MTSE_OK) {                                /* write OK? */
        uptr->pos = gap_pos;                            /* restore orig pos */
        return st;                                      /* PNU was set by wrdata */
        }
    }
while (--marker_count > 0);

return MTSE_OK;
}

/* Erase a gap in the reverse direction (internal routine).

   An erase gap is written in the reverse direction on the tape unit specified
   by "uptr" for the number of bytes specified by "bc".  The status of the
   operation is returned, and the file position is altered as follows:

     Exit Condition       File Position
     ------------------   ------------------
     unit unattached      unchanged
     unsupported format   unchanged
     write protected      unchanged
     read error           unchanged, PNU set
     write error          unchanged, PNU set
     gap written          updated

   If the requested byte count equals the metadatum size, then the routine
   succeeds only if it can overlay a single metadatum (i.e., a tape mark or an
   existing erase gap marker); otherwise, the file position is not altered, and
   MTSE_INVRL (invalid record length) status is returned.


   Implementation notes:

    1. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format.

    2. Erasing a record in the reverse direction currently succeeds only if the
       gap requested occupies the same space as the record located immediately
       before the current file position.  This limitation may be lifted in a
       future update.

    3. The "sim_fread" call cannot return 0 in the absence of an error
       condition.  The preceding "sim_tape_bot" test ensures that "pos" >= 4, so
       "sim_fseek" will back up at least that far, so "sim_fread" will read at
       least one element.  If the call returns zero, an error must have
       occurred, so the "ferror" call must succeed.
*/

static t_stat tape_erase_rev (UNIT *uptr, t_mtrlnt gap_size)
{
const uint32 format = MT_GET_FMT (uptr);                /* the tape format */
const uint32 meta_size = sizeof (t_mtrlnt);             /* the number of bytes per metadatum */
t_stat   status;
t_mtrlnt rec_size, metadatum;
t_addr   gap_pos;
size_t   xfer;

MT_CLR_PNU (uptr);                                      /* clear the position-not-updated flag */

if ((uptr->flags & UNIT_ATT) == 0)                      /* if the unit is not attached */
    return MTSE_UNATT;                                  /*   then we cannot proceed */

else if (sim_tape_wrp (uptr))                           /* otherwise if the unit is write protected */
    return MTSE_WRP;                                    /*   then we cannot write */

else if (gap_size == 0 || format != MTUF_F_STD)         /* otherwise if the gap length is zero or unsupported */
    return MTSE_OK;                                     /*   then take no action */

gap_pos = uptr->pos;                                    /* save the starting position */

if (gap_size == meta_size) {                            /* if the request is for a single metadatum */
    if (sim_tape_bot (uptr))                            /*   then if the unit is positioned at the BOT */
        return MTSE_BOT;                                /*     then erasing backward is not possible */
    else                                                /*   otherwise */
        uptr->pos -= meta_size;                         /*     back up the file pointer */

    if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */
        return sim_tape_ioerr (uptr);                   /*   then quit with I/O error status */

    sim_fread (&metadatum, meta_size, 1, uptr->fileref);    /* read a metadatum */

    if (ferror (uptr->fileref))                             /* if a file I/O error occurred */
        return sim_tape_ioerr (uptr);                       /*   then report the error and quit */

    else if (metadatum == MTR_TMK)                          /* otherwise if a tape mark is present */
        if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /*   then reposition the tape; if it fails */
            return sim_tape_ioerr (uptr);                   /*     then quit with I/O error status */

        else {                                              /*   otherwise */
            metadatum = MTR_GAP;                            /*     replace it with an erase gap marker */

            xfer = sim_fwrite (&metadatum, meta_size,   /* write the gap marker */
                               1, uptr->fileref);

            if (ferror (uptr->fileref) || xfer == 0)    /* if a file I/O error occurred */
                return sim_tape_ioerr (uptr);           /* report the error and quit */
            else                                        /* otherwise the write succeeded */
                status = MTSE_OK;                       /*   so return success */
            }

    else if (metadatum == MTR_GAP)                      /* otherwise if a gap already exists */
        status = MTSE_OK;                               /*   then take no additional action */

    else {                                              /* otherwise a data record is present */
        uptr->pos = gap_pos;                            /*   so restore the starting position */
        return MTSE_INVRL;                              /*     and fail with invalid record length status */
        }
    }

else {                                                  /* otherwise it's an erase record request */
    status = sim_tape_rdlntr (uptr, &rec_size);         /*   so get the length of the preceding record */

    if (status == MTSE_OK                               /* if the read succeeded */
      && gap_size == rec_size + 2 * meta_size) {        /*   and the gap will exactly overlay the record */
        gap_pos = uptr->pos;                            /*     then save the gap start position */

        status = tape_erase_fwd (uptr, gap_size);       /* erase the record */

        if (status == MTSE_OK)                          /* if the gap write succeeded */
            uptr->pos = gap_pos;                        /*   the reposition back to the start of the gap */
        }

    else {                                              /* otherwise the read failed or is the wrong size */
        uptr->pos = gap_pos;                            /*   so restore the starting position */

        if (status != MTSE_OK)                          /* if the record was not found */
            return status;                              /*   then return the failure reason */
        else                                            /* otherwise the record is the wrong size */
            return MTSE_INVRL;                          /*   so report an invalid record length */
        }
    }

return status;                                          /* return the status of the erase operation */
}

/* Write an erase gap.

   An erase gap is written in on the tape unit specified by "uptr" for the
   length specified by "gap_size" in tenths of an inch, and the status of the
   operation is returned.  The tape density must have been set via a previous
   sim_tape_set_dens call; if it has not, then no action is taken, and
   MTSE_IOERR is returned.

   If the requested gap length is zero, or the tape format currently selected
   does not support erase gaps, the call succeeds with no action taken.  This
   allows a device simulator that supports writing erase gaps to use the same
   code without worrying about the tape format currently selected by the user.

   Because SIMH tape images do not carry physical parameters (e.g., recording
   density), overwriting a tape image file containing a gap is problematic if
   the density setting is not the same as that used during recording.  There is
   no way to establish a gap of a certain length unequivocally in an image file,
   so this implementation establishes a gap of a certain number of bytes that
   reflect the desired gap length at the tape density in bits per inch used
   during writing.
*/

t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
const uint32 density = bpi [MT_DENS (uptr->dynflags)];  /* the tape density in bits per inch */
const uint32 byte_length = (gaplen * density) / 10;     /* the size of the requested gap in bytes */

if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */

sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen);

if (density == 0)                                       /* if the density has not been set */
    return MTSE_IOERR;                                  /*   then report an I/O error */
else                                                    /* otherwise */
    return tape_erase_fwd (uptr, byte_length);          /*   erase the requested gap size in bytes */
}

t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_wrgap (uptr, gaplen);
AIO_CALL(TOP_RDRR, NULL, NULL, NULL, 0, 0, gaplen, 0, NULL, callback);
return r;
}

/* Erase a record forward.

   An erase gap is written in the forward direction on the tape unit specified
   by "uptr" for a length corresponding to a record containing the number of
   bytes specified by "bc", and the status of the operation is returned.  The
   resulting gap will occupy "bc" bytes plus the size of the record length
   metadata.  This function may be used to erase a record of length "n" in place
   by requesting a gap of length "n".  After erasure, the tape will be
   positioned at the end of the gap.

   If a length of 0 is specified, then the metadatum marker at the current tape
   position will be erased.  If the tape is not positioned at a metadatum
   marker, the routine fails with MTSE_INVRL, and the tape position is
   unchanged.
*/

t_stat sim_tape_errecf (UNIT *uptr, t_mtrlnt bc)
{
const t_mtrlnt meta_size = sizeof (t_mtrlnt);           /* the number of bytes per metadatum */
const t_mtrlnt gap_size = bc + 2 * meta_size;           /* the requested gap size in bytes */

if (bc == 0)                                            /* if a zero-length erase is requested */
    return tape_erase_fwd (uptr, meta_size);            /*   then erase a metadatum marker */
return MTSE_IOERR;                 /* stub return */
else                                                    /* otherwise */
    return tape_erase_fwd (uptr, gap_size);             /*   erase the requested gap */
}

/* Erase a record reverse.

   An erase gap is written in the reverse direction on the tape unit specified
   by "uptr" for a length corresponding to a record containing the number of
   bytes specified by "bc", and the status of the operation is returned.  The
   resulting gap will occupy "bc" bytes plus the size of the record length
   metadata.  This function may be used to erase a record of length "n" in place
   by requesting a gap of length "n".  After erasure, the tape will be
   positioned at the start of the gap.

   If a length of 0 is specified, then the metadatum marker preceding the
   current tape position will be erased.  If the tape is not positioned after a
   metadatum marker, the routine fails with MTSE_INVRL, and the tape position is
   unchanged.
*/

t_stat sim_tape_errecr (UNIT *uptr, t_mtrlnt bc)
{
const t_mtrlnt meta_size = sizeof (t_mtrlnt);           /* the number of bytes per metadatum */
const t_mtrlnt gap_size = bc + 2 * meta_size;           /* the requested gap size in bytes */

if (bc == 0)                                            /* if a zero-length erase is requested */
    return tape_erase_rev (uptr, meta_size);            /*   then erase a metadatum marker */
return MTSE_IOERR;                 /* stub return */
else                                                    /* otherwise */
    return tape_erase_rev (uptr, gap_size);             /*   erase the requested gap */
}

/* Space record forward

   Inputs:
        uptr    =       pointer to tape unit
        bc      =       pointer to size of record skipped
1684
1685
1686
1687
1688
1689
1690
1691

1692
1693
1694
1695
1696
1697
1698
1962
1963
1964
1965
1966
1967
1968

1969
1970
1971
1972
1973
1974
1975
1976







-
+







struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat st;

if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecf(unit=%d)\n", (int)(uptr-ctx->dptr->units));

st = sim_tape_rdlntf (uptr, bc);                        /* get record length */
st = sim_tape_rdrlfwd (uptr, bc);                       /* get record length */
*bc = MTR_L (*bc);
return st;
}

t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
1779
1780
1781
1782
1783
1784
1785
1786

1787
1788
1789
1790
1791
1792
1793
2057
2058
2059
2060
2061
2062
2063

2064
2065
2066
2067
2068
2069
2070
2071







-
+







sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecr(unit=%d)\n", (int)(uptr-ctx->dptr->units));

if (MT_TST_PNU (uptr)) {
    MT_CLR_PNU (uptr);
    *bc = 0;
    return MTSE_OK;
    }
st = sim_tape_rdlntr (uptr, bc);                        /* get record length */
st = sim_tape_rdrlrev (uptr, bc);                       /* get record length */
*bc = MTR_L (*bc);
return st;
}

t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
1877
1878
1879
1880
1881
1882
1883
1884

1885
1886
1887

1888
1889
1890
1891
1892
1893
1894
2155
2156
2157
2158
2159
2160
2161

2162
2163
2164

2165
2166
2167
2168
2169
2170
2171
2172







-
+


-
+







if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecf(unit=%d, count=%d, check_leot=%d)\n", (int)(uptr-ctx->dptr->units), count, check_leot);

if (check_leot) {
    t_mtrlnt rbc;

    st = sim_tape_rdlntr (uptr, &rbc);
    st = sim_tape_rdrlrev (uptr, &rbc);
    last_tapemark = (MTSE_TMK == st);
    if ((st == MTSE_OK) || (st == MTSE_TMK))
        sim_tape_rdlntf (uptr, &rbc);
        sim_tape_rdrlfwd (uptr, &rbc);
    }
*skipped = 0;
*recsskipped = 0;
while (*skipped < count) {                              /* loopo */
    while (1) {
        st = sim_tape_sprecsf (uptr, 0x1ffffff, &filerecsskipped);/* spc recs */
        *recsskipped += filerecsskipped;
2392
2393
2394
2395
2396
2397
2398
2399

2400
2401
2402
2403
2404
2405
2406
2670
2671
2672
2673
2674
2675
2676

2677
2678
2679
2680
2681
2682
2683
2684







-
+







return SCPE_OK;
}

/* Set the tape density.

   Set the density of the specified tape unit either to the value supplied or to
   the value represented by the supplied character string.
   

   If "desc" is NULL, then "val" must be set to one of the MT_DENS_* constants
   in sim_tape.h other than MT_DENS_NONE; the supplied value is used as the tape
   density, and the character string is ignored.  Otherwise, "desc" must point
   at an int32 value containing a set of allowed densities constructed as a
   bitwise OR of the appropriate MT_*_VALID values.  In this case, the string
   pointed to by "cptr" will be parsed for a decimal value corresponding to the
   desired density in bits per inch and validated against the set of allowed
Changes to src/sim_timer.c.
157
158
159
160
161
162
163

164
165
166
167
168
169
170
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171







+







static uint32 sim_idle_rate_ms = 0;
static uint32 sim_os_sleep_min_ms = 0;
static uint32 sim_os_sleep_inc_ms = 0;
static uint32 sim_os_clock_resoluton_ms = 0;
static uint32 sim_os_tick_hz = 0;
static uint32 sim_idle_stable = SIM_IDLE_STDFLT;
static uint32 sim_idle_calib_pct = 0;
static double sim_timer_stop_time = 0;
static uint32 sim_rom_delay = 0;
static uint32 sim_throt_ms_start = 0;
static uint32 sim_throt_ms_stop = 0;
static uint32 sim_throt_type = 0;
static uint32 sim_throt_val = 0;
static uint32 sim_throt_state = SIM_THROT_STATE_INIT;
static double sim_throt_cps;
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
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







+





+










+







+









+







static uint32 rtc_clock_calib_gap2big[SIM_NTIMERS+1] = { 0 };/* Calibrations skipped Gap Too Big */
static uint32 rtc_clock_calib_backwards[SIM_NTIMERS+1] = { 0 };/* Calibrations skipped Clock Running Backwards */
static uint32 sim_idle_cyc_ms = 0;                      /* Cycles per millisecond while not idling */

UNIT sim_timer_units[SIM_NTIMERS+1];                    /* Clock assist units                         */
                                                        /* one for each timer and one for an internal */
                                                        /* clock if no clocks are registered.         */
UNIT sim_stop_unit;                                     /* Stop unit                         */
UNIT sim_internal_timer_unit;                           /* Internal calibration timer */
UNIT sim_throttle_unit;                                 /* one for throttle */

t_stat sim_throt_svc (UNIT *uptr);
t_stat sim_timer_tick_svc (UNIT *uptr);
t_stat sim_timer_stop_svc (UNIT *uptr);

#define DBG_IDL       TIMER_DBG_IDLE        /* idling */
#define DBG_QUE       TIMER_DBG_QUEUE       /* queue activities */
#define DBG_MUX       TIMER_DBG_MUX         /* tmxr queue activities */
#define DBG_TRC       0x008                 /* tracing */
#define DBG_CAL       0x010                 /* calibration activities */
#define DBG_TIM       0x020                 /* timer thread activities */
#define DBG_THR       0x040                 /* throttle activities */
#define DBG_ACK       0x080                 /* interrupt acknowledgement activities */
#define DBG_CHK       0x100                 /* check scheduled activation time*/
#define DBG_INT       0x200                 /* internal timer activities */
DEBTAB sim_timer_debug[] = {
  {"TRACE",   DBG_TRC, "Trace routine calls"},
  {"IDLE",    DBG_IDL, "Idling activities"},
  {"QUEUE",   DBG_QUE, "Event queuing activities"},
  {"IACK",    DBG_ACK, "interrupt acknowledgement activities"},
  {"CALIB",   DBG_CAL, "Calibration activities"},
  {"TIME",    DBG_TIM, "Activation and scheduling activities"},
  {"INTER",   DBG_INT, "Internal timer activities"},
  {"THROT",   DBG_THR, "Throttling activities"},
  {"MUX",     DBG_MUX, "Tmxr scheduling activities"},
  {"CHECK",   DBG_CHK, "Check scheduled activation time"},
  {0}
};

/* Forward device declarations */
extern DEVICE sim_timer_dev;
extern DEVICE sim_throttle_dev;
extern DEVICE sim_stop_dev;


void sim_rtcn_init_all (void)
{
int32 tmr;

for (tmr = 0; tmr <= SIM_NTIMERS; tmr++)
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
902
903
904
905
906
907
908







909
910
911
912

913
914
915
916
917
918
919
920







-
-
-
-
-
-
-




-
+







    rtc_clock_catchup_pending[tmr] = FALSE;
    }
rtc_ticks[tmr] = rtc_ticks[tmr] + 1;                    /* count ticks */
if (rtc_ticks[tmr] < ticksper)                          /* 1 sec yet? */
    return rtc_currd[tmr];
rtc_ticks[tmr] = 0;                                     /* reset ticks */
rtc_elapsed[tmr] = rtc_elapsed[tmr] + 1;                /* count sec */
if (sim_throt_type != SIM_THROT_NONE) {
    rtc_gtime[tmr] = sim_gtime();                       /* save instruction time */
    rtc_currd[tmr] = (int32)(sim_throt_cps / ticksper); /* use throttle calibration */
    ++rtc_calibrations[tmr];                            /* count calibrations */
    sim_debug (DBG_CAL, &sim_timer_dev, "using throttle calibrated value - result: %d\n", rtc_currd[tmr]);
    return rtc_currd[tmr];
    }
if (!rtc_avail)                                         /* no timer? */
    return rtc_currd[tmr];
if (sim_calb_tmr != tmr) {
    rtc_currd[tmr] = (int32)(sim_timer_inst_per_sec()/ticksper);
    sim_debug (DBG_CAL, &sim_timer_dev, "calibrated calibrated tmr=%d against system tmr=%d, tickper=%d (result: %d)\n", tmr, sim_calb_tmr, ticksper, rtc_currd[tmr]);
    sim_debug (DBG_CAL, &sim_timer_dev, "calibrated calibrated tmr=%d against internal system tmr=%d, tickper=%d (result: %d)\n", tmr, sim_calb_tmr, ticksper, rtc_currd[tmr]);
    return rtc_currd[tmr];
    }
new_rtime = sim_os_msec ();                             /* wall time */
++rtc_calibrations[tmr];                                /* count calibrations */
sim_debug (DBG_TRC, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d, tmr=%d)\n", ticksper, tmr);
if (new_rtime < rtc_rtime[tmr]) {                       /* time running backwards? */
    /* This happens when the value returned by sim_os_msec wraps (as an uint32) */
1025
1026
1027
1028
1029
1030
1031

1032
1033
1034
1035
1036
1037
1038
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038







+








sim_debug (DBG_TRC, &sim_timer_dev, "sim_timer_init()\n");
for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
    sim_timer_units[tmr].action = &sim_timer_tick_svc;
    sim_timer_units[tmr].flags = UNIT_DIS | UNIT_IDLE;
    sim_clock_cosched_queue[tmr] = QUEUE_LIST_END;
    }
sim_stop_unit.action = &sim_timer_stop_svc;
SIM_INTERNAL_UNIT.flags = UNIT_IDLE;
sim_register_internal_device (&sim_timer_dev);          /* Register Clock Assist device */
sim_throttle_unit.action = &sim_throt_svc;
sim_register_clock_unit_tmr (&SIM_INTERNAL_UNIT, SIM_INTERNAL_CLK);
sim_idle_enab = FALSE;                                  /* init idle off */
sim_idle_rate_ms = sim_os_ms_sleep_init ();             /* get OS timer rate */
sim_set_rom_delay_factor (sim_get_rom_delay_factor ()); /* initialize ROM delay factor */
1299
1300
1301
1302
1303
1304
1305




















1306
1307
1308
1309
1310
1311
1312
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







if ((r != SCPE_OK) || (newpct == (int32)(sim_idle_calib_pct)))
    return r;
if (newpct == 0)
    return SCPE_ARG;
sim_idle_calib_pct = (uint32)newpct;
return SCPE_OK;
}

/* Set stop time */

t_stat sim_timer_set_stop (int32 flag, CONST char *cptr)
{
t_stat r;
t_value stop_time;

if (cptr == NULL)
    return SCPE_ARG;
stop_time = get_uint (cptr, 10, T_VALUE_MAX, &r);
if (r != SCPE_OK)
    return r;
if (stop_time <= (t_value)sim_gtime())
    return SCPE_ARG;
sim_register_internal_device (&sim_stop_dev);           /* Register Stop Device */
sim_timer_stop_time = (double)stop_time;
sim_activate_abs (&sim_stop_unit, (int32)(sim_timer_stop_time - sim_gtime()));
return SCPE_OK;
}

/* Set/Clear asynch */

t_stat sim_timer_set_async (int32 flag, CONST char *cptr)
{
if (flag) {
    if (sim_asynch_enabled && (!sim_asynch_timer)) {
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
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







+


















+
+
+
+
+










-
+
+







+
+
+
+
+
+
+
+

-
-
+
+
+
+







#if defined (SIM_ASYNCH_CLOCKS)
    { "ASYNCH",     &sim_timer_set_async, 1 },
    { "NOASYNCH",   &sim_timer_set_async, 0 },
#endif
    { "CATCHUP",    &sim_timer_set_catchup,  1 },
    { "NOCATCHUP",  &sim_timer_set_catchup,  0 },
    { "CALIB",      &sim_timer_set_idle_pct, 0 },
    { "STOP",       &sim_timer_set_stop, 0 },
    { NULL, NULL, 0 }
    };

MTAB sim_timer_mod[] = {
  { 0 },
};

static t_stat sim_timer_clock_reset (DEVICE *dptr);

static const char *sim_timer_description (DEVICE *dptr)
{
return "Clock Assist facilities";
}

static const char *sim_int_timer_description (DEVICE *dptr)
{
return "Internal Timer";
}

static const char *sim_int_stop_description (DEVICE *dptr)
{
return "Stop facility";
}

static const char *sim_throttle_description (DEVICE *dptr)
{
return "Throttle facility";
}

DEVICE sim_timer_dev = {
    "INT-CLOCK", sim_timer_units, sim_timer_reg, sim_timer_mod, 
    SIM_NTIMERS+1, 0, 0, 0, 0, 0, 
    NULL, NULL, &sim_timer_clock_reset, NULL, NULL, NULL, 
    NULL, DEV_DEBUG | DEV_NOSAVE, 0, sim_timer_debug};
    NULL, DEV_DEBUG | DEV_NOSAVE, 0, 
    sim_timer_debug};

DEVICE sim_int_timer_dev = {
    "INT-TIMER", &sim_internal_timer_unit, NULL, NULL, 
    1, 0, 0, 0, 0, 0, 
    NULL, NULL, NULL, NULL, NULL, NULL, 
    NULL, DEV_NOSAVE};

DEVICE sim_stop_dev = {
    "INT-STOP", &sim_stop_unit, NULL, NULL, 
    1, 0, 0, 0, 0, 0, 
    NULL, NULL, NULL, NULL, NULL, NULL, 
    NULL, DEV_NOSAVE, 0, 
    NULL, NULL, NULL, NULL, NULL, NULL,
    sim_int_stop_description};

DEVICE sim_throttle_dev = {
    "INT-THROTTLE", &sim_throttle_unit, sim_throttle_reg, NULL, 1};

    "INT-THROTTLE", &sim_throttle_unit, sim_throttle_reg, NULL,
    1, 0, 0, 0, 0, 0, 
    NULL, NULL, NULL, NULL, NULL, NULL, 
    NULL, DEV_NOSAVE};

/* SET CLOCK command */

t_stat sim_set_timers (int32 arg, CONST char *cptr)
{
char *cvptr, gbuf[CBUFSIZE];
CTAB *ctptr;
1598
1599
1600
1601
1602
1603
1604


1605
1606
1607



1608

1609
1610
1611
1612
1613
1614
1615
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649

1650
1651
1652
1653
1654
1655
1656
1657







+
+



+
+
+
-
+







                sim_throt_val = (uint32) (val * (sim_idle_rate_ms / val2));
                }
            else {
                sim_throt_sleep_time = sim_idle_rate_ms;
                sim_throt_val = (uint32) (val * (1 + (sim_idle_rate_ms / val2)));
                }
            }
        sim_throt_state = SIM_THROT_STATE_THROTTLE;         /* force state */
        sim_throt_wait = sim_throt_val;
        }
    }
sim_register_internal_device (&sim_throttle_dev);       /* Register Throttle Device */
if (sim_throt_type == SIM_THROT_SPC)    /* Set initial value while correct one is determined */
    sim_throt_cps = (int32)((1000.0 * sim_throt_val) / (double)sim_throt_sleep_time);
else
sim_throt_cps = SIM_INITIAL_IPS;    /* Initial value while correct one is determined */
    sim_throt_cps = SIM_INITIAL_IPS;
return SCPE_OK;
}

t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
{
if (sim_idle_rate_ms == 0)
    fprintf (st, "Throttling:                    Not Available\n");
1631
1632
1633
1634
1635
1636
1637

1638

1639
1640
1641
1642
1643
1644
1645
1673
1674
1675
1676
1677
1678
1679
1680

1681
1682
1683
1684
1685
1686
1687
1688







+
-
+







    case SIM_THROT_PCT:
        fprintf (st, "Throttle:                      %d%%\n", sim_throt_val);
        if (sim_throt_wait)
            fprintf (st, "Throttling by sleeping for:    %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_wait);
        break;

    case SIM_THROT_SPC:
        fprintf (st, "Throttle:                      %d/%d\n", sim_throt_val, sim_throt_sleep_time);
        fprintf (st, "Throttle:                      sleep %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_val);
        fprintf (st, "Throttling by sleeping for:    %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_val);
        break;

    default:
        fprintf (st, "Throttling:                    Disabled\n");
        break;
        }
    if (sim_throt_type != SIM_THROT_NONE) {
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
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







-
-
-
-



-
+


+
-
-
+
+
+
+
+
+
+
+







*/
t_stat sim_throt_svc (UNIT *uptr)
{
int32 tmr;
uint32 delta_ms;
double a_cps, d_cps;

if (sim_throt_type == SIM_THROT_SPC) {                  /* Non dynamic? */
    sim_throt_state = SIM_THROT_STATE_THROTTLE;         /* force state */
    sim_throt_wait = sim_throt_val;
    }
switch (sim_throt_state) {

    case SIM_THROT_STATE_INIT:                          /* take initial reading */
        sim_idle_ms_sleep (sim_idle_rate_ms);           /* start on a tick boundart to calibrate */
        sim_idle_ms_sleep (sim_idle_rate_ms);           /* start on a tick boundary to calibrate */
        sim_throt_ms_start = sim_os_msec ();
        sim_throt_inst_start = sim_gtime();
        if (sim_throt_type != SIM_THROT_SPC) {          /* dynamic? */
        sim_throt_wait = SIM_THROT_WST;
        sim_throt_state = SIM_THROT_STATE_TIME;         /* next state */
            sim_throt_wait = SIM_THROT_WST;
            sim_throt_state = SIM_THROT_STATE_TIME;     /* next state */
            }
        else {                                          /* Non dynamic? */
            sim_throt_wait = sim_throt_val;
            sim_throt_state = SIM_THROT_STATE_THROTTLE; /* force state */
            sim_throt_cps = (int32)((1000.0 * sim_throt_val) / (double)sim_throt_sleep_time);
            }
        sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc(INIT) Starting.  Values wait = %d\n", sim_throt_wait);
        break;                                          /* reschedule */

    case SIM_THROT_STATE_TIME:                          /* take final reading */
        sim_throt_ms_stop = sim_os_msec ();
        delta_ms = sim_throt_ms_stop - sim_throt_ms_start;
        if (delta_ms < SIM_THROT_MSMIN) {               /* not enough time? */
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
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







-
-
-
-
+
+
+
+
+











-


+
+
+
+
-
+
-
-
+
+







                    }
            }
        break;

    case SIM_THROT_STATE_THROTTLE:                      /* throttling */
        sim_idle_ms_sleep (sim_throt_sleep_time);
        delta_ms = sim_os_msec () - sim_throt_ms_start;
        if (sim_throt_type != SIM_THROT_SPC) {          /* when not dynamic throttling */
            if (delta_ms >= 10000) {                    /* recompute every 10 sec */
                double delta_insts = sim_gtime() - sim_throt_inst_start;
                a_cps = (delta_insts * 1000.0) / (double) delta_ms;
        if (delta_ms >= 10000) {                        /* recompute every 10 sec */
            double delta_insts = sim_gtime() - sim_throt_inst_start;

            a_cps = (delta_insts * 1000.0) / (double) delta_ms;
            if (sim_throt_type != SIM_THROT_SPC) {      /* when not dynamic throttling */
                if (sim_throt_type == SIM_THROT_MCYC)   /* calc desired cps */
                    d_cps = (double) sim_throt_val * 1000000.0;
                else if (sim_throt_type == SIM_THROT_KCYC)
                    d_cps = (double) sim_throt_val * 1000.0;
                else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0;
                if (fabs(100.0 * (d_cps - a_cps) / a_cps) > (double)SIM_THROT_DRIFT_PCT) {
                    sim_throt_wait = sim_throt_val;
                    sim_throt_state = SIM_THROT_STATE_TIME;/* next state to recalibrate */
                    sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Recalibrating throttle based on values a_cps = %f, d_cps = %f\n", 
                                                        a_cps, d_cps);
                    }
                sim_throt_ms_start = sim_os_msec ();
                sim_throt_inst_start = sim_gtime();
                }
            else {                                      /* record instruction rate */
                sim_throt_cps = (int32)a_cps;
                sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Recalibrating Special %d/%u Cycles Per Second of %f\n", 
                                                    sim_throt_wait, sim_throt_sleep_time, sim_throt_cps);
            }
                }
        else                                            /* record instruction rate */
            sim_throt_cps = (int32)((1000.0 * sim_throt_val) / (double)delta_ms);
            sim_throt_ms_start = sim_os_msec ();
            }
        break;
        }

sim_activate (uptr, sim_throt_wait);                    /* reschedule */
return SCPE_OK;
}

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







+
+
+










+

-
+

-
-
-






+
+
+
+
+
+
+
+
+
+

-

-
+



-
+


-
-
+



+
+
+
+
+







stat = sim_clock_unit[tmr]->action (sim_clock_unit[tmr]);
--sim_cosched_interval[tmr];                    /* Countdown ticks */
if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END)
    sim_clock_cosched_queue[tmr]->time = sim_cosched_interval[tmr];
if ((stat == SCPE_OK)                               && 
    (sim_cosched_interval[tmr] <= 0)                &&
    (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END)) {
    UNIT *sptr = sim_clock_cosched_queue[tmr];
    UNIT *cptr = QUEUE_LIST_END;

    if (rtc_clock_catchup_eligible[tmr]) {      /* calibration started? */
        struct timespec now;
        double skew;

        clock_gettime(CLOCK_REALTIME, &now);
        skew = (_timespec_to_double(&now) - (rtc_calib_tick_time[tmr]+rtc_clock_catchup_base_time[tmr]));

        if (fabs(skew) > fabs(rtc_clock_skew_max[tmr]))
            rtc_clock_skew_max[tmr] = skew;
        }
    /* First gather the queued events that are scheduled for now */
    do {
        UNIT *cptr = sim_clock_cosched_queue[tmr];
        cptr = sim_clock_cosched_queue[tmr];
        sim_clock_cosched_queue[tmr] = cptr->next;
        cptr->next = NULL;
        cptr->cancel = NULL;
        cptr->time = 0;
        if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) {
            sim_clock_cosched_queue[tmr]->time += sim_cosched_interval[tmr];
            sim_cosched_interval[tmr] = sim_clock_cosched_queue[tmr]->time;
            }
        else
            sim_cosched_interval[tmr]  = 0;
        } while ((sim_cosched_interval[tmr] <= 0) &&
                 (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END));
    if (cptr != QUEUE_LIST_END)
        cptr->next = QUEUE_LIST_END;
    /* Now dispatch that list (in order). */
    while (sptr != QUEUE_LIST_END) {
        cptr = sptr;
        sptr = sptr->next;
        cptr->next = NULL;
        cptr->cancel = NULL;
        cptr->time = 0;
        sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_tick_svc(tmr=%d) - coactivating %s", tmr, sim_uname (cptr));
        if (cptr->usecs_remaining) {
            sim_debug (DBG_QUE, &sim_timer_dev, " remnant: %.0f - next %s after cosched interval: %d ticks\n", cptr->usecs_remaining, (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) ? sim_uname (sim_clock_cosched_queue[tmr]) : "", sim_cosched_interval[tmr]);
            sim_debug (DBG_QUE, &sim_timer_dev, " remnant: %.0f - next %s after cosched interval: %d ticks\n", cptr->usecs_remaining, (sptr != QUEUE_LIST_END) ? sim_uname (sptr) : "", sim_cosched_interval[tmr]);
            sim_timer_activate_after (cptr, cptr->usecs_remaining);
            }
        else {
            sim_debug (DBG_QUE, &sim_timer_dev, " - next %s after cosched interval: %d ticks\n", (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) ? sim_uname (sim_clock_cosched_queue[tmr]) : "", sim_cosched_interval[tmr]);
            sim_debug (DBG_QUE, &sim_timer_dev, " - next %s after cosched interval: %d ticks\n", (sptr != QUEUE_LIST_END) ? sim_uname (sptr) : "", sim_cosched_interval[tmr]);
            _sim_activate (cptr, 0);
            }
        } while ((sim_cosched_interval[tmr] <= 0) &&
             (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END));
        }
    }
return stat;
}

t_stat sim_timer_stop_svc (UNIT *uptr)
{
return SCPE_STOP;
}

void sim_rtcn_get_time (struct timespec *now, int tmr)
{
sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_get_time(tmr=%d)\n", tmr);
clock_gettime (CLOCK_REALTIME, now);
}

2067
2068
2069
2070
2071
2072
2073

2074
2075
2076
2077
2078
2079
2080
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145







+








#define CLK_TPS 10
#define CLK_INIT (SIM_INITIAL_IPS/CLK_TPS)
static int32 sim_int_clk_tps;

static t_stat sim_timer_clock_tick_svc (UNIT *uptr)
{
sim_debug(DBG_INT, &sim_timer_dev, "sim_timer_clock_tick_svc()\n");
sim_rtcn_calb (sim_int_clk_tps, SIM_INTERNAL_CLK);
sim_activate_after (uptr, 1000000/sim_int_clk_tps);     /* reactivate unit */
return SCPE_OK;
}

/* 
  This routine exists to assure that there is a single reliably calibrated 
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
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







-
+












-
+







            rtc_hz[sim_calb_tmr] = 0;                           /* back to 0 */
            if (sim_clock_unit[sim_calb_tmr])
                sim_cancel (sim_clock_unit[sim_calb_tmr]);
            sim_cancel (&sim_timer_units[sim_calb_tmr]);
            }
        /* Start the internal timer */
        sim_calb_tmr = SIM_NTIMERS;
        sim_debug (DBG_CAL, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Starting Internal Calibrated Timer at %dHz\n", newtmr, sim_int_clk_tps);
        sim_debug (DBG_CAL|DBG_INT, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Starting Internal Calibrated Timer at %dHz\n", newtmr, sim_int_clk_tps);
        SIM_INTERNAL_UNIT.action = &sim_timer_clock_tick_svc;
        SIM_INTERNAL_UNIT.flags = UNIT_IDLE;
        sim_register_internal_device (&sim_int_timer_dev);      /* Register Internal timer device */
        sim_rtcn_init_unit (&SIM_INTERNAL_UNIT, (CLK_INIT*CLK_TPS)/sim_int_clk_tps, SIM_INTERNAL_CLK);
        SIM_INTERNAL_UNIT.action (&SIM_INTERNAL_UNIT);          /* Force tick to activate timer */
        }
    return;
    }
if ((tmr == newtmr) && 
    (sim_calb_tmr == newtmr))               /* already set? */
    return;
if (sim_calb_tmr == SIM_NTIMERS) {      /* was old the internal timer? */
    sim_debug (DBG_CAL, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Stopping Internal Calibrated Timer, New Timer = %d (%dHz)\n", newtmr, tmr, rtc_hz[tmr]);
    sim_debug (DBG_CAL|DBG_INT, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Stopping Internal Calibrated Timer, New Timer = %d (%dHz)\n", newtmr, tmr, rtc_hz[tmr]);
    rtc_initd[SIM_NTIMERS] = 0;
    rtc_hz[SIM_NTIMERS] = 0;
    sim_register_clock_unit_tmr (NULL, SIM_INTERNAL_CLK);
    sim_cancel (&SIM_INTERNAL_UNIT);
    sim_cancel (&sim_timer_units[SIM_NTIMERS]);
    }
else {
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
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







-
+












+











+
+








            _sim_coschedule_cancel (uptr);
            _sim_activate (uptr, 1);
            uptr->usecs_remaining = usecs_remaining;
            }
        rtc_hz[sim_calb_tmr] = 0;                           /* back to 0 */
        }
    sim_debug (DBG_CAL, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Changing Calibrated Timer from %d (%dHz) to %d (%dHz)\n", newtmr, sim_calb_tmr, rtc_hz[sim_calb_tmr], tmr, rtc_hz[tmr]);
    sim_debug (DBG_CAL|DBG_INT, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Changing Calibrated Timer from %d (%dHz) to %d (%dHz)\n", newtmr, sim_calb_tmr, rtc_hz[sim_calb_tmr], tmr, rtc_hz[tmr]);
    sim_calb_tmr = tmr;
    }
sim_calb_tmr = tmr;
}

static t_stat sim_timer_clock_reset (DEVICE *dptr)
{
sim_debug (DBG_TRC, &sim_timer_dev, "sim_timer_clock_reset()\n");
_rtcn_configure_calibrated_clock (sim_calb_tmr);
sim_timer_dev.description = &sim_timer_description;
sim_throttle_dev.description = &sim_throttle_description;
sim_int_timer_dev.description = &sim_int_timer_description;
sim_stop_dev.description = &sim_int_stop_description;
if (sim_switches & SWMASK ('P')) {
    sim_cancel (&SIM_INTERNAL_UNIT);
    sim_calb_tmr = -1;
    }
return SCPE_OK;
}

void sim_start_timer_services (void)
{
sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services()\n");
_rtcn_configure_calibrated_clock (sim_calb_tmr);
if (sim_timer_stop_time > sim_gtime())
    sim_activate_abs (&sim_stop_unit, (int32)(sim_timer_stop_time - sim_gtime()));
#if defined(SIM_ASYNCH_CLOCKS)
pthread_mutex_lock (&sim_timer_lock);
if (sim_asynch_timer) {
    pthread_attr_t attr;

    sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services() - starting\n");
    pthread_cond_init (&sim_timer_startup_cond, NULL);
2217
2218
2219
2220
2221
2222
2223

2224

2225
2226
2227
2228
2229
2230
2231
2232
2285
2286
2287
2288
2289
2290
2291
2292

2293

2294
2295
2296
2297
2298
2299
2300







+
-
+
-








    if (sim_clock_unit[tmr]) {
        int32 clock_time = _sim_activate_time (&sim_timer_units[tmr]);

        if (clock_time < 0)
            clock_time = 0;
        /* Stop clock assist unit and make sure the clock unit has a tick queued */
        if (sim_is_active (&sim_timer_units[tmr])) {
        sim_cancel (&sim_timer_units[tmr]);
            sim_cancel (&sim_timer_units[tmr]);
        if (rtc_hz[tmr]) {
            sim_debug (DBG_QUE, &sim_timer_dev, "sim_stop_timer_services() - tmr=%d scheduling %s after %d\n", tmr, sim_uname (sim_clock_unit[tmr]), clock_time);
            _sim_activate (sim_clock_unit[tmr], clock_time);
            }
        /* Move coscheduled units to the standard event queue */
        accum = 0;
        while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) {
            UNIT *cptr = sim_clock_cosched_queue[tmr];
2353
2354
2355
2356
2357
2358
2359
2360

2361
2362
2363
2364
2365
2366
2367
2421
2422
2423
2424
2425
2426
2427

2428
2429
2430
2431
2432
2433
2434
2435







-
+







if ((sim_calb_tmr != -1) && (rtc_hz[sim_calb_tmr])) {       /* Calibrated Timer available? */
    int32 inst_til_tick = sim_activate_time (&sim_timer_units[sim_calb_tmr]) - 1;
    int32 ticks_til_calib = rtc_hz[sim_calb_tmr] - rtc_ticks[sim_calb_tmr];
    int32 inst_til_calib = inst_til_tick + ((ticks_til_calib - 1) * rtc_currd[sim_calb_tmr]);
    uint32 usecs_til_calib = (uint32)ceil(inst_til_calib / inst_per_usec);

    if (uptr != &sim_timer_units[sim_calb_tmr]) {           /* Not scheduling calibrated timer? */
        if (inst_delay_d >= (double)inst_til_calib) {       /* long wait? */
        if (inst_delay_d > (double)inst_til_calib) {        /* long wait? */
            stat = sim_clock_coschedule_tmr (uptr, sim_calb_tmr, ticks_til_calib - 1);
            uptr->usecs_remaining = (stat == SCPE_OK) ? usec_delay - usecs_til_calib : 0.0;
            sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - coscheduling with with calibrated timer(%d), ticks=%d, usecs_remaining=%.0f usecs, inst_til_tick=%d\n", 
                       sim_uname(uptr), usec_delay, sim_calb_tmr, ticks_til_calib, uptr->usecs_remaining, inst_til_tick);
            sim_debug (DBG_CHK, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - result = %.0f usecs, %.0f usecs\n", 
                       sim_uname(uptr), usec_delay, sim_timer_activate_time_usecs (ouptr), sim_timer_activate_time_usecs (uptr));
            return stat;
Changes to src/sim_tmxr.c.
460
461
462
463
464
465
466
467

468
469
470
471
472
473
474
460
461
462
463
464
465
466

467
468
469
470
471
472
473
474







-
+







{
lp->tsta = 0;                                           /* init telnet state */
lp->xmte = 1;                                           /* enable transmit */
lp->dstb = 0;                                           /* default bin mode */
lp->rxbpr = lp->rxbpi = lp->rxcnt = lp->rxpcnt = 0;     /* init receive indexes */
if (!lp->txbfd || lp->notelnet)                         /* if not buffered telnet */
    lp->txbpr = lp->txbpi = lp->txcnt = lp->txpcnt = 0; /*   init transmit indexes */
lp->txdrp = 0;
lp->txdrp = lp->txstall = 0;
tmxr_set_get_modem_bits (lp, 0, 0, NULL);
if ((!lp->mp->buffered) && (!lp->txbfd)) {
    lp->txbfd = 0;
    lp->txbsz = TMXR_MAXBUF;
    lp->txb = (char *)realloc (lp->txb, lp->txbsz);
    lp->rxbsz = TMXR_MAXBUF;
    lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
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
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







-
+










+
+
+
+
+





+
+
+
+








    sprintf (msgbuf, "%s%s%s\r\n\n", cmsg, dmsg, lmsg);
    }

if (!mp->buffered) {
    lp->txbpi = 0;                                      /* init buf pointers */
    lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf));
    lp->rxcnt = lp->txcnt = lp->txdrp = 0;              /* init counters */
    lp->rxcnt = lp->txcnt = lp->txdrp = lp->txstall = 0;/* init counters */
    lp->rxpcnt = lp->txpcnt = 0;
    }
else
    if (lp->txcnt > lp->txbsz)
        lp->txbpr = (lp->txbpi + 1) % lp->txbsz;
    else
        lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf));

psave = lp->txbpi;                                      /* save insertion pointer */
lp->txbpi = lp->txbpr;                                  /* insert connection message */
if ((lp->serport) && (!sim_is_running)) {
    sim_os_ms_sleep (TMXR_DTR_DROP_TIME);               /* Wait for DTR to be noticed */
    lp->ser_connect_pending = FALSE;                    /* Mark line as ready for action */
    lp->conn = TRUE;
    }
tmxr_linemsg (lp, msgbuf);                              /* beginning of buffer */
lp->txbpi = psave;                                      /* restore insertion pointer */

unwritten = tmxr_send_buffered_data (lp);               /* send the message */

if ((lp->serport) && (!sim_is_running)) {
    lp->ser_connect_pending = TRUE;                     /* Mark line as not yet ready for action */
    lp->conn = FALSE;
    }
if (unwritten == 0)                                     /* buffer now empty? */
    lp->xmte = 1;                                       /* reenable transmission if paused */

lp->txcnt -= (int32)strlen (msgbuf);                    /* adjust statistics */
return;
}

693
694
695
696
697
698
699
700

701
702
703
704


705
706
707
708
709
710
711
702
703
704
705
706
707
708

709
710
711


712
713
714
715
716
717
718
719
720







-
+


-
-
+
+







int32 written;
int32 i = lp->txbpr;

if (lp->loopback)
    return loop_write (lp, &(lp->txb[i]), length);

if (lp->serport) {                                      /* serial port connection? */
    if (sim_gtime () < lp->txnexttime)
    if ((sim_gtime () < lp->txnexttime) && (sim_is_running))
        return 0;
    written = sim_write_serial (lp->serport, &(lp->txb[i]), length);
    if (written > 0)
        lp->txnexttime = floor (sim_gtime () + (lp->txdelta * sim_timer_inst_per_sec ()));
    if ((written > 0) && (sim_is_running))
        lp->txnexttime = floor (sim_gtime () + (written * lp->txdelta * sim_timer_inst_per_sec ()));
    return written;
    }
else {                                                  /* Telnet connection */
    written = sim_write_sock (lp->sock, &(lp->txb[i]), length);

    if (written == SOCKET_ERROR)                        /* did an error occur? */
        if (lp->datagram)
2052
2053
2054
2055
2056
2057
2058





2059
2060
2061

2062
2063
2064
2065
2066
2067
2068
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074

2075
2076
2077
2078
2079
2080
2081
2082







+
+
+
+
+


-
+







        TMLN *save_oline = sim_oline;                   /* when logging to a socket */

        sim_oline = NULL;                               /* save output socket */
        fputc (chr, lp->txlog);                         /* log to actual file */
        sim_oline = save_oline;                         /* resture output socket */
        }
    sim_exp_check (&lp->expect, chr);                   /* process expect rules as needed */
    if ((sim_interval > 0) &&                           /* not called within sim_process_event? */
        (lp->txbps) && (lp->txdelta > 1000)) {          /* and rate limiting output slower than 1000 cps */
        tmxr_send_buffered_data (lp);                   /* put data on wire */
        sim_os_ms_sleep((lp->txdelta - 1000) / 1000);   /* wait an approximate character delay */
        }
    return SCPE_OK;                                     /* char sent */
    }
++lp->txdrp; lp->xmte = 0;                              /* no room, dsbl line */
++lp->txstall; lp->xmte = 0;                            /* no room, dsbl line */
return SCPE_STALL;                                      /* char not sent */
}

/* Store packet in line buffer

   Inputs:
        *lp     =       pointer to line descriptor
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2399
2400
2401
2402
2403
2404
2405

2406
2407
2408
2409
2410
2411
2412







-







     port[CBUFSIZE], option[CBUFSIZE], speed[CBUFSIZE];
SOCKET sock;
SERHANDLE serport;
CONST char *tptr = cptr;
t_bool nolog, notelnet, listennotelnet, modem_control, loopback, datagram, packet;
TMLN *lp;
t_stat r = SCPE_OK;
t_bool not_quiet = (!sim_quiet) && (0 == (sim_switches & SWMASK ('Q')));

if (*tptr == '\0')
    return SCPE_ARG;
for (i = 0; i < mp->lines; i++) {               /* initialize lines */
    lp = mp->ldsc + i;
    lp->mp = mp;                                /* set the back pointer */
    lp->modem_control = mp->modem_control;
2439
2440
2441
2442
2443
2444
2445
2446

2447
2448
2449
2450
2451
2452
2453
2452
2453
2454
2455
2456
2457
2458

2459
2460
2461
2462
2463
2464
2465
2466







-
+







                if (r)
                    return sim_messagef (SCPE_ARG, "Invalid Line Specifier: %s\n", cptr);
                break;
                }
            if (0 == MATCH_CMD (gbuf, "LOG")) {
                if ((NULL == cptr) || ('\0' == *cptr))
                    return sim_messagef (SCPE_2FARG, "Missing Log Specifier\n");
                strncpy(logfiletmpl, cptr, sizeof(logfiletmpl)-1);
                strlcpy(logfiletmpl, cptr, sizeof(logfiletmpl));
                continue;
                }
             if (0 == MATCH_CMD (gbuf, "LOOPBACK")) {
                if ((NULL != cptr) && ('\0' != *cptr))
                    return sim_messagef (SCPE_2MARG, "Unexpected Loopback Specifier: %s\n", cptr);
                loopback = TRUE;
                continue;
2505
2506
2507
2508
2509
2510
2511
2512

2513
2514
2515
2516
2517
2518
2519

2520
2521
2522
2523
2524
2525
2526
2518
2519
2520
2521
2522
2523
2524

2525
2526
2527
2528
2529
2530
2531

2532
2533
2534
2535
2536
2537
2538
2539







-
+






-
+







                    return sim_messagef (SCPE_2MARG, "Unexpected Stream Specifier: %s\n", cptr);
                datagram = FALSE;
                continue;
                }
            if (0 == MATCH_CMD (gbuf, "CONNECT")) {
                if ((NULL == cptr) || ('\0' == *cptr))
                    return sim_messagef (SCPE_2FARG, "Missing Connect Specifier\n");
                strncpy (destination, cptr, sizeof(destination)-1);
                strlcpy (destination, cptr, sizeof(destination));
                continue;
                }
            if (0 == MATCH_CMD (gbuf, "SPEED")) {
                if ((NULL == cptr) || ('\0' == *cptr) || 
                    (_tmln_speed_delta (cptr) < 0))
                    return sim_messagef (SCPE_ARG, "Invalid Speed Specifier: %s\n", (cptr ? cptr : ""));
                strncpy (speed, cptr, sizeof(speed)-1);
                strlcpy (speed, cptr, sizeof(speed));
                continue;
                }
            cptr = get_glyph (gbuf, port, ';');
            if (sim_parse_addr (port, NULL, 0, NULL, NULL, 0, NULL, NULL))
                return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port);
            if (cptr) {
                char *tptr = gbuf + (cptr - gbuf);
2563
2564
2565
2566
2567
2568
2569
2570

2571
2572
2573
2574
2575
2576
2577
2576
2577
2578
2579
2580
2581
2582

2583
2584
2585
2586
2587
2588
2589
2590







-
+







            if (strchr (destination, ';') && mp->modem_control && !(sim_switches & SIM_SW_REST))
                return sim_messagef (SCPE_ARG, "Serial line parameters must be set within simulated OS: %s\n", 1 + strchr (destination, ';'));
            }
        else {
            char *eptr;

            memset (hostport, '\0', sizeof(hostport));
            strncpy (hostport, destination, sizeof(hostport)-1);
            strlcpy (hostport, destination, sizeof(hostport));
            if ((eptr = strchr (hostport, ';')))
                *(eptr++) = '\0';
            if (eptr) {
                get_glyph (eptr, eptr, 0);          /* upcase this string */
                if (0 == MATCH_CMD (eptr, "NOTELNET"))
                    notelnet = TRUE;
                else
2590
2591
2592
2593
2594
2595
2596
2597

2598
2599
2600
2601
2602

2603
2604

2605
2606

2607
2608
2609
2610
2611
2612
2613
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







-
+





+

-
+

-
+







                return sim_messagef (SCPE_ARG, "Invalid destination: %s\n", hostport);
            }
        }
    if (line == -1) {
        if (modem_control != mp->modem_control)
            return SCPE_ARG;
        if (logfiletmpl[0]) {
            strncpy(mp->logfiletmpl, logfiletmpl, sizeof(mp->logfiletmpl)-1);
            strlcpy(mp->logfiletmpl, logfiletmpl, sizeof(mp->logfiletmpl));
            for (i = 0; i < mp->lines; i++) {
                lp = mp->ldsc + i;
                sim_close_logfile (&lp->txlogref);
                lp->txlog = NULL;
                lp->txlogname = (char *)realloc(lp->txlogname, CBUFSIZE);
                lp->txlogname[CBUFSIZE-1] = '\0';
                if (mp->lines > 1)
                    sprintf(lp->txlogname, "%s_%d", mp->logfiletmpl, i);
                    snprintf(lp->txlogname, CBUFSIZE-1, "%s_%d", mp->logfiletmpl, i);
                else
                    strcpy (lp->txlogname, mp->logfiletmpl);
                    strlcpy (lp->txlogname, mp->logfiletmpl, CBUFSIZE);
                r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref);
                if (r != SCPE_OK) {
                    free (lp->txlogname);
                    lp->txlogname = NULL;
                    break;
                    }
                }
2650
2651
2652
2653
2654
2655
2656
2657
2658

2659
2660
2661
2662
2663
2664
2665
2664
2665
2666
2667
2668
2669
2670


2671
2672
2673
2674
2675
2676
2677
2678







-
-
+







                return sim_messagef (SCPE_OPENERR, "Can't open network socket for listen port: %s\n", listen);
            if (mp->port) {                                 /* close prior listener */
                sim_close_sock (mp->master);
                mp->master = 0;
                free (mp->port);
                mp->port = NULL;
                }
            if (not_quiet)
                sim_printf ("Listening on port %s\n", listen);
            sim_messagef (SCPE_OK, "Listening on port %s\n", listen);
            mp->port = (char *)realloc (mp->port, 1 + strlen (listen));
            strcpy (mp->port, listen);                      /* save port */
            mp->master = sock;                              /* save master socket */
            mp->ring_sock = INVALID_SOCKET;
            free (mp->ring_ipad);
            mp->ring_ipad = NULL;
            mp->ring_start_time = 0;
2684
2685
2686
2687
2688
2689
2690
2691
2692

2693
2694
2695
2696
2697
2698
2699
2697
2698
2699
2700
2701
2702
2703


2704
2705
2706
2707
2708
2709
2710
2711







-
-
+







                tmxr_init_line (lp);                        /* initialize line state */
                lp->sock = 0;                               /* clear the socket */
                }
            }
        if (loopback) {
            if (mp->lines > 1)
                return sim_messagef (SCPE_ARG, "Ambiguous Loopback specification\n");
            if (not_quiet)
                sim_printf ("Operating in loopback mode\n");
            sim_messagef (SCPE_OK, "Operating in loopback mode\n");
            for (i = 0; i < mp->lines; i++) {
                lp = mp->ldsc + i;
                tmxr_set_line_loopback (lp, loopback);
                if (speed[0])
                    tmxr_set_line_speed (lp, speed);
                }
            }
2716
2717
2718
2719
2720
2721
2722
2723

2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2728
2729
2730
2731
2732
2733
2734

2735

2736

2737
2738
2739
2740
2741
2742
2743







-
+
-

-







                lp->serport = serport;
                lp->ser_connect_pending = TRUE;
                lp->notelnet = TRUE;
                tmxr_init_line (lp);                        /* init the line state */
                if (!lp->mp->modem_control)                 /* raise DTR and RTS for non modem control lines */
                    sim_control_serial (lp->serport, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL);
                lp->cnms = sim_os_msec ();                  /* record time of connection */
                if (sim_switches & SWMASK ('V')) {          /* -V flag reports connection on port */
                if (sim_switches & SWMASK ('V'))            /* -V flag reports connection on port */
                    sim_os_ms_sleep (TMXR_DTR_DROP_TIME);
                    tmxr_report_connection (mp, lp);        /* report the connection to the line */
                    }
                }
            else {
                lp->datagram = datagram;
                if (datagram) {
                    if (listen[0]) {
                        lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
                        strcpy (lp->port, listen);           /* save port */
2803
2804
2805
2806
2807
2808
2809
2810
2811

2812
2813
2814
2815
2816
2817
2818
2813
2814
2815
2816
2817
2818
2819


2820
2821
2822
2823
2824
2825
2826
2827







-
-
+







                return sim_messagef (SCPE_ARG, "Single Line MUX can have either line specific OR MUS listener but NOT both\n");
            sock = sim_master_sock (listen, &r);            /* make master socket */
            if (r)
                return sim_messagef (SCPE_ARG, "Invalid Listen Specification: %s\n", listen);
            if (sock == INVALID_SOCKET)                     /* open error */
                return sim_messagef (SCPE_OPENERR, "Can't listen on port: %s\n", listen);
            _mux_detach_line (lp, TRUE, FALSE);
            if (not_quiet)
                sim_printf ("Line %d Listening on port %s\n", line, listen);
            sim_messagef (SCPE_OK, "Line %d Listening on port %s\n", line, listen);
            lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
            strcpy (lp->port, listen);                       /* save port */
            lp->master = sock;                              /* save master socket */
            if (listennotelnet != mp->notelnet)
                lp->notelnet = listennotelnet;
            else
                lp->notelnet = mp->notelnet;
2826
2827
2828
2829
2830
2831
2832
2833

2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2835
2836
2837
2838
2839
2840
2841

2842

2843

2844
2845
2846
2847
2848
2849
2850







-
+
-

-







                lp->serport = serport;
                lp->ser_connect_pending = TRUE;
                lp->notelnet = TRUE;
                tmxr_init_line (lp);                        /* init the line state */
                if (!lp->mp->modem_control)                 /* raise DTR and RTS for non modem control lines */
                    sim_control_serial (lp->serport, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL);
                lp->cnms = sim_os_msec ();                  /* record time of connection */
                if (sim_switches & SWMASK ('V')) {          /* -V flag reports connection on port */
                if (sim_switches & SWMASK ('V'))            /* -V flag reports connection on port */
                    sim_os_ms_sleep (TMXR_DTR_DROP_TIME);
                    tmxr_report_connection (mp, lp);        /* report the connection to the line */
                    }
                }
            else {
                lp->datagram = datagram;
                if (datagram) {
                    if (listen[0]) {
                        lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
                        strcpy (lp->port, listen);          /* save port */
2862
2863
2864
2865
2866
2867
2868
2869
2870

2871
2872
2873
2874
2875
2876
2877
2869
2870
2871
2872
2873
2874
2875


2876
2877
2878
2879
2880
2881
2882
2883







-
-
+







                    }
                else
                    return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n", datagram ? "Datagram" : "Stream", datagram ? listen : "", datagram ? "<->" : "", hostport);
                }
            }
        if (loopback) {
            tmxr_set_line_loopback (lp, loopback);
            if (not_quiet)
                sim_printf ("Line %d operating in loopback mode\n", line);
            sim_messagef (SCPE_OK, "Line %d operating in loopback mode\n", line);
            }
        lp->modem_control = modem_control;
        if (speed[0] && (!datagram) && (!lp->serport))
            tmxr_set_line_speed (lp, speed);
        r = SCPE_OK;
        }
    }
3533
3534
3535
3536
3537
3538
3539
3540

3541
3542
3543
3544
3545
3546
3547
3548
3539
3540
3541
3542
3543
3544
3545

3546

3547
3548
3549
3550
3551
3552
3553







-
+
-







        found = TRUE;
        break;
        }
if (!found) {
    tmxr_open_devices = (TMXR **)realloc(tmxr_open_devices, (tmxr_open_device_count+1)*sizeof(*tmxr_open_devices));
    tmxr_open_devices[tmxr_open_device_count++] = mux;
    for (i=0; i<mux->lines; i++)
        if (0 == mux->ldsc[i].send.delay)
        mux->ldsc[i].send.after = mux->ldsc[i].send.delay = 0;
            mux->ldsc[i].send.delay = SEND_DEFAULT_DELAY;
    }
#if defined(SIM_ASYNCH_MUX)
pthread_mutex_unlock (&sim_tmxr_poll_lock);
if ((tmxr_open_device_count == 1) && (sim_asynch_enabled))
    tmxr_start_poll ();
#endif
}
3602
3603
3604
3605
3606
3607
3608



































3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628



3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691



3692
3693
3694
3695
3696
3697
3698







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




















+
+
+




















-
-
-







return _tmxr_locate_line_send_expect (cptr, snd, NULL);
}

t_stat tmxr_locate_line_expect (const char *cptr, EXPECT **exp)
{
return _tmxr_locate_line_send_expect (cptr, NULL, exp);
}

static const char *_tmxr_send_expect_line_name (const SEND *snd, const EXPECT *exp)
{
static char line_name[CBUFSIZE];
int i, j;

strcpy (line_name, "");
for (i=0; i<tmxr_open_device_count; ++i)
    for (j=0; j<tmxr_open_devices[i]->lines; ++j)
        if ((snd == &tmxr_open_devices[i]->ldsc[j].send) ||
            (exp == &tmxr_open_devices[i]->ldsc[j].expect)) {
            if (tmxr_open_devices[i]->lines > 1)
                snprintf (line_name, sizeof (line_name), "%s:%d", tmxr_open_devices[i]->ldsc[j].send.dptr->name, j);
            else
                strlcpy (line_name, tmxr_open_devices[i]->ldsc[j].send.dptr->name, sizeof (line_name));
            break;
            }
return line_name;
}

const char *tmxr_send_line_name (const SEND *snd)
{
if (snd == sim_cons_get_send ())
    return "CONSOLE";
else
    return _tmxr_send_expect_line_name (snd, NULL);
}

const char *tmxr_expect_line_name (const EXPECT *exp)
{
if (exp == sim_cons_get_expect ())
    return "CONSOLE";
else
    return _tmxr_send_expect_line_name (NULL, exp);
}

t_stat tmxr_change_async (void)
{
#if defined(SIM_ASYNCH_IO)
if (sim_asynch_enabled)
    tmxr_start_poll ();
else
    tmxr_stop_poll ();
#endif
return SCPE_OK;
}


/* Attach unit to master socket */

t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, CONST char *cptr, t_bool async)
{
t_stat r;
int32 i;

if (mp->dptr == NULL)                                   /* has device been set? */
    mp->dptr = find_dev_from_unit (uptr);               /* no, so set device now */

r = tmxr_open_master (mp, cptr);                        /* open master socket */
if (r != SCPE_OK)                                       /* error? */
    return r;
mp->uptr = uptr;                                        /* save unit for polling */
uptr->filename = tmxr_mux_attach_string (uptr->filename, mp);/* save */
uptr->flags = uptr->flags | UNIT_ATT;                   /* no more errors */
uptr->tmxr = (void *)mp;
if ((mp->lines > 1) ||
    ((mp->master == 0) &&
     (mp->ldsc[0].connecting == 0) &&
     (mp->ldsc[0].serport == 0)))
    uptr->dynflags = uptr->dynflags | UNIT_ATTMULT;     /* allow multiple attach commands */

#if defined(SIM_ASYNCH_MUX)
if (!async || (uptr->flags & TMUF_NOASYNCH))            /* if asynch disabled */
    uptr->dynflags |= TMUF_NOASYNCH;                    /* tag as no asynch */
#else
uptr->dynflags |= TMUF_NOASYNCH;                        /* tag as no asynch */
#endif

if (mp->dptr == NULL)                                   /* has device been set? */
    mp->dptr = find_dev_from_unit (uptr);               /* no, so set device now */

if (mp->dptr) {
    for (i=0; i<mp->lines; i++) {
        mp->ldsc[i].expect.dptr = mp->dptr;
        mp->ldsc[i].expect.dbit = TMXR_DBG_EXP;
        mp->ldsc[i].send.dptr = mp->dptr;
        mp->ldsc[i].send.dbit = TMXR_DBG_SEND;
        }
4361
4362
4363
4364
4365
4366
4367


4368
4369
4370
4371
4372
4373
4374
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416







+
+







if (lp->txbfd)
    fprintf (st, "  output buffer size = %d\n", lp->txbsz);
if (lp->txcnt || lp->txbpi)
    fprintf (st, "  bytes in buffer = %d\n", 
               ((lp->txcnt > 0) && (lp->txcnt > lp->txbsz)) ? lp->txbsz : lp->txbpi);
if (lp->txdrp)
    fprintf (st, "  dropped = %d\n", lp->txdrp);
if (lp->txstall)
    fprintf (st, "  stalled = %d\n", lp->txstall);
return;
}


/* Disconnect a line.

   Disconnect a line of the multiplexer associated with descriptor "desc" from a
4410
4411
4412
4413
4414
4415
4416
4417



4418
4419
4420
4421
4422
4423
4424
4452
4453
4454
4455
4456
4457
4458

4459
4460
4461
4462
4463
4464
4465
4466
4467
4468







-
+
+
+








if (lp == NULL)                                                 /* bad line number? */
    return status;                                              /* report it */

if ((lp->sock) || (lp->serport)) {                              /* connection active? */
    if (!lp->notelnet)
        tmxr_linemsg (lp, "\r\nOperator disconnected line\r\n\n");/* report closure */
    tmxr_reset_ln_ex (lp, (sim_switches & SWMASK ('C')));       /* drop the line */
    if (lp->serport && (sim_switches & SWMASK ('C')))
        return tmxr_detach_ln (lp);
    return tmxr_reset_ln_ex (lp, FALSE);                        /* drop the line */
    }

return SCPE_OK;
}


/* Enable logging for line */
4434
4435
4436
4437
4438
4439
4440
4441

4442
4443
4444
4445
4446
4447
4448
4478
4479
4480
4481
4482
4483
4484

4485
4486
4487
4488
4489
4490
4491
4492







-
+







if (lp == NULL)
    return SCPE_IERR;
if (lp->txlog)                                          /* close existing log */
    tmxr_set_nolog (NULL, val, NULL, desc);
lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc namebuf */
if (lp->txlogname == NULL)                              /* can't? */
    return SCPE_MEM;
strncpy (lp->txlogname, cptr, CBUFSIZE);                /* save file name */
strlcpy (lp->txlogname, cptr, CBUFSIZE);                /* save file name */
sim_open_logfile (cptr, TRUE, &lp->txlog, &lp->txlogref);/* open log */
if (lp->txlog == NULL) {                                /* error? */
    free (lp->txlogname);                               /* free buffer */
    return SCPE_OPENERR;
    }
if (mp->uptr)                                           /* attached?, then update attach string */
    lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
Changes to src/sim_tmxr.h.
149
150
151
152
153
154
155

156
157
158
159
160
161
162
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163







+







    int32               rxcnt;                          /* rcv count */
    int32               rxpcnt;                         /* rcv packet count */
    int32               txbpr;                          /* xmt buf remove */
    int32               txbpi;                          /* xmt buf insert */
    int32               txcnt;                          /* xmt count */
    int32               txpcnt;                         /* xmt packet count */
    int32               txdrp;                          /* xmt drop count */
    int32               txstall;                        /* xmt stall count */
    int32               txbsz;                          /* xmt buffer size */
    int32               txbfd;                          /* xmt buffered flag */
    t_bool              modem_control;                  /* line supports modem control behaviors */
    int32               modembits;                      /* modem bits which are currently set */
    FILE                *txlog;                         /* xmt log file */
    FILEREF             *txlogref;                      /* xmt log file reference */
    char                *txlogname;                     /* xmt log file name */
283
284
285
286
287
288
289


290
291
292
293
294
295
296
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299







+
+







t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval);
t_stat tmxr_clock_coschedule_abs (UNIT *uptr, int32 interval);
t_stat tmxr_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 ticks);
t_stat tmxr_clock_coschedule_tmr_abs (UNIT *uptr, int32 tmr, int32 ticks);
t_stat tmxr_change_async (void);
t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd);
t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp);
const char *tmxr_send_line_name (const SEND *snd);
const char *tmxr_expect_line_name (const EXPECT *exp);
t_stat tmxr_startup (void);
t_stat tmxr_shutdown (void);
t_stat tmxr_start_poll (void);
t_stat tmxr_stop_poll (void);
void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize);
#define tmxr_debug(dbits, lp, msg, buf, bufsize) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) _tmxr_debug (dbits, lp, msg, buf, bufsize); } while (0)
#define tmxr_debug_msg(dbits, lp, msg) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) sim_debug (dbits, (lp)->mp->dptr, "%s", msg); } while (0)
Changes to src/sim_video.c.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
27
28
29
30
31
32
33


34
35
36
37
38
39
40







-
-







   11-Jun-2013  MB      First version
*/

#include "sim_video.h"
#include "scp.h"

t_bool vid_active = FALSE;
int32 vid_mouse_xrel;
int32 vid_mouse_yrel;
int32 vid_cursor_x;
int32 vid_cursor_y;
t_bool vid_mouse_b1 = FALSE;
t_bool vid_mouse_b2 = FALSE;
t_bool vid_mouse_b3 = FALSE;
static VID_QUIT_CALLBACK vid_quit_callback = NULL;

92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104







-
+







/*
 * Save an SDL_Surface as a PNG file.
 *
 * Returns 0 success or -1 on failure, the error message is then retrievable
 * via SDL_GetError().
 */
#define SDL_SavePNG(surface, file) \
	SDL_SavePNG_RW(surface, SDL_RWFromFile(file, "wb"), 1)
        SDL_SavePNG_RW(surface, SDL_RWFromFile(file, "wb"), 1)

/*
 * SDL_SavePNG -- libpng-based SDL_Surface writer.
 *
 * This code is free software, available under zlib/libpng license.
 * http://www.libpng.org/pub/png/src/libpng-LICENSE.txt
 */
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
517
518
519
520
521
522
523


524
525
526
527
528
529
530







-
-







        sprintf (vid_title, "%s", sim_name);
    vid_flags = flags;
    vid_active = TRUE;
    vid_width = width;
    vid_height = height;
    vid_mouse_captured = FALSE;
    vid_cursor_visible = (vid_flags & SIM_VID_INPUTCAPTURED);
    vid_mouse_xrel = 0;
    vid_mouse_yrel = 0;

    vid_key_events.head = 0;
    vid_key_events.tail = 0;
    vid_key_events.count = 0;
    vid_key_events.sem = SDL_CreateSemaphore (1);
    vid_mouse_events.head = 0;
    vid_mouse_events.tail = 0;
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
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







+
+
+












-
-


-
-







return SCPE_OK;
}

void vid_set_cursor_position (int32 x, int32 y)
{
int32 x_delta = vid_cursor_x - x;
int32 y_delta = vid_cursor_y - y;

if (vid_flags & SIM_VID_INPUTCAPTURED)
    return;

if ((x_delta) || (y_delta)) {
    sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "vid_set_cursor_position(%d, %d) - Cursor position changed\n", x, y);
    /* Any queued mouse motion events need to have their relative 
       positions adjusted since they were queued based on different info. */
    if (SDL_SemWait (vid_mouse_events.sem) == 0) {
        int32 i;
        SIM_MOUSE_EVENT *ev;

        for (i=0; i<vid_mouse_events.count; i++) {
            ev = &vid_mouse_events.events[(vid_mouse_events.head + i)%MAX_EVENTS];
            sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "Pending Mouse Motion Event Adjusted from: (%d, %d) to (%d, %d)\n", ev->x_rel, ev->y_rel, ev->x_rel + x_delta, ev->y_rel + y_delta);
            vid_mouse_xrel -= ev->x_rel;                            /* remove previously accumulated relative position */
            vid_mouse_yrel -= ev->y_rel;
            ev->x_rel += x_delta;
            ev->y_rel += y_delta;
            vid_mouse_xrel += ev->x_rel;                            /* update cumulative x & y rel */
            vid_mouse_yrel += ev->y_rel;
            }
        if (SDL_SemPost (vid_mouse_events.sem))
            sim_printf ("%s: vid_set_cursor_position(): SDL_SemPost error: %s\n", vid_dev ? sim_dname(vid_dev) : "Video Device", SDL_GetError());
        }
    else {
        sim_printf ("%s: vid_set_cursor_position(): SDL_SemWait error: %s\n", vid_dev ? sim_dname(vid_dev) : "Video Device", SDL_GetError());
        }
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
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







-
-



-
-
+
+


















-
-
+
+







        dev->x, dev->y, dev->xrel, dev->yrel, (dev->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? 1 : 0, (dev->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? 1 : 0, (dev->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? 1 : 0);
    };
if (SDL_SemWait (vid_mouse_events.sem) == 0) {
    if (!vid_mouse_captured) {
        event->xrel = (event->x - vid_cursor_x);
        event->yrel = (event->y - vid_cursor_y);
        }
    vid_mouse_xrel += event->xrel;                          /* update cumulative x rel */
    vid_mouse_yrel += event->yrel;                          /* update cumulative y rel */
    vid_mouse_b1 = (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? TRUE : FALSE;
    vid_mouse_b2 = (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? TRUE : FALSE;
    vid_mouse_b3 = (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? TRUE : FALSE;
    sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: pos:(%d,%d) rel:(%d,%d) buttons:(%d,%d,%d) - Count: %d vid_mouse_rel:(%d,%d), vid_cursor:(%d,%d)\n", 
                                            event->x, event->y, event->xrel, event->yrel, (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? 1 : 0, vid_mouse_events.count, vid_mouse_xrel, vid_mouse_yrel, vid_cursor_x, vid_cursor_y);
    sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: pos:(%d,%d) rel:(%d,%d) buttons:(%d,%d,%d) - Count: %d vid_cursor:(%d,%d)\n", 
                                            event->x, event->y, event->xrel, event->yrel, (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? 1 : 0, vid_mouse_events.count, vid_cursor_x, vid_cursor_y);
    if (vid_mouse_events.count < MAX_EVENTS) {
        SIM_MOUSE_EVENT *tail = &vid_mouse_events.events[(vid_mouse_events.tail+MAX_EVENTS-1)%MAX_EVENTS];

        ev.x_rel = event->xrel;
        ev.y_rel = event->yrel;
        ev.b1_state = vid_mouse_b1;
        ev.b2_state = vid_mouse_b2;
        ev.b3_state = vid_mouse_b3;
        ev.x_pos = event->x;
        ev.y_pos = event->y;
        if ((vid_mouse_events.count > 0) &&             /* Is there a tail event? */
            (ev.b1_state == tail->b1_state) &&          /* With the same button state? */
            (ev.b2_state == tail->b2_state) && 
            (ev.b3_state == tail->b3_state)) {          /* Merge the motion */
            tail->x_rel += ev.x_rel;
            tail->y_rel += ev.y_rel;
            tail->x_pos = ev.x_pos;
            tail->y_pos = ev.y_pos;
            sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: Coalesced into pending event: (%d,%d) vid_mouse_rel:(%d,%d)\n", 
                tail->x_rel, tail->y_rel, vid_mouse_xrel, vid_mouse_yrel);
            sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: Coalesced into pending event: (%d,%d)\n", 
                tail->x_rel, tail->y_rel);
            }
        else {                                          /* Add a new event */
            vid_mouse_events.events[vid_mouse_events.tail++] = ev;
            vid_mouse_events.count++;
            if (vid_mouse_events.tail == MAX_EVENTS)
                vid_mouse_events.tail = 0;
            }
1700
1701
1702
1703
1704
1705
1706



1707
1708
1709
1710
1711
1712
1713
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709







+
+
+







            case SDL_WINDOWEVENT:
                if (event.window.windowID == vid_windowID) {
                    sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE|SIM_VID_DBG_CURSOR, vid_dev, "vid_thread() - Window Event: %d - %s\n", event.window.event, windoweventtypes[event.window.event]);
                    switch (event.window.event) {
                        case SDL_WINDOWEVENT_ENTER:
                             if (vid_flags & SIM_VID_INPUTCAPTURED)
                                 SDL_WarpMouseInWindow (NULL, vid_width/2, vid_height/2);   /* center position */
                            break;
                        case SDL_WINDOWEVENT_EXPOSED:
                            vid_update ();
                            break;
                        }
                    }
                break;
#endif
            case SDL_USEREVENT:
                /* There are 6 user events generated */
Changes to src/sim_video.h.
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
193
194
195
196
197
198
199





200
201
202
203
204
205
206







-
-
-
-
-







t_stat vid_show_release_key (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
t_stat vid_show_video (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
t_stat vid_show (FILE* st, DEVICE *dptr,  UNIT* uptr, int32 val, CONST char* desc);
t_stat vid_screenshot (const char *filename);

extern t_bool vid_active;
extern uint32 vid_mono_palette[2];
extern int32 vid_mouse_xrel;                            /* mouse cumulative x rel */
extern int32 vid_mouse_yrel;                            /* mouse cumulative y rel */
extern t_bool vid_mouse_b1;                             /* mouse button 1 state */
extern t_bool vid_mouse_b2;                             /* mouse button 2 state */
extern t_bool vid_mouse_b3;                             /* mouse button 3 state */
void vid_set_cursor_position (int32 x, int32 y);        /* cursor position (set by calling code) */

#define SIM_VID_DBG_MOUSE   0x01000000
#define SIM_VID_DBG_CURSOR  0x02000000
#define SIM_VID_DBG_KEY     0x04000000
#define SIM_VID_DBG_VIDEO   0x08000000

Changes to tools/bosi.
161
162
163
164
165
166
167






168
169
170
171
172
173
174
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180







+
+
+
+
+
+







    sudo dphys-swapfile uninstall || true
    sudo dd if=/dev/zero of=/junk bs=1M || true # it *will* error-out!
    sudo rm -f /junk

    encpass=$(openssl passwd -1 edsonDeCastro1968)
    sudo usermod -p $encpass pidp8i
    sudo passwd -e pidp8i

    cl=/boot/cmdline.txt
    if ! grep -Fq ' init=' $cl
    then
        sudo sed -i -e 's#$# init=/usr/lib/raspi-config/init_resize.sh#' $cl
    fi

    sudo poweroff
}


# Shrink the filesystem on the OS SD card we're about to image to just a
# bit bigger than required to hold its present contents.
Added tools/cc8-tu56-update.





























































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/python
# -*- coding: utf-8 -*-
########################################################################
# cc8-tu56-update - Rebuilds cc8.tu56 from source code.
#
# It is intended to be be called manually whenever a file in src/cc8/os8
# changes.  It is not called automatically from the top-level Makefile
# because not all end user systems will be able to run it, since our
# dependencies (host-side cc8 and SABR) are not always present.
#
# 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.
########################################################################

# 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')
sys.path.insert (0, os.getcwd () + '/lib')

# Our local modules
from pidp8i import *
from simh   import *

# Other global Python modules
import glob
import subprocess


#### GLOBALS AND CONSTANTS #############################################

# Path to the cross-compiler version of cc8.
cc8_cross = os.path.join (dirs.build, 'bin', 'cc8')

# Path to the CC8 sources
cc8_src = os.path.join (dirs.src, 'src', 'cc8')


#### cross_compile #####################################################
# Cross-compile a *.c file on the local machine to a *.s file we can
# send into the OS/8 instance.  The parameter should have no extension.

def cross_compile (module):
  inf  = module + '.c'
  outf = module + '.s'

  if os.path.exists (outf) and \
      os.path.getmtime (inf) <= os.path.getmtime (outf):
    print outf + " up-to-date."
  else:
    print "Building " + outf + "..."
    rc = subprocess.call ([cc8_cross, inf])
    if rc == 0:
      print inf + " -> " + outf
    else:
      print "Failed to cross-compile the " + module.upper() + " module!"
      exit (1)


#### main ##############################################################

def main ():
  # We have to do much of the following from within the CC8 source dir
  # since the same source code is used for both versions of CC8, so the
  # compiler assumes a flat filesystem, hence no "include path", hence
  # all headers and such must be symlinked or copied into the same
  # directory as our primary inputs.
  os.chdir (os.path.join (cc8_src, 'os8'))

  # Cross-compile the OS/8 version of CC8's C sources to SABR.  These
  # files are listed in dependency order, in case that later matters.
  modules = [ 'libc', 'c8', 'n8', 'p8' ]
  try:
    for m in modules: cross_compile (m)
  except OSError as e:
    print "Cross-compilation steps failed: " + e.strerror + '!'
    exit (1)

  # Create the SIMH child instance and tell it where to send log output
  try:
    s = simh (dirs.build, True)
  except (RuntimeError) as e:
    print "Could not start simulator: " + e.message + '!'
    exit (1)
  s.set_logfile (os.fdopen (sys.stdout.fileno (), 'w', 0))

  # Zero core before beginning.  SIMH's PDP-8 simulator doesn't do it,
  # on purpose, because the actual hardware did not do that.  SIMH does
  # not attempt to simulate the persistence of core memory by saving it
  # to disk between runs, but the developers are right: you cannot trust
  # the prior state of core before initializing it yourself.  Rather
  # than do that in some OS/8 specific way, we tell SIMH to do it.
  s.send_cmd ('de all 0')

  # Attach an empty DECtape to the simulator to hold our output.
  tape = os.path.join (dirs.os8mi, 'subsys', 'cc8.tu56')
  if os.path.exists (tape): os.remove (tape)
  s.send_cmd ("att dt0 " + tape)

  # Find and boot the bootable OS/8 disk.  Use the "patched" version
  # because that is what "make run" uses; we use that command to
  # inspect this script's work.
  rk = os.path.join (dirs.os8mo, 'os8v3d-patched.rk05')
  if not os.path.isfile (rk):
    print "Could not find " + rk + "; OS/8 media not yet built?"
    exit (1)
  print "Booting " + rk + "..."
  s.send_cmd ("att rk0 " + rk)
  s.send_cmd ("boot rk0")

  # Get rid of any C program sources that may happen to be laying around
  # on the RK05 disk image we start with so we don't copy any working
  # files created by hand to the cc8.tu56 tape with the wildcard below.
  s.os8_send_cmd ('\\.', 'DEL *.C')

  # Copy the SABR files produced above by the host-side cross-compiler
  # to the OS/8 environment's RK05 disk.
  for m in modules:
    s.os8_send_file (m + '.sb')

  # Copy example programs in as well.
  #
  # We have to filter these files' content for the reasons given at the
  # top of cc8-to-os8.
  #
  # We don't let os8_send_file build the destination file name because
  # the source is a random-named temp file.
  tool = os.path.join (dirs.bin, 'cc8-to-os8')
  for src in glob.glob ('../examples/*.c'):
    if 'basic.c' not in src:
    # else: leave this one out.  The OS/8 compiler can't cope with it.
      ctf = tempfile.NamedTemporaryFile ()
      ctn = ctf.name
      os.system (tool + ' < ' + src + ' > ' + ctn)
      s.os8_send_file (ctn, os.path.basename (src).upper ())

  # Copy the remaining static OS/8 CC8 text files in.  We purposely do
  # not send init.h or libc.h because OS/8 CC8 doesn't process #include,
  # so it has those bits hard-coded into the compiler at the moment.
  for src in [ 'bldcc8.bi', 'cc.bi', 'header.sb' ]:
    s.os8_send_file (src)

  # Build the OS/8 version of CC8 using the batch file we copied in.
  s.os8_send_cmd ('\\.', 'EXE BLDCC8.BI')

  # Get rid of intermediary files on DSK: so the subsequent COPY
  # commands don't add them to the DECtape image.
  s.os8_send_cmd ('\\.', 'DEL ?8.RL,?8.SB,LIBC.SB')

  # Save all the remaining involved files to the cc8.tu56 tape, giving
  # DIR DTA0: output approximately like this:
  #
  # CC    .SV  27           CC1   .SV  41           CC2   .SV  41
  # FIB   .C    1           CALC  .C    5           BASIC .C   11
  # PS    .C    1           CC    .BI   1           HEADER.SB   6
  # LIBC  .RL  22       
  s.os8_send_cmd ('\\.', 'ZERO DTA0:')
  s.os8_send_cmd ('\\.', 'COPY DTA0:<SYS:CC.SV,SYS:CC1.SV,SYS:CC2.SV')
  s.os8_send_cmd ('\\.', 'COPY DTA0:<DSK:HEADER.SB,DSK:LIBC.RL,DSK:*.C,DSK:CC.BI')

  # Exit simulator nicely so that tape detaches cleanly
  s.back_to_cmd ('\\.')
  s.send_cmd ('quit')


if __name__ == "__main__": main()
Added tools/cmplz.














1
2
3
4
5
6
7
8
9
10
11
12
13
14
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/bash
if [ $# = 2 ]
then
    cmp <(lz4 -dq "$1") <(lz4 -dq "$2")
    exit $?
else
    cat <<USAGE
usage: $0 <file1.lz> <file2.lz>

    Compares two lz4-compressed files, reporting differences in hex form.

USAGE
    exit 99
fi
Added tools/diffstat-since-release.



1
2
3
+
+
+
#!/bin/bash
files=$(ls | grep -v -e autosetup -e test | tr '\n' ' ')
fossil diff --internal --from release $files | diffstat
Added tools/mkadrules.



























































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/env perl
########################################################################
# mkadrules - Write .c.o Makefile rules that also do autodependency
#   generation to $adf for each source subdirectory named on the command
#   line to avoid the need to manually maintain these near-identical
#   Makefile rule sets.
#
#   Each *.o file gets a *.d file generated by the compiler listing the
#   object file's dependencies as discovered by the compiler at build
#   time, using the same build options used to generate the *.o file.
#   These automatically generated dependency rule sets are therefore
#   inherently accurate and comprehensive, which hand-written rules
#   virtually never are, particularly for software that builds on
#   multiple platforms, where out-of-tree dependencies (e.g. the
#   location of stdio.h) vary from one platform to another.
#
#   This mechanism depends on the -M feature of GCC and Clang, which
#   means our build system is probably not as widely portable as it
#   might otherwise be.
#
#   Based on http://scottmcpeak.com/autodepend/autodepend.html
#
# Copyright © 2017 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.
########################################################################

use strict;
use warnings;

use Cwd;

# The basic rule set we generate, which is repeatedly modified and
# written to the output file.
my $template = <<'TEMPLATE';
obj/%.o: SRCDIR/src/%.c
	$(CC)  -c $(CFLAGS) SRCDIR/src/$*.c -o obj/$*.o
	$(CC) -MM $(CFLAGS) SRCDIR/src/$*.c  > obj/$*.d
	@mv -f obj/$*.d obj/$*.d.tmp
	@sed -e 's|.*:|obj/$*.o:|' < obj/$*.d.tmp > obj/$*.d
	@sed -e 's/.*://' -e 's/\\$$//' < obj/$*.d.tmp | fmt -1 | \
		sed -e 's/^ *//' -e 's/$$/:/' >> obj/$*.d
	@rm -f obj/$*.d.tmp
TEMPLATE

# Parse command line
die "usage: $0 <srcdir> <paths...>\n\n" unless @ARGV >= 2;
my $srcdir = shift @ARGV;
die "Source tree $srcdir does not exist!\n" unless -d $srcdir;
my @dirs = @ARGV;
for (@dirs) {
    die "Source subdir $_ does not exist!\n" unless -d "$srcdir/$_";
}

# Create the output file
my $adf = 'adrules.mk';
unlink $adf;
open my $ad, '>', $adf, or die "Could not write to $adf: $!\n";
print $ad "## DO NOT MODIFY. GENERATED BY tools/mkadrules! ##\n";
print $ad "## -------------------------------------------- ##\n\n";

# If we're building in-tree, we can simplify $srcdir.
my $intree = $srcdir eq getcwd;
if ($intree) {
    $srcdir = '.';
}

# Write rule set for each dir passed
for my $sdir (@dirs) {
    my $rule = $template;

    my $odir = 'obj/' . $sdir;
    $odir =~ s{/src}{};

    my $cflags = uc $sdir;
    $cflags =~ s{SRC}{};
    $cflags =~ s{^/}{};
    $cflags =~ s{/}{_}g;
    $cflags = $cflags ? "${cflags}_CFLAGS" : 'TOP_CFLAGS';

    $rule =~ s{obj/}{$odir/}g;
    $rule =~ s{src/}{$sdir/}g;
    $rule =~ s{CFLAGS}{$cflags}g;
    $rule =~ s{SRCDIR}{$srcdir}g;
    $rule =~ s{ \./}{ }g;
    print $ad $rule, "\n";
}

# If we're building out-of-tree, some of the *.c files were written
# by autosetup from $srcdir/*.c.in, so we need an additional copy of
# the top src dir rule set especially for them.
unless ($intree) {
    my $rule = $template;
    $rule =~ s{SRCDIR/}{}g;
    print $ad $rule, "\n";
}

# Finish up
close $ad;
chmod 0440, $adf;
print "Generated $adf for ", join(', ', @dirs), ".\n";
Deleted tools/mkos8.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
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













































































































































































































































































































































































































































































































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

import argparse
import os
import sys
import subprocess
import pexpect
import time
from shutil import copyfile


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

_media_dir = "@abs_top_srcdir@/media/os8/"
_bin_dir = "@builddir@/bin/"
_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_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)
  for i in xrange(0, len(send_line)):
    child.send(send_line[i])
    os8_kbd_delay()
  child.send("\r")


#### 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 = _media_dir + 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
   

#### copyin_pair #######################################################
# Copy two images into two destinations with two messages
#
# A copyin is a 3 element array: [destination, source, message]
# 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:
    print "Copying: " + copyin0[1] + " to: " + copyin0[0] + "from dt0"
    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.
  if copyin0: simh_send("attach -r dt0 " + _media_dir + copyin0[1])
  if copyin1: simh_send("attach -r dt1 " + _media_dir + copyin1[1])
  simh_send("boot rk0")
  if copyin0:
    if progmsg: print copyin0[2]
    os8_pmt_send("\.", "COPY " + copyin0[0] + "<DTA0:*.*")
  if copyin1:
    if progmsg: print copyin1[2]
    os8_pmt_send("\.", "COPY " + copyin1[0] + "<DTA1:*.*")
  back_to_simh("\.")

  if copyin0: simh_send("detach dt0")
  if copyin1: simh_send("detach dt1")
  
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 =  _media_dir + ro_boot_tape
  driver_tape =        "al-4712c-ba-os8-v3d-2.1978.tu56"
  driver_tape_path =   _media_dir + driver_tape
  local_tape =         "local.tu56"
  local_tape_path =    _media_dir + local_tape
  
  boot_dt_path = _media_dir + "bootable-al-4711-c-ba-os8-v3d-1.1978.tu56"

  special_bin_copyins = [
    ["", ro_boot_tape,  ""],
    ['RKA0:', driver_tape, "Device Drivers..."],
    ["", local_tape, ""]
  ]

  music_copyin  = ['RKB0:', 'subsys/music.tu56',   "Copying in Music score files and source code..."]
  ba_copyin     = ['RKB0:', 'subsys/ba.tu56',      "Installing *.BA BASIC games and demos..."]
  cc8_copyin    = ['RKA0:', 'subsys/cc8.tu56',     "Installing Ian Schofield's CC8 compiler..."]
  k12_copyin    = ['RKA0:', 'subsys/k12.tu56',     "Installing Kermit-12..."]
  advent_copyin = ['RKB0:', 'subsys/advent.tu56',  "Installing ADVENT..."]

  bin_copyins = [
    ['RKA0:', "al-4761c-ba-os8-v3d-ext.1978.tu56", "Copying in OS/8 V3D extensions..."],
    ['RKA0:', "al-4549d-ba-fr4-v3d-1.1978.tu56", "Copying in FORTRAN IV tape 1 of 2..."],
    ['RKA0:', "al-5596d-ba-fr4-v3d-2.1978.tu56", "Copying in FORTRAN IV tape 2 of 2..."],
    ['RKA0:', "al-5642a-ba-macrel-linker.1978.tu56", "Installing MACREL..."]
    ]

  if args.with_music:         bin_copyins.append(music_copyin)
  if not args.without_ba:     bin_copyins.append(ba_copyin)
  if not args.without_cc8:    bin_copyins.append(cc8_copyin)
  if not args.without_k12:    bin_copyins.append(k12_copyin)
  if not args.without_advent: bin_copyins.append(advent_copyin)

  check_exists(special_bin_copyins)
  check_exists(bin_copyins)

  print "Generating " + _bin_rk05 + " from " + str(len(bin_copyins) + 2) + \
        " source tapes..."

  image_path = _bin_dir + _bin_rk05
  if os.path.isfile(image_path):
    save_path = _bin_dir + _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 " + _bin_dir + "os8v3d-bin.rk05")
  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")   # For Safety's Sake
  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")
  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("attach -r dt0 " + local_tape_path)
  simh_send("boot rk0")

  if progmsg: print "Installing lower case and uppercase config batch scripts..."
  os8_pmt_send("\.", "COPY RKA0:<DTA0:LC??S.BI")
  os8_pmt_send("\.", "COPY RKA0:<DTA0:LC??S.BI")
  
  if not args.without_crt:
    # NO SCOPE mode is the default on distribution tapes.
    if progmsg: print "Configuring scope-style rubout processing..."
    os8_pmt_send("\.", "SET TTY SCOPE")
  
  if not args.without_chess:
    if progmsg: print "Installing CHESS.SV binary and CHESS.TX documentation..."
    os8_pmt_send("\.", "COPY RKA0:<DTA0:CHESS.*")

  if args.with_vtedit:
    if progmsg: print "Installing TECO VTEDIT setup..."
    os8_pmt_send("\.", "COPY RKA0:<DTA0:VTEDIT.*")
    os8_pmt_send("\.", "COPY RKA0:<DTA0:TECO.IN")

  if not args.without_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")
  
  if progmsg: print "Cleaning up..."
  back_to_simh("\.")
  simh_send("detach dt0")
  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..."],
    ["RKA1:", "al-4692c-sa-os8-v3d-2.1978.tu56", "...part 2 of 7..."],
    ["RKA1:", "al-4693d-sa-os8-v3d-3.1978.tu56", "...part 3 of 7..."],
    ["RKA1:", "al-4694c-sa-os8-v3d-4.1978.tu56", "...part 4 of 7..."],
    ["RKB1:", "al-4695c-sa-os8-v3d-5.1978.tu56", "...part 5 of 7..."],
    ["RKB1:", "al-4696c-sa-os8-v3d-6.1978.tu56", "...part 6 of 7..."],
    ["RKB1:", "al-4697c-sa-os8-v3d-7.1978.tu56", "...part 7 of 7..."],
    ["RKB1:", "al-4759c-sa-os8-ext-1.1978.tu56", "extensions part 1 of 3..."],
    ["RKB1:", "al-4760c-sa-os8-ext-2.1978.tu56", "...part 2 of 3..."],
    ["RKA1:", "al-5586c-sa-os8-ext-3.1978.tu56", "...part 3 of 3..."],
  ]
  
  check_exists(src_copyins)

  print "Generating " + _src_rk05 + " from " + str(len(src_copyins)) + \
        " source tapes..."

  image_path = _bin_dir + _bin_rk05
  if (not os.path.isfile(image_path)):
    print _bin_rk05 + " is needed to build src.  Creating..."
    make_bin(args)

  image_path = _bin_dir + _src_rk05
  if os.path.isfile(image_path):
    save_path = _bin_dir + _src_rk05 + ".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(image_path,save_path)
    
  if progmsg: print "Copying OS/8 source distribution:"
  simh_send("attach rk0 " + _bin_dir + "os8v3d-bin.rk05")
  simh_send("attach rk1 " + _bin_dir + "os8v3d-src.rk05")

  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
  # os8 is a place holder for future functionality if we decide
  # to have a separate master RK05 sys pack and a runnable os8 pack.
  allowed_acts = ["all", "bin", "src", "os8"]
  parser = argparse.ArgumentParser(description='Build OS/8 RK05 disk images')
  parser.add_argument('-v', '--verbose',
                      action='store_true',
                      help='verbose SIMH output instead of progress messages')
  parser.add_argument('--with-music',
                      action='store_true',
                      default=False,
                      help='add *.MU files to binary disk')
  parser.add_argument('--without-ba',
                      action='store_true',
                      default=False,
                      help='leave BASIC games and demos off binary disk')
  parser.add_argument('--without-cc8',
                      action='store_true',
                      default=False,
                      help='leave CC8 off binary disk')
  parser.add_argument('--without-k12',
                      action='store_true',
                      default=False,
                      help='leave 12-bit Kermit off binary disk')
  parser.add_argument('--with-vtedit',
                      action='store_true',
                      default=False,
                      help='Install and enable TECO vtedit mode')
  parser.add_argument('--without-crt',
                      action='store_true',
                      default=False,
                      help='console is a printing terminal and does not use character overwrite on rubout')
  parser.add_argument('--without-lcmod',
                      action='store_true',
                      default=False,
                      help='Disable the OS/8 command upcasing patch; best set when SIMH is set to tti ksr mode')
  parser.add_argument('--without-advent',
                      action='store_true',
                      default=False,
                      help='leave game of Adventure off binary disk')
  parser.add_argument('--without-chess',
                      action='store_true',
                      default=False,
                      help='Leave CHEKMO-II off binary disk')
  parser.add_argument('what',
                      choices=allowed_acts,
                      help='select which RK05 media gets built; default is "all"',
                      nargs=argparse.REMAINDER)
  args = parser.parse_args()
  if len(args.what) == 0: args.what = [ 'all' ]
  global progmsg
  progmsg = not args.verbose

  # Initialize our acts dict.
  acts = {}
  for this in allowed_acts:
    acts[this] = False

  for act in args.what:
    if act not in allowed_acts:
      print "Invalid act: " + act + " ignored."
      continue
    if act == "all":
      for this in allowed_acts:
        acts[this] = True
      break
    acts[act] = True
  
  global child
  child = pexpect.spawn('@builddir@/bin/pidp8i-sim')
  child.delaybeforesend = None

  # 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('@builddir@/obj/mkos8.log', 'w') \
    if progmsg else os.fdopen(sys.stdout.fileno(), 'w', 0)

  if acts["bin"]: make_bin(args)
  if acts["src"]: make_src(args)
  
  simh_send("q")
  child.expect(pexpect.EOF)
  if progmsg: print "Done!"


if __name__ == "__main__":
    main()
Added tools/publish-fossil.













1
2
3
4
5
6
7
8
9
10
11
12
13
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/bash -e
lr=/usr/bin/lsb_release
if [ -x $lr ]
then
    vs=$(lsb_release -sicr | tr '\n' - | tr '[A-Z]' '[a-z]' | sed -e 's/-$//')
    d=fossil-$vs
    cp /usr/local/bin/fossil $d
    fossil uv add $d
    fossil uv sync
    rm $d
else
    echo Could not find $lr!
fi
Added tools/publish-os8.




1
2
3
4
+
+
+
+
#!/bin/sh -e
cd bin
fossil uv add os8v3d-*.rk05
fossil uv sync
Changes to tools/simh-update.in.
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
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







+
+




-
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







git worktree add "$OUR_SIMH_DIR" $OLD_SGCID
popd

# Copy over updated versions of the docs and replace them in the Fossil
# unversioned area.  We simplify the upstream naming scheme in the
# transfer, dropping unnecessary prefixes and suffixes.
pushd "$SRCDIR"
pdfdir=doc/simh
mkdir -p $pdfdir
for fs in pdp8_doc simh_faq simh_doc
do
	ft=$(echo $fs | sed -e 's/simh_//' -e 's/_doc//')
	test "$ft" = "doc" && ft=main
	pdfout=doc/simh/$ft.pdf
	pdfout=$pdfdir/$ft.pdf
    pdftmp="$fs.pdf"
    say -n "Converting upstream $fs.doc to $pdfout..."
    if [ ! -e "$pdfout" ] ; then touch $pdfout ; fi  # complaint squisher
    if "$SOFFICE" --convert-to pdf "$CURR_SIMH_DIR/doc/$fs.doc" &&
            [ -s "$pdfout" != -s "$pdftmp" ]
    then
        # The upstream doc has apparently changed, since the PDF output
        # file is a different size.  Replace our public version.
        #
        # We can't use cmp or similar here because a bunch of metadata
        # change each time we re-render the PDF, even if the source doc
        # is unchanged.  There are proper PDF comparison tools, but none
        # preinstalled everywhere, and we don't want to make one of
        # those tools a dependency of this script.  Size comparison
        # suffices for our purposes, since most any substantial text
        # change will change the output file size.
        say "changes detected."
        mv "$pdftmp" "$pdfout"
        fossil uv add "$pdfout"
    else
        say "unchanged."
    if [ ! -e "$pdfout" ]
    then
        # Pull a copy out of the unversioned store
        fossil uv export $pdfout $pdfout
    fi
    if "$SOFFICE" --convert-to pdf "$CURR_SIMH_DIR/doc/$fs.doc"
    then
        szo=$(stat -f %z $pdfout)
        szt=$(stat -f %z $pdftmp)
        if [ -n "$szo" ] && [ -n "$szt" ] && [ "$szo" != "$szt" ]
        then
            # The upstream doc has apparently changed, since the PDF output
            # file is a different size.  Replace our public version.
            #
            # We can't use cmp or similar here because a bunch of metadata
            # change each time we re-render the PDF, even if the source doc
            # is unchanged.  There are proper PDF comparison tools, but none
            # preinstalled everywhere, and we don't want to make one of
            # those tools a dependency of this script.  Size comparison
            # suffices for our purposes, since most any substantial text
            # change will change the output file size.
            say "changes detected."
            mv "$pdftmp" "$pdfout"
            fossil uv add "$pdfout"
        else
            say "unchanged."
        fi
    else
        say "generation failed!"
        exit 1
    fi
done
say "Syncing new PDFs..."
fossil uv sync
popd

# Rename upstream Git paths to match our *.in files so that our produced
Added tools/test-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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/env perl
########################################################################
# test-mkos8 - Collects the set of all --*-os8-* options from the
#   configure script, generates all unique subsets of that option set,
#   runs configure with each of those subsets, and compares the output
#   OS/8 bin RK05 disk to the previous run's version.  If there is a
#   discrepancy, generate a diff against the build log for that option
#   set and report the problem for the user to diagnose.
#
#   The first time the script is run, or the first time a new unique
#   option subset is generated, we save the build results as the
#   exemplar to use in later tests.
#
# 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.
########################################################################

use strict;
use warnings;

# Modules from CPAN
use Math::Subsets::List;
use Parallel::Loops;

# Core modules
use Cwd qw(getcwd abs_path);
use Digest::SHA qw(sha256_hex);
use English;
use File::Basename;
use File::Copy;
use Getopt::Std;
use List::Util qw(shuffle);
use Term::ANSIColor;

# Perl::Critic rules we're willing to bend
## no critic (InputOutput::RequireBriefOpen )


#### GLOBALS ###########################################################

my @tests;
my $tests_mf;
my (%generated, %tested);
my $currlog = 'obj/mkos8-bin.log';
my $cmplz = abs_path (dirname ($0)) . '/cmplz';

# Command line option values
my ($dry_run, $existing_only, $generate_only, $single_core, $shuffle,
        $verbose);


#### sanitize_log ######################################################
# Copy the given input log file to the ouptut file, expurgating bits
# that change from one run to the next without being meaningful.

sub sanitize_log
{
    my ($ifile, $ofile) = @_;

    open my $if, '<', $ifile or die "Cannot read $ifile: $!\n";
    open my $of, '>', $ofile or die "Cannot write $ofile: $!\n";

    while (<$if>) {
        # Strip all the CRs out.  We only need the LFs.
        s{\r}{}gs;
        my $original = $_;      # save it post-strip

        # Strip variable parts of SIMH line following each Ctrl-E:
        #
        # "Simulation stopped, PC: 01210 (JMP 1207)"
        s{
            (Simulation\ stopped)            # bit to preserve
            ,\ PC:\ \d+\ \([A-Z]+(\ \d+)?\)  # variable bit
        }{$1.}x;

        # Rewrite SIMH ATTACH commands to remove pointless differences
        # in absolute paths between machines or Fossil checkouts.
        s{
            (attach\ )           # SIMH command
            (-r\ )?              # optional flag
            ([a-z0-9]+\ )        # SIMH device name
            (.*test/tmp/[0-9]+/) # noise parts of image file name thru PID
        }{$1$3.../}x;
        s{
            (attach\ )
            (-r\ )?
            ([a-z0-9]+\ )
            (.*/(media/os8)/)    # also squish this noise
        }{$1$3.../$5/}x;

        # Add cleaned line to ofile
        print $of $_;
    }

    close $if;
    close $of;

    return;
}


#### construct_test ####################################################
# Assembles a test record for a single permutation

sub construct_test
{
    my @opts = @_;

    # Distill this option set to a hash value after which we will
    # name the output files.  We don't want to name files with a
    # leading hyphen or with long variable-length names, potentially
    # multiple lines long.
    my $optstr = join ' ', @opts;
    my $hash = sha256_hex($optstr);
    my $hdir = "test/$hash";    # test hash dir relative to our CWD
    my $rhdir = "../../$hash";  # $hdir relative to builddir test/tmp/$PID
    my $test = { 
        hash   => $hash,
        hdir   => $rhdir,
        log    => "$rhdir/last.log",
        name   => '{' . substr ($hash, 0, 12) . '}',
        optstr => $optstr,
        rklz   => "$rhdir/last.rklz",
    };

    # Skip this one if it already exists and we're in -g mode.
    if ($generate_only) {
        if (-d $hdir) {
            if (-f "$hdir/last.log") {
                if (-f "$hdir/last.rklz") {
                    print "Skipping $test->{name}: already done.\n";
                    return;
                }
                elsif ($verbose) {
                    print "Must re-gen $hash despite -g: rklz missing!\n";
                }
            }
            elsif ($verbose) {
                print "Must re-gen $hash despite -g: log missing!\n";
            }
        }
        elsif ($verbose) {
            print "Will generate $hash.\n";
        }
    }
    elsif (-d $hdir) {
        print "Will test $optstr against $hash.\n" if $verbose;
    }
    elsif ($verbose) {
        print "Must generate missing test set $hash.\n";
    }

    push @tests, $test;

    return;
}


#### compare_rklz ######################################################
# Compare two lz4-compressed RK05 disk images, returning true if they
# are the same.  If they are different, also outputs a binary difference
# report.
#
# We call a separate shell script instead of use inline shell code here
# because the helper code uses a Bash feature, and /bin/sh might not be
# bash, as on a Raspbian box.

sub compare_rklz
{
    my ($r1, $r2) = @_;
    return system("$cmplz '$r1' '$r2'") == 0;
}


#### do_test ###########################################################
# Test a single permutation

sub do_test
{
    # Set up working directory
    my $test = $_;
    print "Configuring test $test->{name}, PID $PID...\n";
    return if $dry_run;
    chdir "test/tmp" or die "Could not CD to tmp dir: $!\n";
    mkdir $PID;
    chdir $PID;
    die "Could not mkdir $test->{hdir}: $!\n"
            unless -d $test->{hdir} || mkdir($test->{hdir});

    # Are we building the -patched disk or the -bin disk?
    my $patched = $test->{optstr} !~ m{--disable-os8-patches}x;
    my $typestr = $patched ? 'patched' : 'bin';
    my $currdsk = "bin/os8v3d-$typestr.rk05";
    my $currdlz = substr ($currdsk, 0, length ($currdsk) - 2) . 'lz';
    my $target  = 'os8-' . $typestr;

    # Configure the test disk image
    system "../../../configure $test->{optstr} > cfg.log 2>&1"
            and die "Failed to configure $test->{name}!\n";
    link "../../../bin/pidp8i-sim", "bin/pidp8i-sim";  # no sense rebuilding it
    open my $itf, '>', 'media/os8/init.tx'             # avoid a pointless diff
            or die "Cannot overwrite init.tx with neutral version: $!\n";
    print $itf "TEST-MKOS8 BUILT THIS DISK IMAGE.\n\n";
    close $itf;

    # Build the test disk image
    print "Building $currdsk for test $test->{name} (PID $PID)...\n";
    system "make $target > make.log 2>&1"
            and die "Failed to build $currdsk!\n";

    # Quickly compress the test disk: we don't want to store all the
    # "air" in an RK05 in our test corpus.
    system("lz4 -q $currdsk > $currdlz");

    if (not -f $test->{log} or not -f $test->{rklz}) {
        # This test hasn't run here yet, so save it as our exemplar for
        # this optstr, to be compared to future builds.
        sanitize_log ($currlog, $test->{log});
        move ($currdlz, $test->{rklz});

        # Log the mapping between the hash and the options it
        # represents, so the user can reverse it.
        print $tests_mf "$test->{hash}  $test->{optstr}\n";
        $generated{$test->{hash}} = 1;
    }
    elsif (compare_rklz ($currdlz, $test->{rklz})) {
        # We had this test examplar here already and on re-doing it we
        # got the same result.
        print colored(['green'], "mkos8 $test->{name} test passed."), "\n";
        $tested{$test->{hash}} = 1;
    }
    else {
        # This build resulted in a difference, so yell and save the
        # results for manual comparison.
        my $fdiff = "$test->{hdir}/fail.diff";
        my $faillog = "$test->{hdir}/fail.log";
        sanitize_log ($currlog, $faillog);
        move ($currdlz, $test->{hdir} . '/fail.rklz');
        system "diff -wu $test->{log} $faillog > $fdiff";
        print colored(['bold red'], 'RESULT DIFFERS!  See test/',
                substr($fdiff, 6)), "\n";
        $tested{$test->{hash}} = 0;
    }

    system("cd .. ; rm -fr $PID");      # -f because there are read-only files

    return;
}


#### remove_missing ####################################################
# Implements -e: given a list of mkos8 options, returns only those for
# which we have a valid test set.

sub remove_missing
{
    my (@tests) = @_;
    my $all = @tests;

    # First read in the set of prebuilt tests, filtering out those that
    # refer to output files that do not exist here.  (This happens when
    # copying over the manifest file but only a subset of the actual
    # test output files.)
    my %existing;
    my $genned = 0;
    my $mff = 'test/tests-manifest.txt';
    open my $mf, '<', $mff or die "Could not read from $mff: $!\n";
    while (<$mf>) {
        chomp;
        my ($hash, @opts) = split ' ';
        my $dir = 'test/' . $hash;
        if (-d $dir and -f "$dir/last.rklz" and -f "$dir/last.log") {
            $existing{join ' ', @opts} = $hash;
        }
        ++$genned;
    }
    close $mf;

    # Now filter the test set to remove those that do not exist
    my @filtered = grep { $existing{$_->{optstr}} } @tests;
    print "Filtered $genned of $all tests down to ", scalar(@filtered),
            " for -e.\n";
    return @filtered;
}


#### report* ###########################################################
# Print on-exit status report.

sub report_part
{
    my ($partref, $kind) = @_;

    return unless keys %$partref;

    my ($successes, $tries) = (0, 0);
    for my $s (values %$partref) {
        ++$tries;
        ++$successes if $s;
    }

    my $extra = $successes == $tries ? '' : " of $tries";
    print colored ([
                $successes == 0 ? 'bold red' :
                    $successes != $tries ?
                        'bold yellow' :
                        'green'
            ],
            "Successfully $kind $successes$extra tests.\n");
}

sub report
{
    print "\n", '=' x 79, "\n";
    report_part (\%generated, 'generated');
    report_part (\%tested,    'built');
    print "\n";

    return;
}


#### main ##############################################################

# Parse command line
my %clopts;
getopts('egnsv1', \%clopts) or die "Failed to parse command line!\n";
$dry_run = $clopts{n};
$existing_only = $clopts{e};
$generate_only = $clopts{g};
$shuffle = $clopts{s};
$verbose = $clopts{v};
$single_core = $clopts{1};

# Init global resources
mkdir 'test';
system("rm -rf test/tmp");
mkdir 'test/tmp';
open $tests_mf, '>>', 'test/tests-manifest.txt'
        or die "Cannot append to test manifest: $!\n";
$SIG{INT} = $SIG{TERM} = sub { report; exit 1 };
$SIG{PIPE} = 'IGNORE';

# Get all current --*-os8-* options, filtering out those we know should
# not be tried for this:
#
# *   No --os8-minimal because that just turns on all --disable-os8-* 
#     options, so it's already covered.
# *   No --disable-os8-src because we don't test the src disk; it's
#     always generated the same way.
# *   No --disable-os8-focal because it disables the other two FOCAL
#     options, which we're already going to test singly and together.
my @cmd = (
    "./configure --help",
    "grep -- -os8-",
    "sed -Ee 's/^ +//'",
    "cut -f1 -d' '",
    "grep -v -e 'os8-minimal' -e 'os8-src' -e 'os8-focal\$'"
);
open my $ocmd, '-|', join('|', @cmd) or die "Failed to get os8 option set: $!\n";
my @cfgOpts = <$ocmd>;
close $ocmd;
chomp @cfgOpts;

# Generate all possible permutations of those options.  Shuffle them if
# requested.
subsets \&construct_test, @cfgOpts;
@tests = remove_missing(@tests) if $existing_only;
@tests = shuffle @tests if $shuffle;

# Run the tests
my $tdir = abs_path(dirname($0));
my $cores = $single_core ? 1 : int(`$tdir/corecount`);
my $pl = Parallel::Loops->new($cores);
$pl->share (\%generated, \%tested);
$pl->foreach ( \@tests, \&do_test);

report;
Added tools/test-os8-send-file.































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/python
# -*- coding: utf-8 -*-
########################################################################
# test-os8-send-file - Repeatedly sends random files through class simh
#   method os8_send_file() and pulls it back through os8_get_file(),
#   then checks that the file is unchanged.
#
# 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.
########################################################################

# 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')
sys.path.insert (0, os.getcwd () + '/lib')

# Our local modules
from pidp8i import *
from simh   import *

# Other core modules we need
import filecmp
import os.path
import random
import tempfile


#### gen_file ##########################################################
# Generate a random text file.  In order that the process be lossless
# through the txt2ptp/ptp2txt filters and the SIMH + OS/8 terminal
# handling, we use only printable ASCII plus CR+LF characters.  Returns
# the name of the generated file.

def gen_file ():
    f = tempfile.NamedTemporaryFile (delete = False, suffix = '.tmp')
    for i in range (0, random.randint (10, 4000)):
      if random.randint (0, 10) != 0:
        # Normal case: write some number of printable ASCII characters
        # on this line.
        for j in range (0, random.randint (1, 79)):
          f.write (chr (random.randint (32, 126)))
      # else: Every now and then, just write a blank line

      f.write ('\r\n')

    f.close ()
    return f.name


#### main ##############################################################

def main ():
  # Create the SIMH child instance and tell it where to send log output
  try:
    s = simh (dirs.build)
  except (RuntimeError) as e:
    print "Could not start simulator: " + e.message + '!'
    exit (1)
  s.set_logfile (os.fdopen (sys.stdout.fileno (), 'w', 0))

  # Find and boot the built OS/8 bin disk
  rk = os.path.join (dirs.os8mo, 'os8v3d-bin.rk05')
  if not os.path.isfile (rk):
    print "Could not find " + rk + "; OS/8 media not yet built?"
    exit (1)
  print "Booting " + rk + "..."
  s.send_cmd ("att rk0 " + rk)
  s.send_cmd ("boot rk0")

  # Setup
  random.seed ()

  # Transfer several random files through.  Beware increasing the range
  # too far: max is 99999 due to the file name length limit of OS/8 due
  # to the temporary file naming scheme we use in the loop.
  for i in range (0, 1000):
    # Build another temp file
    ifn = gen_file ()
    of = tempfile.NamedTemporaryFile (suffix = '.out', delete = False)
    of.close ()

    # Send it
    ofn = of.name
    tfn = 'T%05d.TX' % i
    s.os8_send_file (ifn, tfn)
    s.os8_get_file (tfn, ofn)

    # Did it change?
    if filecmp.cmp (ifn, ofn):
      print ifn + ' transferred successfully.'
      s.os8_send_cmd ('\\.', 'DEL ' + tfn)
      os.remove (ifn)
      os.remove (ofn)
    elif os.path.getsize (ofn) == 0:
      print "\nDifferences found: output is empty!\n"
    else:
      print "\nDifferences found:\n--------------------------------"
      os.system ('diff -wu "' + ifn + '" "' + ofn + '"')
      print 'Left ' + tfn + ' inside OS/8.'


if __name__ == "__main__":
    main()
Changes to tools/version.
28
29
30
31
32
33
34
35

36










37
38
39
40
41
42
43
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







-
+

+
+
+
+
+
+
+
+
+
+







# use or other dealings in this Software without prior written
# authorization from those authors.
########################################################################

use strict;
use warnings;

use Cwd 'abs_path';
use Cwd qw(abs_path getcwd);
use File::Basename;

# Give dummy result when run via test-mkos8.  A proper version string
# isn't helpful, and fossil isn't intended to be run in parallel
# multiple times in the same directory.  We could retry several times
# until we succeed, but again, it doesn't really help us to bother.
my $cwd = getcwd;
if ($cwd =~ m{/test/tmp/}x) {
    print "test:id[00000000]\n";
    exit 0;
}

my $topdir = dirname($0) . '/..';
if (-e "$topdir/.fslckout") {
    # Get version info from Fossil
    my ($branch, $checkout, $version, $comment);
    chdir $topdir;      # we might be building out-of-tree