PiDP-8/I Software

Check-in Differences
Log In

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

Difference From 7ce3553bf5f4bccd To 61ce7d6852d4e684

2017-01-26
02:09
Several adjustments to set_pidp8i_leds() calls. The main practical improvement is that we can now see execute state on the front panel. It was in that state too briefly before it got reset to fetch. check-in: 3c2086101e user: tangent tags: trunk
01:44
Removed the code that skips updating the global ledstatus[] values from the PDP-8 CPU core based on the human PoV limit. Because the GPIO thread runs asynchronously with it and has its own timing, these two high-jitter timing loops created a messy beat frequency on the front panel, which you could see in a tight KSF ; JMP .-1 loop, as OS/8 does when idle, waiting for a key. This mechanism came to us in the merger of the common parts of ILS and NLS, and ILS only needed it when it was modifying the LED brightness values values in the SIMH thread instead of in the GPIO thread as it does now. The only argument for continuing to do this — and for also doing it for NLS — is that it avoids a bunch of recalculation that the human couldn't see, but that is only true if the GPIO thread is able to stochastically sample the LED states so that even with the asynchronous jitter going on, a tight loop like the above eventually blurs together to a nice 50/50 mix of the two instructions' LED states, especially under the ILS. Ultimately, any attention paid to the human PoV limit has to be over in the GPIO thread. check-in: 61ce7d6852 user: tangent tags: trunk
01:08
Comment fix check-in: ec527293eb user: tangent tags: trunk
2016-11-21
09:33
Upstream version 20151215, containing everything from the tarball except for backups and *.o. Also, all CRLF text files converted to LF. check-in: ec36ee3dab user: tangent tags: trunk, v20151215
09:11
initial empty check-in check-in: 7ce3553bf5 user: tangent tags: trunk

Added .fossil-settings/binary-glob.










1
2
3
4
5
6
7
8
9
10
+
+
+
+
+
+
+
+
+
+
doc/*.png
examples/*.pt
labels/*.pdf
media/*/*.bin
media/*/*.dsk
media/*/*.pt
media/*/*.rk05
media/*/*.tu56
pics/*/*.jpg
pics/*/*.png
Added .fossil-settings/crlf-glob.





1
2
3
4
5
+
+
+
+
+
src/scp.*
src/sim_*.[ch]
src/sim_*.in
src/PDP8/pdp8_*.[ch]
src/PDP8/pidp8i.c.in
Added .fossil-settings/ignore-glob.

1
+
doc/simh/*.doc
Added 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Creators and Major Contributors to the PiDP-8/I Project

*   **Oscar Vermeulen <oscar.vermeulen@hotmail.com>**:

    -   Creator of the project (both hardware and software)
    
    -   Author of the 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.

*   **Mike Barnes** Ported Oscar Vermeulen's SimH 3.9 based PiDP-8/I
    simulator to the new SimH 4.0 code base.

*   **Dylan McNamee** Creator of the "buildroot" feature used in the
    creation of the official 2015.12.15 release versions.

*   **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
    init script that came with that release as `rc.pidp8`, shipped here
    as `pidp8i-init`.

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

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

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

*   **Rick Murphy <k1mu.nospam@gmail.com>** optimized the `pep001.pal` *
    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
    managed to land in this software distribution's OS/8 disk image.

*   **Tony Hill <hill.anthony@gmail.com>** Merged all the upstream SIMH
    changes between late September 2015 and late December 2016 into the
    PiDP-8/I simulator.

*   **Warren Young <tangentsoft@gmail.com>** Did everything listed in
    `ChangeLog.md` that is not attributed to anyone else.
Added 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# 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

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


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

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

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


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

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


## 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
Added ChangeLog.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# PiDP-8/I Changes

## Version 2017.01.23

*   When any program that talks to the PiDP-8/I front panel starts up,
    it now prints out a banner modeled on the [Erlang configuration
    line][ecl].  For example, when I run the software in the development
    tree on my PiDP-8/I, I get the following:

        PiDP-8/I trunk:i49cd065c [pi3b] [ils] [serpcb] [gpio]

    It tells me that:
    
    *   I'm running code built from Fossil checkin ID 49cd065c on the
        trunk branch, as opposed to a release version, which would be
        marked `release:v20170123` or similar.  (The `i` here is a tag
        standing for "ID", as in Fossil checkin ID.  Contrast `v` used
        to tag release version numbers.)

    *   I'm running it on a Raspberry Pi 3 Model B with Ian Schofield's
        incandescent lamp simulator (ILS) feature enabled.

    *   The software is built to expect that the PiDP-8/I PCB and the Pi
        board attached to it have had the serial mods made to them.

    *   The GPIO module found the GPIO hardware and was able to attach
        to it.

*   I get a very different result when running it on my desktop machine:

        PiDP-8/I trunk:id8536d91 [cake] [nls] [nopcb] [rt]

    This tells me:

    *   I'm running a different version of the development branch (i.e.
        the "trunk") of the code than what's running on the Pi.

    *   It's not running on a Pi at all.  (Cake ≠ pi.)

    *   I've disabled the ILS feature, so it's running with the "no lamp
        simulator" (NLS) GPIO module.
    
    *   Which is all to the good, because there's no point burning CPU
        power running the ILS code on a host machine that doesn't have a
        PiDP-8/I PCB attached.

    *   The GPIO thread is running with real-time privileges.

*   The ILS feature can now be disabled at `configure` time via the new
    `--no-lamp-simulator` flag.  This option is automatically set when
    building on a single-core Raspberry Pi.  (The flag is there only to
    allow someone building the software on a multi-core host machine to
    disable the ILS.)

*   Tweaked the ILS decay constants to be asymmetric, better mimicking
    the way real incandescent lamps work: they heat up to full
    brightness faster than they fade to perceptively "off."

*   The LED values used by the GPIO thread were being recalculated way
    too often.

    In the ILS case, it was updating the values approximately at the
    same rate as the ILS's PWM core frequency, roughly 7,500 times per
    second, which is far higher than the human persistence of vision
    limit.  While the PWM rate does need to be that fast to do its job,
    the underlying LED state values do not need to change nearly that
    often to fool the human into seeing instantaneous updates.

    The NLS case was actually worse, recalculating the LED values on
    every instruction executed by the PDP-8 CPU simulator, which even on
    a Pi 1 is likely to be a few MHz.

    In both the ILS and NLS cases, we now update the LED values about
    100 times a second, maintaining that rate dynamically based on the
    current execution speed of the simulator.

*   In prior versions, the ILS was only updating at its intended rate
    when the PDP-8 simulator was running flat-out on a current
    multi-core Raspberry Pi.  If you throttled the SIMH simulator to a
    slower execution rate, the display quality would start to degrade
    noticeably below about 1 MIPS.

*   With the prior fix, we now ship 5.script (i.e. the handler for
    starting the simulator with IF=5, or restarting it with IF=5 +
    `SING_STEP`) set to a throttle value of 30 kIPS, which allows the
    human to see each AC/MQ modification.  The built-in delay loops are
    still there, else we'd have to drop this to well under 1 kIPS.

*   The `SING_INST` switch now immediately puts the processor into
    single instruction mode, not requiring a separate press of the
    `STOP` key, as in prior versions.  This is the correct behavior
    according to the 1967-1968 edition of DEC's Small Computer Handbook
    for the PDP-8/I.

*   Greatly simplified the way single-instruction mode, processor
    stoppage, and the `CONT` key work.  The prior implementation was
    error-prone and difficult to understand.  This fixes a particularly
    bad interaction between the way `HLT` instructions and `CONT` key
    presses were handled, causing the processor to not resume correctly
    from `HLT` state.

*   Consolidated and cleaned up the bulk of the PiDP-8/I switch handling
    code so that it is not so intimately tied into the guts of the PDP-8
    CPU emulator.  This will greatly increase the chance that future
    updates to the upstream SIMH code will apply cleanly to our version.

*   Fixed a bug in `examples/bit-rotate.pal` which caused it to skip the
    actual bit rotation step.  We were trying to microcode two
    instructions into one that the PDP-8 won't accept together, and we
    didn't catch it until now because the HLT bug masked it, and the
    `palbart` assembler we ship isn't smart enough to notice the bug.

*   Fully generalized the mechanism for generating `obj/*.lst`,
    `bin/*.pt`, and `boot/*.script` from `examples/*.pal`.  You can now
    drop any PAL assembly language program into the `examples` directory
    and type `make` to build these various output forms automatically
    using the shipped version of `palbart`.  This effectively turns this
    PiDP-8/I software distribution into a PDP-8 assembly language
    development environment: rapidly build, test, and debug your PAL
    programs on your PC before you deploy them to real hardware.  Or,
    write PAL programs to debug the hardware or simulator, as we did
    with `examples/bit-rotate.pal`.

*   Fixed a sorting bug in the tool that generates `boot/*.script` from
    `obj/*.lst`, resulting in `dep` instructions that weren't sorted by
    core address.  This didn't cause any real problem, but it made
    tracing the execution of a PAL assembly program difficult if you
    were trying to refer to the `boot/*.script` file to check that the
    PiDP-8/I's front panel is showing the correct register values.

*   Updated SIMH to the latest upstream version and shipping a subset of
    the SIMH docs as unversioned files from tangentsoft.com.

*   The `configure` script now aborts the build if it sees that you're
    trying to build the software as root, since that means it generates
    the init script and the pidp8i script expecting to run the installed
    software as root, not as your normal user.  The most common way this
    happens is that you have a configured source tree, then change one
    of the `*.in` files and say `sudo make install`, thinking to build
    and install the change in one step.  This fixes that.

*   Several improvements to the build system.

[ecl]: http://stackoverflow.com/q/1182025/142454


## Version 2017.01.16

*   Prior releases did not include proper licensing for many of the
    included files.  This project was, therefore, not a proper Open
    Source Software project.  This problem has been fixed.

    In this release, many files that were previously technically only
    under standard copyright due to having no grant of license now have
    an explicit license, typically the same as SIMH itself.  (Thank you
    to all of the authors who allowed me to apply this license to their
    contributions!)

    For several other files, I was able to trace down some prior license
    and include its text here for the first time.

    There remain a few "gray" items: the TSS/8 and ETOS disk images.
    See the [`COPYING.md` file][copying] for more on the current status
    of these OS images.  If the legal status of these files clarifies in
    the future, this software distribution will react accordingly, even
    if that means removing these files from the distribution if we learn
    that these files are not freely-redistributable, as we currently
    believe them to be today.

*   The Step Counter LEDs on the front panel weren't being lit properly
    when EAE instructions were being used.  Thanks for this patch go to
    Henk Gooijen and Paul R. Bernard.

*   The prior `boot/1.script` and `boot/5.script` files are no longer
    simply opaque lists of octal addresses and machine code.  They are
    generated from PAL assembly files provided in the `examples`
    directory, so that you can now modify the assembly code and type
    `make` to rebuild these boot scripts.

*   The mechanism behind the prior item is fully general-purpose, not
    something that only works with `1.script` and `5.script`.  Any
    `examples/*.pal` file found at `make` time is transformed into a
    SIMH boot script named after the PAL file and placed in the `boot`
    directory.  This gives you an easier way to run PDP-8 assembly code
    inside the simulator.  After saying `make` to transform `*.pal` into
    `*.script` files, you can run the program with `bin/pidp8i-sim
    boot/my-program.script` to poke your program's octal values into
    core and run it.  This round-trip edit-and-run process is far faster
    than any of the options given in the [examples' `README.md`
    file][ex].

*   Disassembled both versions of the RIM loader to commented, labeled
    PAL assembly language files.  If you ever wanted to know what those
    16 mysterious instructions printed on the front panel of your
    PiDP-8/I did, you can now read my pidgin interpretation of these
    programs in `examples/*-rim.loader.pal` and become just as confused
    as I am now. :)

*   The two RIM loader implementations now start with a nonstandard
    `HLT` instruction so that when you fire up the simulator with IF=1 to
    start the high-speed RIM loader, it automatically halts for you, so
    you don't have to remember to STOP the processor manually.

    There is currently [a bug][hltbug] in the way the simulator handles
    `HLT` instructions which prevents you from simply pressing START or
    CONT to enter the RIM loader after you've attached your paper tape,
    so you still have to manually toggle in the 7756 starting address
    and press START to load the tape into core.  (I hope to fix this
    before the next release, but no promises.)

*   Added the `configure --throttle` feature for making the simulator
    run at a different speed than it normally does.  See
    [`README-throttle.md`][rmth] for details.

*   The build system now reacts differently when building the PiDP-8/I
    software on a single-core Raspberry Pi:

    *   If you're building the trunk or release branch, you'll get a
        configure error because it knows you can't run the current
        implementation of the incandescent lamp simulator on a
        single-core Pi.  (Not enough spare CPU power, even with heavy
        amounts of throttling.)

    *   If you're building the no-lamp-simulator branch, it inserts a
        throttle value into the generated `boot/*.script` files that do
        not already contain a throttle value so that the simulator
        doesn't hog 100% of the lone core, leaving some spare cycles for
        background tasks.  The above `--throttle` feature overrides
        this.

    These features effectively replace the manual instructions in the
    old `README-single-core.md` file, which is no longer included with
    this software distribution, starting with this release.

*   Lowered the real-time priority of the GPIO thread from 98 to 4.
    This should not result in a user-visible change in behavior, but it
    is called out here in case it does.  (In our testing, such high
    values simply aren't necessary to get the necessary performance,
    even on the trunk branch with the incandescent lamp simulator.)

*   Since v20161128, when you `make install` on a system with an
    existing PiDP-8/I software installation, the binary OS media images
    were not being overwritten, on purpose, since you may have modified
    them locally, so the installer chose not to overwrite your versions.

    With this release, the same principle applies to the SIMH boot
    scripts (e.g. `$prefix/share/boot/0.script`) since those are also
    things the user might want to modify.

    This release and prior ones do have important changes to some of
    these files, so if you do not wish to overwrite your local changes
    with a `make mediainstall` command, you might want to diff the two
    versions and decide which changes to copy over or merge into your
    local files.

[hltbug]:  https://tangentsoft.com/pidp8i/info/f961906a5c24f5de
[copying]: https://tangentsoft.com/pidp8i/doc/trunk/COPYING.md
[rmth]:    https://tangentsoft.com/pidp8i/doc/trunk/README-throttle.md


## Version 2017.01.05

*   Automated the process for merging in new SIMH updates.  From within
    the PiDP-8/I software build directory, simply say `make simh-update`
    and it will do its best to merge in the latest upstream changes.

    This process is more for the PiDP-8/I software maintainers than for
    the end users of that software, but if you wish to update your SIMH
    software without waiting for a new release of *this* software, you
    now have a nice automated system for doing that.

*   Updated SIMH using that new process.  The changes relevant to the
    PiDP-8/I since the prior update in release v20161226 are:

    *   Many more improvements to the simulator's internal timer system.
        This should make deliberate underclocking more accurate.

    *   It is now possible to get hex debug logs for the simulator console
        port by cranking up the simulator's debug level.

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

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

    *   Many improvements to the internal handling of timers.
    
        The most user-visible improvement is that you can now clock your
        emulated PDP-8 down to well below the performance of a real
        PDP-8 via `SET THROTTLE`, which can be useful for making
        blinkenlights demos run at human speeds without adding huge
        delay loops to the PDP-8 code implementing that demo.

    *   Increased the number of supported terminals from four to either
        twelve or sixteen, depending on how you look at it.  Eight of
        the additional supported terminal devices are conflict-free,
        while the final four variously conflict with one or more of the
        other features of the simulated PDP-8.  If you want to use all
        16, you will be unable to use the FPP, CT, MT and TSC features
        of the system.

        This limitation reflects the way the PDP-8 worked.  It is not an
        arbitrary limitation of SIMH.

    *   Added support for the LS8E printer interface option used by the
        WPS8 word processing system.

    *   The simulator's command console now shows the FPP register
        descriptions when using it as a PDP-8 debugger.

    *   Added the `SHOW TTIX/TTOX DEVNO` SIMH command to display the
        device numbers used for TTIX and TTOX.

    *   The `SHOW TTIX SUMMARY` SIMH command is now case-insensitive.

    *   Upstream improvements to host OS/compiler compatibility.  This
        increases the chances that this software will build out of the
        box on random non-Raspbian systems such as your development
        laptop running some uncommon operating system.

*   When you `make install`, we now disable Deeper Thought 2 and the
    legacy `pidp8` service if we find them, since they conflict with our
    `pidp8i` service.

*   Added the install user to the `gpio` group if you `make install` if
    that group is present at install time.  This is useful when building
    and installing the software on an existing Raspbian SD card while
    logged in as a user other than `pi` or `pidp8i`.

[simh]: https://github.com/simh/simh/


## Version 2016.12.18

*   The entire software stack now runs without explicit root privileges.
    It now runs under the user and group of the one who built the
    software.

    For the few instances where it does need elevated privileges, a
    limited-scope set of sudo rules are installed that permit the
    simulator to run the necessary helper programs.

*   The power down and reboot front panel switch combinations are no
    longer sensitive to the order you flip the switches.

*   Changed the powerdown front panel switch combination to the more
    mnemonically sensible `Sing_Step` + `Sing_Inst` + `Stop`.

    Its prior switch combo — `Sing_Step` + `Sing_Inst` + `Start` — is
    now the reboot sequence, with the mnemomic "restart."

*   Removed the USB stick mount/unmount front panel switch combos.  The
    automount feature precludes a need for a manual mount command, and
    unmount isn't necessary for paper tape images on FAT sticks.

*   The simulator now runs correctly on systems where the GPIO setup
    process fails.  (Basically, anything that isn't a Raspberry Pi.)
    Prior to this, this failure was just blithely ignored, causing
    subsequent code to behave as though all switches were being pressed
    at the same time, causing utter havoc.

    The practical benefit of this is that you can now work with the
    software on your non-Pi desktop machine, losing out only on the
    front panel LEDs and switches.  Everything else works just as on the
    Pi.  You no longer need a separate vanilla SimH setup.

*   Added a locking mechanism that prevents `pidpi8-test` and
    `pidp8i-sim` from fighting over the front panel LEDs.  While
    one of the two is running, the other refuses to run.

*   Added `examples/ac-mq-blinker.pal`, the PAL8 assembly code for the
    `boot/5.script` demo.

*   Fixed two unrelated problems with OS/8's FORTRAN IV implementation
    which prevented it from a) building new software; and b) running
    already-built binaries.  Thanks go to Rick Murphy for providing the
    working OS/8 images from which the files needed to fix these two
    problems were extracted.

*   Added the VT100-patched `VTEDIT` TECO macro from Rick Murphy's OS/8
    images, and made it automatically run when you run TECO from the
    OS/8 disk pack.  Also added documentation for it in `VTEDIT.DC` on
    the disk pack as well as [in the wiki][vteditdoc].

*   The default user name on the binary OS images is now `pidp8i`
    instead of `pi`, its password has changed to `edsonDeCastro1968`,
    and it demands a password change on first login.  I realize it's a
    hassle, but I decided I didn't want to contribute to the plague of
    open-to-the-world IoT boxes.

*   Many build system and documentation improvements.

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


## Version 2016.12.06

*   The `pidp8i-test` program's LED test routines did not work correctly
    when built against the incandescent lamp simulator version of the
    GPIO module.  Reworked the build so that this test program builds
    against the no-lamp-simulator version instead so that you don't have
    to choose between having the lamp simulator or having a working
    `pidp8i-test` program.

*   More improvements to `examples/pep001.pal`.

*   Extracted improved `PRINTS` routine from that example as
    `examples/routines/prints.pal`.


## Version 2016.12.05

*   This release marks the first binary SD card image released under my
    maintainership of the software.  As such, the major user-visible
    features in this release of the Fossil tree simply support that:

    *   The `pidp8i-init` script now understands that the OS's SSH host
        keys may be missing, and re-generates them.  Without this
        security measure, anyone who downloads that binary OS image
        could impersonate the SSH server on *your* PiDP-8/I.

    *   Added a `RELEASE-PROCESS.md` document.  This is primarily for my
        own benefit, to ensure that I don't miss a step, particularly
        given the complexity of producing the binary OS image.  However,
        you may care to look into it to see what goes on over here on
        the other side of the Internet. :)

*   Added an OS/8 BASIC solution to Project Euler Problem #1, so you can
    see how much simpler it is compared to the PAL8 assembly language
    version added in the prior release.

*   Updated the PAL8 assembly version with several clever optimizations
    by Rick Murphy, the primary effect of which is that it now fits into
    a single page of PDP-8 core memory.


## Version 2016.12.03

*   Debounced the switches.  See [the mailing list post][cdb] announcing
    this fix for details.

*   Merged the [`pidp8i-test` program][testpg] from the mailing list.
    The LED testing portion of this program [currently][gpiols] only works
    correctly without the incandescent lamp simulation patch applied.

*   Added a solution to [Project Euler Problem #1][pep001] in PAL8
    assembly language and wrote the [saga of my battle][p1saga] with
    this problem into the wiki.  This also adds a couple of useful PAL8
    routines in `examples/routines`.

*   Integrated David Gesswein's latest `palbart` program (v2.13) into
    the source distribution so that we don't have to:
    
    1.  ship pre-built RIM format paper tapes for the examples; and

    2.  put up with the old versions that OS package repos tend to have
        (Ubuntu is still shipping v2.4, from 6 years ago!)

*   Fixed a bug in the `make install` script that caused it to skip
    installing `screen` and `usbmount` from the OS's package repo when
    they are found to be missing.

*   Fixed a related bug that prevented it from disabling the serial
    console if you configure the software without `--serial-mod` and
    then install it, causing the serial console and the GPIO code in the
    PiDP-8/I simulator to fight over GPIO pins 14 and 15.

*   Removed the last of the duplicate binary media entries.  This makes
    the zip files for this version well under half the size of those for
    the 2015.12.15 upstream release despite having more features.

[cdb]:    https://groups.google.com/d/msg/pidp-8/Fg9I8OFTXHU/VjamSoFxDAAJ
[testpg]: https://groups.google.com/d/msg/pidp-8/UmIaBv2L9Ts/wB1CVeGDAwAJ
[gpiols]: https://tangentsoft.com/pidp8i/tktview?name=9843cab968
[pep001]: https://projecteuler.net/problem=1
[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.

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

*   Changed all of the various "PDP," "PDP-8", and "PiDP-8" strings to
    variants on "PiDP-8/I", partly for consistency and partly because it
    seems unlikely that this software will ever be used with anything
    other than the PiDP-8/I project.

    Part of this renaming means that the default installation location
    is now `/opt/pidp8i`, which has the nice side benefit that
    installing this version of the software will not overwrite an
    existing installation of the upstream version in `/opt/pidp8`.

    Another user-visible aspect of this change is that the upstream
    version's `pdp.sh` script to [re]enter the simulator is now called
    `pidp8i`.

*   Merged Ian Schofield's [Display update for the PiDP8][dupatch]
    patch.  Currently it is not optional, but there is [a plan][dudis] to
    allow this feature to be disabled via a `configure` script option.

*   The scripts that control the startup sequence of the PiDP-8/I
    simulator now include helpful header comments and emit useful
    status messages to the console.  Stare no more at opaque lists
    of SimH commands, wondering what each script does!

*   Merged `scanswitch` into the top-level `src` directory, since the
    only thing keeping it in a separate directory was the redundant
    `gpio.h` file. There were minor differences between the two `gpio.h`
    files, but their differences do not matter.

*   Installing multiple times no longer overwrites the binary OS/program
    media, since the disk images in particular may contain local
    changes.  If you want your media images overwritten, you can insist
    on it via `make mediainstall`.

*   The installation tree follows the Linux Filesystem Hierarchy
    Standard, so that files are in locations an experienced Linux user
    would expect to find them.  The biggest changes are that the content
    of the upstream `bootscripts` tree is now installed into
    `$prefix/share/boot`, and the OS/program media images which used to
    be in `imagefiles` are now in `$prefix/share/media`.

*   Added a bunch of ancillary material: [wiki articles][wiki],
    [USB stick label artwork][art], a PAL8 assembly [example program][ex]
    for you to toggle in, etc. Also filed a bunch of [tickets][tix]
    detailing feature proposals, known bugs and weaknesses, etc. If you
    were looking for ways to contribute to the development effort, these
    new resources provide a bunch of ideas.

*   Made some efforts toward portability.

    While this project will always center around Raspbian and the
    PiDP-8/I add-on board, the intent is that you should be able to
    unpack the project on any other Unix type system and at least get
    the simulator up and running with the normal SimH manual control
    over execution instead of the nice front panel controls provided by
    the PiDP-8/I board.

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


## Version 2015.12.15

*   The official upstream release of the software, still current as of
    late 2016, at least.
Added HACKERS.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Hacking on the PiDP-8/I Software
====

If you are going to make any changes to the PiDP-8/I software, here are
some rules and hints to keep in mind while you work.


Getting Started with Fossil
----

The PiDP-8/I software project is hosted using the [Fossil][fossil]
[distributed version control system][dvcs].  Fossil provides most of the
features of GitHub under a simpler operating model than Subversion
without tying you to a proprietary web service.

This guide will introduce you to some of the basics, but you should also
at least read the [Fossil Quick Start Guide][fqsd]. For a more thorough
introduction, I recommend [the Schimpf book][fbook]. If you have
questions, it is best to ask them on [its low-volumn mailing list][fml],
though you may also ask me, either on [the PiDP-8/I mailing list][ggml]
or via private email.

Most Raspberry Pi OS distributions include Fossil in their package
repository, and it is also available for all common desktop platforms.
If you started with one of the binary OS images downloaded from
tangentsoft.com, Fossil is already installed.  If you don't like any of
those options, you can also use [the official binaries][fbin].


[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
[ggml]:   https://groups.google.com/forum/#!forum/pidp-8


Fossil Anonymous Access
----

To clone the code repository anonymously, say:

    $ mkdir ~/museum         # because where else do you store fossils?
    $ fossil clone https://tangentsoft.com/pidp8i ~/museum/pidp8i.fossil
    $ mkdir -p ~/src/pidp8i/trunk
    $ cd ~/src/pidp8i/trunk
    $ fossil open ~/museum/pidp8i.fossil

The second 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 just a convention,
not a requirement.  (There is one feature of Fossil that requires that
file extension, but you probably won't use that feature.)


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

Second, the top-level project directory stores multiple separate
checkouts, one for each branch or tag I'm actively working with at the
moment.  So, to add a few other checkouts, you could say:

    $ cd ~/src/pidp8i
    $ mkdir -p release          # another branch
    $ mkdir -p v20151215        # a tag this time, not a branch
      ...etc...
    $ cd release
    $ fossil open ~/museum/pidp8i.fossil release
    $ cd ../v20151215
    $ fossil open ~/museum/pidp8i.fossil v20151215
      ...etc...

This gives you multiple independent checkouts.  The branch checkouts
remain pinned to the tip of that branch, so that if someone else checks
changes in on that branch and you say `fossil update`, those changes
appear in your checkout of that branch.  The tag checkouts behave
differently, always showing a specific checkout with the given tag name.

(In Fossil, tags and branches are related, but the details are beyond
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.

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

While Fossil does allow offline operation and local independent clones,
its default mode of operation is to try and keep the clones in sync as
much as possible.  Git works the way it does because it was designed to
meet the needs of the Linux kernel development project, which is
inherently federated, so Git tries to operate in a federated model as
well.  Fossil is better for smaller, more coherent teams, where there is
a single, clear goal for the project and a single source for its
official code.  Fossil helps remote developers cooperate, whereas Git
helps remote developers go off on their own tangents for extended
periods of time and optionally sync back up with each other
occasionally.

Fossil is a better match for the way the PiDP-8/I software project
works: we want you to cooperate closely with us, not go off on wild
tangents.

[gitwt]:  https://git-scm.com/docs/git-worktree


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

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.
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
attempting to sync when you know it will fail.


Getting Developer Access
----

The administrator of this repository is Warren Young, whose email you
can find on the [official PiDP-8/I project mailing list][ggml].
Developer access is available to anyone who makes a reasonable request.


Creating Branches
----

Creating a branch in Fossil is scary-simple, to the point that those
coming from other version control systems may ask, "Is that really all
there is to it?"  Yes, really, this is it:

    $ fossil ci --branch new-branch-name

That is to say, you make your changes as you normally would; then when
you go to check them in, you give the `--branch` option to the
`ci/checkin` command to put the changes on a new branch, rather than add
them to the same branch the changes were made against.

While developers with login rights to the PiDP-8/I Fossil instance are
allowed to check in on the trunk at any time, we recommend using
branches whenever you're working on something experimental, or where you
can't make the necessary changes in a single coherent checkin.
Basically, `trunk` should always build without error, and it should
always function correctly.  Branches are for isolating work until it is
ready to merge into the trunk.

Here again we have a difference with Git: because Fossil normally syncs
your work back to the central repository, this means we get to see the
branches you are still working on.  This is a *good thing*.  Do not fear
committing broken or otherwise bad code to a branch.  [You are not your
code.][daff]  We are software developers, too: we understand that
software development is an iterative process, and that not all ideas
spring forth perfect and production-ready from the fingers of its
developers.  These public branches let your collaborators see what
you're up to, and maybe lend advice or a hand in the work, but mostly
public branches let your collaborators see what you're up to, so they're
not surprised when the change finally lands in trunk.

This is part of what I mean about Fossil fostering close cooperation
rather than fostering wild tangents.

Jim McCarthy (author of [Dynamics of Software Development][dosd]) has a
presentation on YouTube that touches on this topic at a couple of
points:

* [Don't go dark](https://www.youtube.com/watch?v=9OJ9hplU8XA)
* [Beware of a guy in a room](https://www.youtube.com/watch?v=oY6BCHqEbyc)

Fossil's sync-by-default behavior fights these negative tendencies.

[daff]: http://www.hanselman.com/blog/YouAreNotYourCode.aspx
[dosd]: http://amzn.to/2iEVoBL


Debug Builds
----

By default, the build system creates a release build, but you can force
it to produce a binary without as much optimization and with debug
symbols included:

     $ ./configure --debug-mode


Manipulating the Build System Source Files
----

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

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

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


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
trunk of PiDP-8/I:

    $ fossil diff > my-changes.patch

Then attach that file to a new [PiDP-8/I mailing list][ggml] message
along with a declaration of the license you wish to contribute your
changes under.  We suggest using the [SIMH license][simhl], but any
[non-viral][viral] [OSI-approved license][osil] should suffice.

If your change is more than a small patch, `fossil diff` might not
incorporate all of the changes you have made.  The old unified `diff`
format can't encode branch names, file renamings, file deletions, tags,
checkin comments, and other Fossil-specific information.  For such
changes, it is better to send a Fossil bundle:

    $ fossil set autosync 0                # disable autosync
    $ fossil checkin --branch my-changes
      ...followed by more checkins on that branch...
    $ fossil bundle export --branch my-changes my-changes.bundle

After that first `fossil checkin --branch ...` command, any subsequent
changes will also be made on that branch without needing a `--branch`
option until you explicitly switch to some other branch.  This lets you
build up a larger change on a private branch until you're ready to
submit the whole thing as a bundle.

Because you are working on a branch on your private copy of the
PiDP-8/I Fossil repository, you are free to make as many checkins as
you like on the new branch before giving the `bundle export` command.

Once you are done with the bundle, send it to the mailing list just as
with the patch.

If you provide a quality patch, we are likely to offer you a developer
login on [the repository][repo] so you don't have to continue with the
patch or bundle methods.

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/
[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
----

Every code base should have a common code style.  Love it or
hate it, here are PiDP-8/I's current code style rules:

**C Source Code**

File types: `c`, `h`, `c.in`

We follow the SIMH project's pre-existing code style when modifying one
of its source files:

*   Spaces for indents, size 4

*   DOS line endings.  (Yes, even though this is a Linux-based project!
    All decent Linux text editors can cope with this.)

*   Function, structure, type, and variable names are all lowercase,
    with underscores separating words

*   Macro names are in `ALL_UPPERCASE_WITH_UNDERSCORES`

*   Whitespace in the SIMH C files is of a style I have never seen
    anywhere else in my decades of software development.  This example
    shows the important features:

        int some_function (char some_parameter)
        {
        int some_variable = 0;

        if (some_parameter != '\0') {
            int nbytes = sizeof (some_parameter);
            char *buffer = malloc (4 * nbytes);

            switch (some_parameter) {
				case 'a':
					do_something_with_buffer ((char *)buffer); 
				default:
					do_something_else ();
                }
            }
        else {
            some_other_function (with_a_large, "number of parameters",
                wraps_with_a_single, "indent level");
            printf (stderr, "Failed to allocate buffer.\n");
            }
        }
    
    It is vaguely like K&R C style except that:

    -   The top level of statements in a function are not indented

    -   The closing curly brace is indented to the same level as the
        statement(s) it contains

    -   There is a space before all opening parentheses, not just those
        used in `if`, `while`, and similar flow control statements.

        Nested open parentheses do not have extra spaces, however.  Only
        the outer opening parenthesis has a space separating it from
        what went before.

    -   Multiple variables declared together don't have their types and
        variable names aligned in columns.

I find that this style is mostly sensible, but with two serious problems:
I find the indented closing curly braces confusing, and I find that the
loss of the first indent level for the statements inside a function makes
functions all visually run together in a screenful of code.  Therefore,
when we have the luxury to be working on a file separate from SIMH,
we use a variant of its style with these two changes, which you can
produce with this command:

    $ indent -kr -nce -cli4 -nlp -pcs -di1 -i4 -l100 \
        -ncs -ss -nbbo FILES...

That is, start with K&R, then:

-   nce:  don't cuddle else
-   cli4: indent case statement labels 4 spaces
-   nlp:  don't align continued statements at the opening parenthesis
-   pcs:  put a space before the opening parenthesis of a function call
-   di1:  don't line up variable types and names in separate columns
-   i4:   use 4-space indents
-   l100: allow lines up to 100 columns before forcibly breaking them
-   ncs:  don't put a space between a cast and its operand
-   ss:   add a space before semicolon with empty loop body
-   nbbo: don't break long lines before || and && operators

That gives the following, when applied to the above example:

        int some_function (char some_parameter)
        {
			int some_variable = 0;

			if (some_parameter != '\0') {
				int nbytes = sizeof (some_parameter);
				char *buffer = malloc (4 * nbytes);

				switch (some_parameter) {
					case 'a':
						do_something_with_buffer ((char *)buffer); 
					default:
						do_something_else ();
				}
			}
			else {
				some_other_function (with_a_large, "number of parameters",
					wraps_with_a_single, "indent level");
				printf (stderr, "Failed to allocate buffer.\n");
			}
        }
    
If that looks greatly different, realize that it is just two indenting
level differences: add one indent at function level, except for the
closing braces, which we leave at their previous position.

SIMH occasionally exceeds 100-column lines.  I recommend breaking
long lines at 72 columns.  Call me an 80-column traditionalist.

BSD `indent` does't understand the `-kr` option, so you can use this
alternative on BSD and macOS systems:

    $ indent -nce -cli4 -nlp -pcs -di1 -i4 -l100 \
            -bap -ncdb -nfc1 -npsl -nsc FILES...

When in doubt, mimic what you see in the current code.  When still in
doubt, ask on the mailing list.

[indent]: http://linux.die.net/man/1/indent


**Plain Text Files**

File types: `md`, `txt`

*   Spaces for indents, size 4.

*   Unix line endings.  The only common text editor I'm aware of that
    has a problem with this is Notepad, and people looking at these
    files anywhere other than unpacked on a Raspberry Pi box are
    probably looking at them through the Fossil web interface on
    tangentsoft.com.

*   Markdown files must follow the syntax flavor understood by
    [Fossil's Markdown interpreter][fmd].

[fmd]: https://tangentsoft.com/pidp8i/md_rules
Added 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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
########################################################################
# Makefile.in - Processed by autosetup's configure script to generate
#    the GNU make(1) file for building the PiDP-8/I software.
#
# 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 © 2015-2017 Oscar Vermeulen, 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.
########################################################################

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

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) \
	-U__STRICT_ANSI__ \
	-I @srcdir@/src -I @srcdir@/src/PDP8 -I @builddir@/src

SIM = bin/pidp8i-sim
BINS = bin/palbart $(SIM) bin/pidp8i-test libexec/scanswitch

BUILDDIRS = bin libexec obj/PDP8

INSTDIRS = bin etc libexec share/boot share/media share/man/man1

OBJS = \
	obj/gpio-common.o \
	obj/sim_console.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 \
	obj/PDP8/pdp8_lp.o \
	obj/PDP8/pdp8_mt.o \
	obj/PDP8/pdp8_pt.o \
	obj/PDP8/pdp8_rf.o \
	obj/PDP8/pdp8_rk.o \
	obj/PDP8/pdp8_rl.o \
	obj/PDP8/pdp8_rx.o \
	obj/PDP8/pdp8_sys.o \
	obj/PDP8/pdp8_td.o \
	obj/PDP8/pdp8_tsc.o \
	obj/PDP8/pdp8_tt.o \
	obj/PDP8/pdp8_ttx.o \
	obj/PDP8/pidp8i.o \
	obj/scp.o \
	obj/sim_disk.o \
	obj/sim_ether.o \
	obj/sim_fio.o \
	obj/sim_serial.o \
	obj/sim_sock.o \
	obj/sim_tape.o \
	obj/sim_timer.o \
	obj/sim_tmxr.o

LIBS = -lm -ldl -lpthread

EXAMPLES := $(wildcard @srcdir@/examples/*.pal)
EXAMPLES := $(subst @srcdir@/examples,bin,$(EXAMPLES))
EXAMPLES := $(EXAMPLES:.pal=.pt)
LISTINGS := $(EXAMPLES:.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!
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@/etc/pidp8i-init.in \
	@srcdir@/etc/sudoers.in \
	@srcdir@/examples/Makefile.in \
	@srcdir@/src/Makefile.in \
	@srcdir@/src/gpio-common.c.in \
	@srcdir@/src/PDP8/Makefile.in \
	@srcdir@/src/PDP8/pidp8i.c.in \
	@srcdir@/src/scp.c.in \
	@srcdir@/tools/simh-update.in
OUTFILES := $(subst @srcdir@/,,$(INFILES))
OUTFILES := $(subst .in,,$(OUTFILES))

CLTXT = /boot/cmdline.txt

.PHONY: tags

all: $(OUTFILES) $(BUILDDIRS) $(BINS) $(BOOTSCRIPTS) $(LISTINGS) $(EXAMPLES)
	@chmod 755 @builddir@/bin/pidp8i

clean:
	@rm -f $(BINS) $(BOOTSCRIPTS) $(EXAMPLES) $(LISTINGS) $(OBJS) \
		@builddir@/tags \
			@builddir@/obj/*.d \
		@builddir@/obj/*.o \
		@builddir@/obj/PDP8/*.d \
		@srcdir@/examples/*.err
	@for f in $(OUTFILES) ; do test "$$f" = "$${f/Makefile//}" && rm $$f ; done
	@-rmdir -p $(BUILDDIRS) 2> /dev/null || true

distclean: clean
	@rm -f \
		@builddir@/config.log \
		@builddir@/Makefile \
		@builddir@/autosetup/jimsh0 \
		@builddir@/examples/Makefile \
		@builddir@/src/Makefile \
		@builddir@/src/config.h \
		@builddir@/src/PDP8/Makefile

ctags tags:
	ctags -R @srcdir@

install: all
	@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 @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

	@# 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 750 @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

	@# Give the install user permission to shut down and reboot the Pi
	@# if this is a systemd/sudo based system.
	@(	test -d /etc/sudoers.d -a -w /etc/sudoers.d -a -x /bin/systemctl && \
		@INSTALL@ -m 440 -o root -g root @srcdir@/etc/sudoers \
		    /etc/sudoers.d/099_pidp8i \
	) || true

	@# Add installation bin dir to the non-root user's PATH unless it's
	@# already in there or we aren't running under sudo.
	@(for p in .profile .bash_profile ; do \
		test -n "$$SUDO_USER" -a -w "/home/$$SUDO_USER/$$p" && \
			! grep -qF "@ABSPREFIX@/bin" "/home/$$SUDO_USER/$$p" && \
			echo "export PATH=\$$PATH:@ABSPREFIX@/bin" >> "/home/$$SUDO_USER/$$p" ; \
	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) || 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@" -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 palbart stuff
	@INSTALL@ -m 755 @builddir@/bin/palbart @prefix@/bin
	@INSTALL@ -m 644 @srcdir@/palbart/palbart.1 @prefix@/share/man/man1

mediainstall:
	@echo "[Re]installing OS and program media..."
	@cd @srcdir@ ; \
	find media \( \
		-name \*.bin  -o \
		-name \*.dsk  -o \
		-name \*.rk05 -o \
		-name \*.tu56 \
	\) -exec @INSTALL@ -D -m 664 -g @INSTGRP@ {} @ABSPREFIX@/share/{} \;
	@INSTALL@ -m 664 -g @INSTGRP@ @builddir@/boot/*.script @BOOTDIR@

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

reconfig:
	@AUTOREMAKE@

release: all
	@srcdir@/tools/mkrel

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


# Rule for compiling *.c to *.o and autogenerating dependency info.
# Explained at http://scottmcpeak.com/autodepend/autodepend.html
#
# Reflect any changes here into near-duplicate below!
obj/%.o: @srcdir@/src/%.c
	$(CC)  -c $(CFLAGS) @srcdir@/src/$*.c -o obj/$*.o
	$(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: @builddir@/src/%.c
	$(CC)  -c $(CFLAGS) @builddir@/src/$*.c -o obj/$*.o
	$(CC) -MM $(CFLAGS) @builddir@/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 example PAL assembly language programs.
obj/%.lst bin/%.pt: @srcdir@/examples/%.pal bin/palbart
	@builddir@/bin/palbart -lr $< || cat @srcdir@/examples/$*.err
	mv @srcdir@/examples/$*.lst @builddir@/obj/$*.lst
	mv @srcdir@/examples/$*.rim @builddir@/bin/$*.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/$*.d

$(BUILDDIRS):
	mkdir -p $@

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

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

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

ifeq ($(findstring clean,$(MAKECMDGOALS)),)
Makefile: @srcdir@/Makefile.in @srcdir@/auto.def $(INFILES) @AUTODEPS@
	@AUTOREMAKE@ && $(MAKE)

# If you simply make $(OUTFILES) 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 shit up
# right good and proper.  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.
bin/pidp8i: @srcdir@/bin/pidp8i.in
	@AUTOREMAKE@ && $(MAKE)
endif

-include $(OBJS:.o=.d) obj/scanswitch.d

Added README-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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# 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:

    $ sudo systemctl stop pidp8i
	$ 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].)


## Test Procedure

You can at any time hit Ctrl-C to stop the test.

The test proceeds as follows:

*   All On test:

    It turns on all LEDs for 5 seconds.

*   All Off test:

    It turns off all LEDs for 5 seconds.

*   Row test:

    It turns on one full row of LEDs and pauses for 5 seconds, then
    switches to the next row.  There are eight rows of LEDs of up to 12
    LEDs each.

*   Column test:

    It then turns on one full column of LEDs and pauses for 5 seconds,
    then switches to the next column.  There are 12 columns of LEDs with
    up to 8 LEDs each.  (Some of the LEDs positions in a column are
    sometimes rather chaotic, it will require intimate knowledge of the
    schematic to verify.  It's somewhat of a useless test but it might
    turn up an assembly error for someone.)

*   Switch test:

    It then goes into a single LED chase pattern and starts looking at
    switches.  This loop is infinite.  Every time it detects a change in
    the switch positions it prints out the full Octal bit pattern for
    the three switch banks.  No attempt is made to name the actual
    switch that has been flipped.  The goal is to verify switch
    functionality, not to debug the design of the circuit or the driver.

    When running this test, if you get a new line printed with a single
    bit change when you flip a single switch, the switch in question is
    working.  If you get no output printed or multiple bits changed in
    the output printed something is wrong.

    If for some reason you need to decode the output bits to physical
    switches they appear as follows:

    | A    | B    | C   
    |-------------------
    | 4000 | 0000 | 0000

    The first twelve bits (labelled A) is the Switch Register.  The bits
    left to right correspond to the SR switches also left to right.  So
    above the SR1 switch is toggled down, ie 1.  Every other SR switch
    is up, ie 0.

    The leftmost 6 bits (labelled B) are the 3 DF switches followed by
    the 3 IF switches.  Again left to right.  The rest of the bits are
    unused in the B section.

    The leftmost 8 bits (labelled C) are the remaining 8 switches
    starting at "START" and ending at "SING INST".  Again Left to right.


## License

This document is licensed under the same terms as the associated
[`src/test.c` program][program].


[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 README-throttle.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Throttling the Simulator

When you do not give the `--throttle` option to the `configure` script,
the simulator's speed is set based on the number of CPU cores detected
by the `tools/corecount` script.


## Multi-Core Default

If `corecount` detects a multi-core system, the default behavior is to
not throttle the simulator at all, since there are only 2 threads in the
software that take substantial amounts of CPU power.

The most hungry thread is the PDP-8 simulator proper, which runs flat-out,
taking an entire core's available power by default.

The other hungry thread is the one that drives the front panel LEDs,
which takes about 15% of a single core's power on a Raspberry Pi 3 when
you build the software with the incandescent lamp simulator enabled.

This leaves over 2 cores worth of CPU power untapped on multi-core
Raspberry Pis, so the system performance remains snappy even with the
simulator running.

You can force this behavior with `--throttle=none`.


## Single-Core Default

If the `configure` script decides that you're building this on a
single-core system, it purposely throttles the PDP-8 simulator so that
it takes about 75% of a single core's worth of power on the slowest
Raspberry Pi supported by this software. This leaves enough CPU power
for some background tasks on a single-core Pi.

This default assumes you are building without the incandescent lamp
simulator feature enabled, as that currently takes so much CPU power to
run that the simulator runs slower than even a PDP-8/S!  (We're working
on ways to improve the speed of that lamp simulator to let it run on
single-core raspberry Pis.)  Indeed, the build system will actively try
to prevent you from building the incandescent lamp simulator feature on
a single-core Pi.

You can force the build system to select this throttle value even on a
multi-core Pi with `--throttle=single-core`.

You will erroneously get this single-core behavior if you run the
`configure` script on a system where `tools/corecount` has no built-in
way to count the CPU cores in your system correctly, so it returns 1,
forcing a single-core build. That script currently only returns the
correct value on Linux, BSD, and macOS systems.  To fix it, you can
either say `--throttle=none` or you can patch `tools/corecount` to
properly report the number of cores on your system.  If you choose the
latter path, please send the patch to the mailing list so it can be
integrated into the next release of the software.


## Underclocking

If you want the software to run even slower, there are additional
`configure --throttle` option values available to achieve that:

*   `--throttle=STRING`: any value not otherwise understood is passed
    directly to SIMH in `SET THROTTLE` commands inserted into the
    generated `boot/*.script` files. You can use any string here that
    SIMH itself supports; RTFM.

*   `--throttle=CPUTYPE`: if you give a value referencing one of the
    many PDP-8 family members, it selects a value based on the execution
    time of `TAD` in direct access mode on that processor:

    | Value            | Alias For | Memory Cycle Time
    ---------------------------------------------------
    | `pdp8e`          | 416k      | 1.2 µs
    | `pdp8i`, `pdp8a` | 333k      | 1.5 µs
    | `pdp8l`, `pdp8`  | 313k      | 1.6 µs
    | `ha6120`         | 182k      | 2.7 µs
    | `im6100a`        | 200k      | 2.5 µs
    | `im6100`         | 100k      | 5 µs
    | `im6100c`        | 83k       | 6 µs
    | `pdp8s`          | 63k       | 8 µs

    I chose `TAD` because it's a typical instruction for the processor,
    and its execution speed is based on the memory cycle time for the
    processor, an easy specification to find.  Other instructions (e.g.
    most OPR instructions) execute faster than this, while others (e.g.
    IOT) execute far slower.  (See the processor's manual for details.)

    SIMH, on the other hand, does not discriminate.  When you say
    `--throttle=pdp8i`, causing the build system to insert `SET THROTTLE
    333k` commands into the SIMH boot scripts, the SIMH PDP-8 simulator
    does its best to execute exactly 333,000 instructions per second,
    regardless of the instruction type.  Consequently, if you were to
    benchmark this simulator configured with one of the options above,
    there would doubtless be some difference in execution speed,
    depending on the mix of instructions executed.
    
    (See the I/O Matters section below for a further complication.)

    The values for the Intersil and Harris CMOS microprocessors are for
    the fastest clock speed supported for that particular chip. Use the
    `STRING` form of this option if you wish to emulate an underclocked
    microprocessor.

*   `--throttle=human`: Causes the computer to throttle the human.

    "I'm sorry, Dave, but you are not worthy to run this software."

    "Aaackkthhhpptt..."

    No, wait, that can't be right.
    
    Let's see here...ah, yes, what it *actually* does is slows the
    processor down to 10 instructions per second, about the fastest that
    allows the human eye to easily discern LED state changes as
    separate.  If you increase it very much above this, the eye starts
    seeing the LED state changes as a blur.

    This mode is useful for running otherwise-useful software as a
    "blinkenlights" demo.
    
*   `--throttle=trace`: Alias for `--throttle=1`, causing the simulator
    to act more or less like it's in single-instruction mode and you're
    pressing the `CONT` button once a second to step through a program.


## I/O Matters

The throttle mechanism discussed above only affects the speed of the
PDP-8 CPU simulator. It does not affect the speed of I/O operations.

The only I/O channel you can throttle in the same way is a serial
terminal by purposely choosing a slower bit rate for it than the maximum
value. If you set it to 110 bps, it runs at the speed of a Teletype
Model 33 ASR, the most common terminal type used for the PDP-8/I, and
most other early PDP-8 flavors. Later PDP-8s were often paired with (or
integrated into!) glass TTYs such as the VT05, which flew along at 2400
bps. Then things got really fancy with the VT52, which screamed along at
9600 bps. Wowee!

I'm not aware of a way to make SIMH slow the other I/O operations, such
as disk access speeds, in order to emulate the speed of the actual
hardware.


## License

Copyright © 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 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# 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.

    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.

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

*   A working C compiler and other standard Linux build tools, such as
    `make(1)`.  On Debian type systems — including Raspbian — you can
    install such tools with `sudo apt install build-essential`


## Configuring, Building and Installing

This software distribution builds and installs in the same way as most
other Linux/Unix software these days.  The short-and-sweet is:

    $ ./configure && make && sudo make install


### Configure Script Options

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


#### --prefix

Perhaps the most widely useful `configure` script option is `--prefix`,
which lets you override the default installation directory,
`/opt/pidp8i`.  You could make it install the software under your home
directory on the Pi with this command:

    $ ./configure --prefix=$HOME/pidp8i && sudo make install

Although this is installing to a directory your user has write access
to, you still need to install via `sudo` because the installation
process does other things that do require `root` access.


#### --no-lamp-simulator

If you build the software on a multi-core host, the PDP-8/I simulator is
normally built with Ian Schofield's incandescent lamp simulator feature,
which drives the LEDs in a way that mimics the incandescent lamps used
in the original PDP-8/I.  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.

Those on a multi-core host who want this low-CPU-usage LED driving
method can give the `--no-lamp-simulator` option to `configure`.


#### --serial-mod

If you have done the [serial mod][smod] 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.


#### --throttle

See [`README-throttle.md`][thro] for the values this option takes.  If
you don't give this option, the simulator runs as fast as possible, more
or less.


#### --help

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


### Installing

The `sudo make install` step in the command above does what most people
want.

That step will not overwrite the operating system and program media
(e.g. the OS/8 RK05 disk cartridge image) when installing multiple times
to the same location, but you can demand an overwrite with:

    $ sudo make mediainstall

This can be helpful if you have damaged your OS/program media or simply
want to return to the pristine versions as distributed.

This will also overwrite the boot scripts in `$prefix/share/boot` with
fresh versions from the source distribution.


## Testing

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 [`README-test.md`][test] for more details.


## Using the Software

For the most part, this software distribution works like the upstream
[2015.12.15 distribution][usd].  Its [documentation][prj] therefore
describes this software too, for the most part.

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

1.  To start the simulator:

        $ sudo systemctl start pidp8i

    This will happen automatically on reboot unless you disable the
    service, such as in order to run one of the various [forks of Deeper
    Thought][dt2].

2.  To attach the terminal you're working on to the simulator:

        $ pidp8i

3.  To detach from the simulator's terminal interface while leaving the
    PiDP-8/I simulator running, type <kbd>Ctrl-A d</kbd>.  You can
    re-attach to it later with a `pidp8i` command.

4.  To shut the simulator down while attached to its terminal interface,
    type <kbd>Ctrl-E</kbd> to pause the simulator, then at the `simh>`
    prompt type `quit`.  Type `help` at that prompt to get some idea of
    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
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/
[upst]: http://obsolescence.wixsite.com/obsolescence/pidp-8
[smod]: http://obsolescence.wixsite.com/obsolescence/2016-pidp-8-building-instructions
[usd]:  http://obsolescence.wixsite.com/obsolescence/pidp-8-details
[dt2]:  https://github.com/VentureKing/Deeper-Thought-2
[sdoc]: http://simh.trailing-edge.com/pdf/simh_doc.pdf
[prj]:  http://obsolescence.wixsite.com/obsolescence/pidp-8
[test]: https://tangentsoft.com/pidp8i/doc/trunk/README-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
Added 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# PiDP-8/I Software Release Process

If you are just a user of this software, you need read no further. This
document is for those producing release versions of the software, or for
those curious about what goes into doing so.


## 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 [Raspbian Lite OS image][os].

1.  If the version of the base OS has changed since the last binary OS
    image was created, download the new one and blast it onto an SD card
    used for no other purpose. Boot it up.

2.  After logging in, retreive and initialize the BOSI process:

        $ wget https://tangentsoft.com/bosi
		$ chmod +x bosi
		$ ./bosi init
 
    It will either reboot the system after completing its tasks
    successfully or exit early, giving the reason it failed.

3.  Test that the software starts up as it should.

4.  Reset the OS configuration:

        $ exec sudo ./bosi reset

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

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

    `BLOCKS` is the value output at the end of the `shrink` step.

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:

        $ bosi finish[-nls]

8.  While the OS image uploads, compose the announcement message, and
    modify the front page to point to the new images.  Post the
    announcement message and new front page once the uploads complete.

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


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

Log into the SD card from which you made the regular image above, then
say `./bosi init --no-lamp-simulator`, and continue from step 3 above.

When you get down to the `image` and `test` steps, give `image-nls` and
`test-nls` instead.


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

### 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 SIMH-LICENSE.md.

























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Copyright © 2015-2017 by various authors

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.
Added auto.def.






















































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
########################################################################
# auto.def - Configure file for the PiDP-8/I software build system,
#            based on autosetup.
#
# Copyright © 2016-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.
########################################################################

define defaultprefix /opt/pidp8i

use cc
use cc-lib

options {
	debug-mode =>        "create a debug build (default is release)"
	no-lamp-simulator => "use simple LED driver instead of incandescent lamp simulator"
	serial-mod =>        "build simulator to expect the PCB serial mods"
	throttle:  =>        "override the throttle values in the boot scripts"
}

if {[opt-bool serial-mod]} {
	msg-result "The simulator will expect the serial mods to the Pi and PiDP-8/I PCBs."
	define PCB_SERIAL_MOD
}

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

# Swap the incandescent lamp simulator feature out for the original LED
# driving method on single-core hosts.  The user can force this on
# multi-core hosts via --no-lamp-simulator.
set cores [exec tools/corecount]
if {($cores < 2) || [opt-bool no-lamp-simulator]} {
	msg-result "Driving PiDP-8/I front panel LEDs using low-CPU-usage method."
	define LED_DRIVER_MODULE n
	define ILS_MODE 0
} else {
	msg-result "Driving PiDP-8/I front panel LEDs using incandescent lamp simulator."
	define LED_DRIVER_MODULE i
	define ILS_MODE 1
}

# Translate --throttle value to a SIMH command
set tv [opt-val throttle]
set tvsl [string length $tv]
if {($tvsl == 0 && $cores > 1) || $tv == "none"} {
	define SET_THROTTLE {set nothrottle}
	set tv "unlimited"
} else {
	# Rewrite symbolic values with values SIMH can understand.  See
	# README-throttle.md for the justification of these values.
	if {$tv == "single-core" || $tvsl == 0} {
		# value for ~75% CPU usage on a Pi Model B+ with the simple
        # LED driving code; conveniently, 4x the speed of a real PDP-8/I
		set tv "1332k"
	} elseif {$tv == "pdp8e"} {
		set tv "416k"
	} elseif {$tv == "pdp8i" || $tv == "pdp8a"} {
		set tv "333k"
	} elseif {$tv == "pdp8l" || $tv == "pdp8"} {
		set tv "313k"
	} elseif {$tv == "ha6120"} {
		set tv "182k"
	} elseif {$tv == "im6100a"} {
		set tv "200k"
	} elseif {$tv == "im6100"} {
		set tv "100k"
	} elseif {$tv == "im6100c"} {
		set tv "83k"
	} elseif {$tv == "pdp8s"} {
		set tv "63k"
	} elseif {$tv == "human"} {
		set tv "10/1000"
	} elseif {$tv == "trace"} {
		set tv "1/1000"
	}
	# else, assume --throttle was given a legal SIMH throttle value

	define SET_THROTTLE "set throttle $tv"
}
msg-result "Simulator CPU throttle set to $tv IPS"

# Compiler and header checks
cc-check-includes time.h
cc-check-functions clock_nanosleep nanosleep usleep
cc-check-functions sched_yield

# We need to find an install(1) type program that supports -D.  The
# Raspberry Pi OSes typically used with the PiDB-8/I board do have this,
# but this package also runs on non-Linux OSes (e.g. for testing on a
# desktop Mac) so make sure we've got a suitable implementation.  The
# ginstall name is typical on non-Linux systems where GNU Coreutils was
# installed alongside the core OS utilities.
if {[cc-check-progs ginstall]} {
	define INSTALL ginstall
} elseif {[cc-check-progs install]} {
	if {[catch {exec install -D -d . >& /dev/null} result] == 0} {
		define INSTALL install
	} else {
		user-error "install(1) does not support -D; install GNU Coreutils."
	}
} else {
	user-error "No install(1) type program found; install GNU Coreutils."
}
msg-result "Found GNU install(1) program as [get-define INSTALL]."

# Also find GNU readlink in the same way
if {[cc-check-progs greadlink]} {
	set rlprg greadlink
} elseif {[cc-check-progs readlink]} {
	if {[catch {exec readlink -f . >& /dev/null} result] == 0} {
		set rlprg readlink
	} else {
		user-error "readlink(1) does not support -D; install GNU Coreutils."
	}
} else {
	user-error "No readlink(1) type program found; install GNU Coreutils."
}
msg-result "Found GNU readlink(1) as $rlprg."

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

# Remember the name and primary group of the user who installed this, since
# we want to give that group write privileges to some files when they're
# installed, and we want them to own the screen(1) session.
set instgrp [exec id -grn]
set instusr [exec id -urn]
if {$instusr == "root"} {
	msg-result "Error: This software will not install and run as root."
	user-error "Reconfigure without sudo!"
}
define INSTGRP $instgrp
define INSTUSR $instusr
msg-result "Install group for user-writeable files will be $instgrp."
msg-result "Owner of screen(1) session will be $instusr."

# Can we use any nonstandard flags here?  We don't bother including
# flags that both GCC and Clang support.  The ones inside the "if"
# block are those that Clang will accept in an autosetup test but
# then will yell about if you try to use them.  The test checks for
# an -f sub-option that Clang doesn't currently support even enough
# to fool autosetup.
cc-check-standards c99
if {![opt-bool debug-mode]} {
	cc-check-flags -fipa-cp-clone
	cc-check-flags -fno-strict-overflow
	cc-check-flags -fpredictive-commoning
	if ([get-define HAVE_CFLAG_FIPA_CP_CLONE]) {
		cc-check-flags -fgcse-after-reload
		cc-check-flags -finline-functions
		cc-check-flags -fno-unsafe-loop-optimizations
		define-append CFLAGS "-D_GNU_SOURCE"
	}
}

# Embed this software's Fossil-based version string into gpio-common.c
define VERSION [exec "[get-define srcdir]/tools/version"]

# 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_SERIAL_MOD}
make-template Makefile.in
make-template bin/pidp8i.in
make-template boot/0.script.in
make-template boot/2.script.in
make-template boot/3.script.in
make-template boot/4.script.in
make-template boot/6.script.in
make-template boot/7.script.in
make-template etc/pidp8i-init.in
make-template etc/sudoers.in
make-template examples/Makefile.in
make-template src/Makefile.in
make-template src/gpio-common.c.in
make-template src/PDP8/Makefile.in
make-template src/PDP8/pidp8i.c.in
make-template src/scp.c.in
make-template tools/simh-update.in
exec chmod +x "[get-define builddir]/tools/simh-update"
Added autosetup/LICENSE.



































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Unless explicitly stated, all files which form part of autosetup
are released under the following license:

---------------------------------------------------------------------
autosetup - A build environment "autoconfigurator"

Copyright (c) 2010-2011, WorkWare Systems <http://workware.net.au/>

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
   copyright notice, this list of conditions and the following
   disclaimer in the documentation and/or other materials
   provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE WORKWARE SYSTEMS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WORKWARE
SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation
are those of the authors and should not be interpreted as representing
official policies, either expressed or implied, of WorkWare Systems.
Added autosetup/README.autosetup.

1
+
This is autosetup v0.6.6. See http://msteveb.github.com/autosetup/
Added autosetup/autosetup.

































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/sh
# Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# vim:se syntax=tcl:
# \
dir=`dirname "$0"`; exec "`$dir/find-tclsh`" "$0" "$@"

set autosetup(version) 0.6.6

# Can be set to 1 to debug early-init problems
set autosetup(debug) 0

##################################################################
#
# Main flow of control, option handling
#
proc main {argv} {
	global autosetup define

	# There are 3 potential directories involved:
	# 1. The directory containing autosetup (this script)
	# 2. The directory containing auto.def
	# 3. The current directory

	# From this we need to determine:
	# a. The path to this script (and related support files)
	# b. The path to auto.def
	# c. The build directory, where output files are created

	# This is also complicated by the fact that autosetup may
	# have been run via the configure wrapper ([getenv WRAPPER] is set)

	# Here are the rules.
	# a. This script is $::argv0
	#    => dir, prog, exe, libdir
	# b. auto.def is in the directory containing the configure wrapper,
	#    otherwise it is in the current directory.
	#    => srcdir, autodef
	# c. The build directory is the current directory
	#    => builddir, [pwd]

	# 'misc' is needed before we can do anything, so set a temporary libdir
	# in case this is the development version
	set autosetup(libdir) [file dirname $::argv0]/lib
	use misc

	# (a)
	set autosetup(dir) [realdir [file dirname [realpath $::argv0]]]
	set autosetup(prog) [file join $autosetup(dir) [file tail $::argv0]]
	set autosetup(exe) [getenv WRAPPER $autosetup(prog)]
	if {$autosetup(installed)} {
		set autosetup(libdir) $autosetup(dir)
	} else {
		set autosetup(libdir) [file join $autosetup(dir) lib]
	}
	autosetup_add_dep $autosetup(prog)

	# (b)
	if {[getenv WRAPPER ""] eq ""} {
		# Invoked directly
		set autosetup(srcdir) [pwd]
	} else {
		# Invoked via the configure wrapper
		set autosetup(srcdir) [file-normalize [file dirname $autosetup(exe)]]
	}
	set autosetup(autodef) [relative-path $autosetup(srcdir)/auto.def]

	# (c)
	set autosetup(builddir) [pwd]

	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
	set autosetup(optdefault) {}
	set autosetup(optionhelp) {}
	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"
		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"
		install:=.   => "install autosetup to the current or given directory (in the 'autosetup/' subdirectory)"
		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) [opt-val conf]
	}

	# 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
	foreach file [glob -nocomplain $autosetup(libdir)/*.auto $autosetup(libdir)/*/*.auto] {
		automf_load source $file
	}

	if {[opt-val help] ne ""} {
		incr autosetup(showhelp)
		use help
		autosetup_help [opt-val help]
	}

	if {[opt-val {manual ref reference}] ne ""} {
		use help
		autosetup_reference [opt-val {manual ref reference}]
	}

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

	if {[opt-val init] ne ""} {
		use init
		autosetup_init [opt-val init]
		incr earlyexit
	}

	if {$earlyexit} {
		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)"
	}

	# Parse extra arguments into autosetup(cmdline)
	foreach arg $argv {
		if {[regexp {([^=]*)=(.*)} $arg -> n v]} {
			dict set autosetup(cmdline) $n $v
			define $n $v
		} else {
			user-error "Unexpected parameter: $arg"
		}
	}

	autosetup_add_dep $autosetup(autodef)

	define CONFIGURE_OPTS ""
	foreach arg $autosetup(argv) {
		define-append CONFIGURE_OPTS [quote-if-needed $arg]
	}
	define AUTOREMAKE [file-normalize $autosetup(exe)]
	define-append AUTOREMAKE [get-define CONFIGURE_OPTS]


	# Log how we were invoked
	configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]"

	# Note that auto.def is *not* loaded in the global scope
	source $autosetup(autodef)

	# Could warn here if options {} was not specified

	show-notices

	if {$autosetup(debug)} {
		msg-result "Writing all defines to config.log"
		configlog "================ defines ======================"
		foreach n [lsort [array names define]] {
			configlog "define $n $define($n)"
		}
	}

	exit 0
}

# @opt-bool ?-nodefault? option ...
#
# Check each of the named, boolean options and if any have been explicitly enabled
# or disabled by the user, return 1 or 0 accordingly.
#
# If the option was specified more than once, the last value wins.
# e.g. With --enable-foo --disable-foo, [opt-bool foo] will return 0
#
# If no value was specified by the user, returns the default value for the
# first option. If -nodefault is given, this behaviour changes and
# -1 is returned instead.
#
proc opt-bool {args} {
	set nodefault 0
	if {[lindex $args 0] eq "-nodefault"} {
		set nodefault 1
		set args [lrange $args 1 end]
	}
	option-check-names {*}$args

	foreach opt $args {
		if {[dict exists $::autosetup(optset) $opt]} {
			return [dict get $::autosetup(optset) $opt]
		}
	}

	if {$nodefault} {
		return -1
	}
	# Default value is the default for the first option
	return [dict get $::autosetup(optdefault) [lindex $args 0]]
}

# @opt-val option-list ?default=""?
#
# Returns a list containing all the values given for the non-boolean options in 'option-list'.
# 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).
#
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
}

proc option-check-names {args} {
	foreach o $args {
		if {$o ni $::autosetup(options)} {
			autosetup-error "Request for undeclared option --$o"
		}
	}
}

# Parse the option definition in $opts and update
# ::autosetup(setoptions) and ::autosetup(optionhelp) appropriately
#
proc options-add {opts {header ""}} {
	global autosetup

	# First weed out comment lines
	set realopts {}
	foreach line [split $opts \n] {
		if {![string match "#*" [string trimleft $line]]} {
			append realopts $line \n
		}
	}
	set opts $realopts

	for {set i 0} {$i < [llength $opts]} {incr i} {
		set opt [lindex $opts $i]
		if {[string match =* $opt]} {
			# This is a special heading
			lappend autosetup(optionhelp) $opt ""
			set header {}
			continue
		}

		#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

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

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

			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 {$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
					}
					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]
			#string match \n* $desc
			if {$header ne ""} {
				lappend autosetup(optionhelp) $header ""
				set header ""
			}
			# A multi-line description
			lappend autosetup(optionhelp) $opthelp $desc
			incr i 2
		}
	}
}

# @module-options optionlist
#
# Like 'options', but used within a module.
proc module-options {opts} {
	set header ""
	if {$::autosetup(showhelp) > 1 && [llength $opts]} {
		set header "Module Options:"
	}
	options-add $opts $header

	if {$::autosetup(showhelp)} {
		# Ensure that the module isn't executed on --help
		# We are running under eval or source, so use break
		# to prevent further execution
		#return -code break -level 2
		return -code break
	}
}

proc max {a b} {
	expr {$a > $b ? $a : $b}
}

proc options-wrap-desc {text length firstprefix nextprefix initial} {
	set len $initial
	set space $firstprefix
	foreach word [split $text] {
		set word [string trim $word]
		if {$word == ""} {
			continue
		}
		if {$len && [string length $space$word] + $len >= $length} {
			puts ""
			set len 0
			set space $nextprefix
		}
		incr len [string length $space$word]
		puts -nonewline $space$word
		set space " "
	}
	if {$len} {
		puts ""
	}
}

proc options-show {} {
	# Determine the max option width
	set max 0
	foreach {opt desc} $::autosetup(optionhelp) {
		if {[string match =* $opt] || [string match \n* $desc]} {
			continue
		}
		set max [max $max [string length $opt]]
	}
	set indent [string repeat " " [expr $max+4]]
	set cols [getenv COLUMNS 80]
	catch {
		lassign [exec stty size] rows cols
	}
	incr cols -1
	# Now output
	foreach {opt desc} $::autosetup(optionhelp) {
		if {[string match =* $opt]} {
			puts [string range $opt 1 end]
			continue
		}
		puts -nonewline "  [format %-${max}s $opt]"
		if {[string match \n* $desc]} {
			puts $desc
		} else {
			options-wrap-desc [string trim $desc] $cols "  " $indent [expr $max + 2]
		}
	}
}

# @options options-spec
#
# Specifies configuration-time options which may be selected by the user
# and checked with opt-val and opt-bool. The format of options-spec 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.
#
# 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"
#
proc options {optlist} {
	# Allow options as a list or args
	options-add $optlist "Local Options:"

	if {$::autosetup(showhelp)} {
		options-show
		exit 0
	}

	# Check for invalid options
	if {[opt-bool option-checking]} {
		foreach o [dict keys $::autosetup(getopt)] {
			if {$o ni $::autosetup(options)} {
				user-error "Unknown option --$o"
			}
		}
	}
}

proc config_guess {} {
	if {[file-isexec $::autosetup(dir)/config.guess]} {
		exec-with-stderr sh $::autosetup(dir)/config.guess
		if {[catch {exec-with-stderr sh $::autosetup(dir)/config.guess} alias]} {
			user-error $alias
		}
		return $alias
	} else {
		configlog "No config.guess, so using uname"
		string tolower [exec uname -p]-unknown-[exec uname -s][exec uname -r]
	}
}

proc config_sub {alias} {
	if {[file-isexec $::autosetup(dir)/config.sub]} {
		if {[catch {exec-with-stderr sh $::autosetup(dir)/config.sub $alias} alias]} {
			user-error $alias
		}
	}
	return $alias
}

# @define name ?value=1?
#
# Defines the named variable to the given value.
# These (name, value) pairs represent the results of the configuration check
# and are available to be checked, modified and substituted.
#
proc define {name {value 1}} {
	set ::define($name) $value
	#dputs "$name <= $value"
}

# @undefine name
#
# Undefine the named variable
#
proc undefine {name} {
	unset -nocomplain ::define($name)
	#dputs "$name <= <undef>"
}

# @define-append name value ...
#
# Appends the given value(s) to the given 'defined' variable.
# 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
		foreach arg $args {
			if {[string first $arg $::define($name)] == -1} {
				append ::define($name) " " $arg
			}
		}
	} else {
		set ::define($name) [join $args]
	}
	#dputs "$name += [join $args] => $::define($name)"
}

# @get-define name ?default=0?
#
# Returns the current value of the 'defined' variable, or $default
# if not set.
#
proc get-define {name {default 0}} {
	if {[info exists ::define($name)]} {
		#dputs "$name => $::define($name)"
		return $::define($name)
	}
	#dputs "$name => $default"
	return $default
}

# @is-defined name
#
# Returns 1 if the given variable is defined.
#
proc is-defined {name} {
	info exists ::define($name)
}

# @all-defines
#
# Returns a dictionary (name value list) of all defined variables.
#
# This is suitable for use with 'dict', 'array set' or 'foreach'
# and allows for arbitrary processing of the defined variables.
#
proc all-defines {} {
	array get ::define
}


# @get-env name default
#
# If $name was specified on the command line, return it.
# If $name was set in the environment, return it.
# Otherwise return $default.
#
proc get-env {name default} {
	if {[dict exists $::autosetup(cmdline) $name]} {
		return [dict get $::autosetup(cmdline) $name]
	}
	getenv $name $default
}

# @env-is-set name
#
# Returns 1 if the $name was specified on the command line or in the environment.
# Note that an empty environment variable is not considered to be set.
#
proc env-is-set {name} {
	if {[dict exists $::autosetup(cmdline) $name]} {
		return 1
	}
	if {[getenv $name ""] ne ""} {
		return 1
	}
	return 0
}

# @readfile filename ?default=""?
#
# Return the contents of the file, without the trailing newline.
# If the file doesn't exist or can't be read, returns $default.
#
proc readfile {filename {default_value ""}} {
	set result $default_value
	catch {
		set f [open $filename]
		set result [read -nonewline $f]
		close $f
	}
	return $result
}

# @writefile filename value
#
# Creates the given file containing $value.
# Does not add an extra newline.
#
proc writefile {filename value} {
	set f [open $filename w]
	puts -nonewline $f $value
	close $f
}

proc quote-if-needed {str} {
	if {[string match {*[\" ]*} $str]} {
		return \"[string map [list \" \\" \\ \\\\] $str]\"
	}
	return $str
}

proc quote-argv {argv} {
	set args {}
	foreach arg $argv {
		lappend args [quote-if-needed $arg]
	}
	join $args
}

# @suffix suf list
#
# Takes a list and returns a new list with $suf appended
# to each element
#
## suffix .c {a b c} => {a.c b.c c.c}
#
proc suffix {suf list} {
	set result {}
	foreach p $list {
		lappend result $p$suf
	}
	return $result
}

# @prefix pre list
#
# Takes a list and returns a new list with $pre prepended
# to each element
#
## prefix jim- {a.c b.c} => {jim-a.c jim-b.c}
#
proc prefix {pre list} {
	set result {}
	foreach p $list {
		lappend result $pre$p
	}
	return $result
}

# @find-executable name
#
# Searches the path for an executable with the given name.
# Note that the name may include some parameters, e.g. "cc -mbig-endian",
# in which case the parameters are ignored.
# Returns 1 if found, or 0 if not.
#
proc find-executable {name} {
	# Ignore any parameters
	set name [lindex $name 0]
	if {$name eq ""} {
		# The empty string is never a valid executable
		return 0
	}
	foreach p [split-path] {
		dputs "Looking for $name in $p"
		set exec [file join $p $name]
		if {[file-isexec $exec]} {
			dputs "Found $name -> $exec"
			return 1
		}
	}
	return 0
}

# @find-an-executable ?-required? name ...
#
# Given a list of possible executable names,
# searches for one of these on the path.
#
# Returns the name found, or "" if none found.
# If the first parameter is '-required', an error is generated
# if no executable is found.
#
proc find-an-executable {args} {
	set required 0
	if {[lindex $args 0] eq "-required"} {
		set args [lrange $args 1 end]
		incr required
	}
	foreach name $args {
		if {[find-executable $name]} {
			return $name
		}
	}
	if {$required} {
		if {[llength $args] == 1} {
			user-error "failed to find: [join $args]"
		} else {
			user-error "failed to find one of: [join $args]"
		}
	}
	return ""
}

# @configlog msg
#
# Writes the given message to the configuration log, config.log
#
proc configlog {msg} {
	if {![info exists ::autosetup(logfh)]} {
		set ::autosetup(logfh) [open config.log w]
	}
	puts $::autosetup(logfh) $msg
}

# @msg-checking msg
#
# Writes the message with no newline to stdout.
#
proc msg-checking {msg} {
	if {$::autosetup(msg-quiet) == 0} {
		maybe-show-timestamp
		puts -nonewline $msg
		set ::autosetup(msg-checking) 1
	}
}

# @msg-result msg
#
# Writes the message to stdout.
#
proc msg-result {msg} {
	if {$::autosetup(msg-quiet) == 0} {
		maybe-show-timestamp
		puts $msg
		set ::autosetup(msg-checking) 0
		show-notices
	}
}

# @msg-quiet command ...
#
# msg-quiet evaluates it's arguments as a command with output
# from msg-checking and msg-result suppressed.
#
# This is useful if a check needs to run a subcheck which isn't
# of interest to the user.
proc msg-quiet {args} {
	incr ::autosetup(msg-quiet)
	set rc [uplevel 1 $args]
	incr ::autosetup(msg-quiet) -1
	return $rc
}

# Will be overridden by 'use misc'
proc error-stacktrace {msg} {
	return $msg
}

proc error-location {msg} {
	return $msg
}

##################################################################
#
# Debugging output
#
proc dputs {msg} {
	if {$::autosetup(debug)} {
		puts $msg
	}
}

##################################################################
#
# User and system warnings and errors
#
# Usage errors such as wrong command line options

# @user-error msg
#
# Indicate incorrect usage to the user, including if required components
# or features are not found.
# autosetup exits with a non-zero return code.
#
proc user-error {msg} {
	show-notices
	puts stderr "Error: $msg"
	puts stderr "Try: '[file tail $::autosetup(exe)] --help' for options"
	exit 1
}

# @user-notice msg
#
# Output the given message to stderr.
#
proc user-notice {msg} {
	lappend ::autosetup(notices) $msg
}

# Incorrect usage in the auto.def file. Identify the location.
proc autosetup-error {msg} {
	autosetup-full-error [error-location $msg]
}

# Like autosetup-error, except $msg is the full error message.
proc autosetup-full-error {msg} {
	show-notices
	puts stderr $msg
	exit 1
}

proc show-notices {} {
	if {$::autosetup(msg-checking)} {
		puts ""
		set ::autosetup(msg-checking) 0
	}
	flush stdout
	if {[info exists ::autosetup(notices)]} {
		puts stderr [join $::autosetup(notices) \n]
		unset ::autosetup(notices)
	}
}

proc maybe-show-timestamp {} {
	if {$::autosetup(msg-timing) && $::autosetup(msg-checking) == 0} {
		puts -nonewline [format {[%6.2f] } [expr {([clock millis] - $::autosetup(start)) % 10000 / 1000.0}]]
	}
}

proc autosetup_version {} {
	return "autosetup v$::autosetup(version)"
}

##################################################################
#
# Directory/path handling
#

proc realdir {dir} {
	set oldpwd [pwd]
	cd $dir
	set pwd [pwd]
	cd $oldpwd
	return $pwd
}

# Follow symlinks until we get to something which is not a symlink
proc realpath {path} {
	while {1} {
		if {[catch {
			set path [file readlink $path]
		}]} {
			# Not a link
			break
		}
	}
	return $path
}

# Convert absolute path, $path into a path relative
# to the given directory (or the current dir, if not given).
#
proc relative-path {path {pwd {}}} {
	set diff 0
	set same 0
	set newf {}
	set prefix {}
	set path [file-normalize $path]
	if {$pwd eq ""} {
		set pwd [pwd]
	} else {
		set pwd [file-normalize $pwd]
	}

	if {$path eq $pwd} {
		return .
	}

	# Try to make the filename relative to the current dir
	foreach p [split $pwd /] f [split $path /] {
		if {$p ne $f} {
			incr diff
		} elseif {!$diff} {
			incr same
		}
		if {$diff} {
			if {$p ne ""} {
				# Add .. for sibling or parent dir
				lappend prefix ..
			}
			if {$f ne ""} {
				lappend newf $f
			}
		}
	}
	if {$same == 1 || [llength $prefix] > 3} {
		return $path
	}

	file join [join $prefix /] [join $newf /]
}

# Add filename as a dependency to rerun autosetup
# The name will be normalised (converted to a full path)
#
proc autosetup_add_dep {filename} {
	lappend ::autosetup(deps) [file-normalize $filename]
}

##################################################################
#
# Library module support
#

# @use module ...
#
# Load the given library modules.
# e.g. 'use cc cc-shared'
#
# Note that module 'X' is implemented in either 'autosetup/X.tcl'
# or 'autosetup/X/init.tcl'
#
# The latter form is useful for a complex module which requires additional
# support file. In this form, '$::usedir' is set to the module directory
# when it is loaded.
#
proc use {args} {
	foreach m $args {
		if {[info exists ::libmodule($m)]} {
			continue
		}
		set ::libmodule($m) 1
		if {[info exists ::modsource($m)]} {
			automf_load eval $::modsource($m)
		} else {
			set sources [list $::autosetup(libdir)/${m}.tcl $::autosetup(libdir)/${m}/init.tcl]
			set found 0
			foreach source $sources {
				if {[file exists $source]} {
					incr found
					break
				}
			}
			if {$found} {
				# For the convenience of the "use" source, point to the directory
				# it is being loaded from
				set ::usedir [file dirname $source]
				automf_load source $source
				autosetup_add_dep $source
			} else {
				autosetup-error "use: No such module: $m"
			}
		}
	}
}

# Load module source in the global scope by executing the given command
proc automf_load {args} {
	if {[catch [list uplevel #0 $args] msg opts] ni {0 2 3}} {
		autosetup-full-error [error-dump $msg $opts $::autosetup(debug)]
	}
}

# Initial settings
set autosetup(exe) $::argv0
set autosetup(istcl) 1
set autosetup(start) [clock millis]
set autosetup(installed) 0
set autosetup(msg-checking) 0
set autosetup(msg-quiet) 0

# Embedded modules are inserted below here
set autosetup(installed) 1
# ----- module asciidoc-formatting -----

set modsource(asciidoc-formatting) {
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Module which provides text formatting
# asciidoc format

use formatting

proc para {text} {
    regsub -all "\[ \t\n\]+" [string trim $text] " "
}
proc title {text} {
    underline [para $text] =
    nl
}
proc p {text} {
    puts [para $text]
    nl
}
proc code {text} {
    foreach line [parse_code_block $text] {
        puts "    $line"
    }
    nl
}
proc codelines {lines} {
    foreach line $lines {
        puts "    $line"
    }
    nl
}
proc nl {} {
    puts ""
}
proc underline {text char} {
    regexp "^(\[ \t\]*)(.*)" $text -> indent words
    puts $text
    puts $indent[string repeat $char [string length $words]]
}
proc section {text} {
    underline "[para $text]" -
    nl
}
proc subsection {text} {
    underline "$text" ~
    nl
}
proc bullet {text} {
    puts "* [para $text]"
}
proc indent {text} {
    puts " :: "
    puts [para $text]
}
proc defn {first args} {
    set sep ""
    if {$first ne ""} {
        puts "${first}::"
    } else {
        puts " :: "
    }
    set defn [string trim [join $args \n]]
    regsub -all "\n\n" $defn "\n ::\n" defn
    puts $defn
}
}

# ----- module formatting -----

set modsource(formatting) {
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Module which provides common text formatting

# This is designed for documenation which looks like:
# code {...}
# or
# code {
#    ...
#    ...
# }
# In the second case, we need to work out the indenting
# and strip it from all lines but preserve the remaining indenting.
# Note that all lines need to be indented with the same initial
# spaces/tabs.
#
# Returns a list of lines with the indenting removed.
#
proc parse_code_block {text} {
    # If the text begins with newline, take the following text,
    # otherwise just return the original
    if {![regexp "^\n(.*)" $text -> text]} {
        return [list [string trim $text]]
    }

    # And trip spaces off the end
    set text [string trimright $text]

    set min 100
    # Examine each line to determine the minimum indent
    foreach line [split $text \n] {
        if {$line eq ""} {
            # Ignore empty lines for the indent calculation
            continue
        }
        regexp "^(\[ \t\]*)" $line -> indent
        set len [string length $indent]
        if {$len < $min} {
            set min $len
        }
    }

    # Now make a list of lines with this indent removed
    set lines {}
    foreach line [split $text \n] {
        lappend lines [string range $line $min end]
    }

    # Return the result
    return $lines
}
}

# ----- module getopt -----

set modsource(getopt) {
# Copyright (c) 2006 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Simple getopt module

# Parse everything out of the argv list which looks like an option
# Everything which doesn't look like an option, or is after --, is left unchanged
# Understands --enable-xxx and --with-xxx as synonyms for --xxx to enable the boolean option xxx.
# Understands --disable-xxx and --without-xxx to disable the boolean option xxx.
#
# The returned value is a dictionary keyed by option name
# Each value is a list of {type value} ... where type is "bool" or "str".
# The value for a boolean option is 0 or 1. The value of a string option is the value given.
proc getopt {argvname} {
	upvar $argvname argv
	set nargv {}

	set opts {}

	for {set i 0} {$i < [llength $argv]} {incr i} {
		set arg [lindex $argv $i]

		#dputs arg=$arg

		if {$arg eq "--"} {
			# End of options
			incr i
			lappend nargv {*}[lrange $argv $i end]
			break
		}

		if {[regexp {^--([^=][^=]+)=(.*)$} $arg -> name value]} {
			# --name=value
			dict lappend opts $name [list str $value]
		} elseif {[regexp {^--(enable-|disable-|with-|without-)?([^=]*)$} $arg -> prefix name]} {
			if {$prefix in {enable- with- ""}} {
				set value 1
			} else {
				set value 0
			}
			dict lappend opts $name [list bool $value]
		} else {
			lappend nargv $arg
		}
	}

	#puts "getopt: argv=[join $argv] => [join $nargv]"
	#array set getopt $opts
	#parray getopt

	set argv $nargv

	return $opts
}
}

# ----- module help -----

set modsource(help) {
# Copyright (c) 2010 WorkWare Systems http://workware.net.au/
# All rights reserved

# Module which provides usage, help and the command reference

proc autosetup_help {what} {
    use_pager

    puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n"
    puts "This is [autosetup_version], a build environment \"autoconfigurator\""
    puts "See the documentation online at http://msteveb.github.com/autosetup/\n"

    if {$what eq "local"} {
        if {[file exists $::autosetup(autodef)]} {
            # This relies on auto.def having a call to 'options'
            # which will display options and quit
            source $::autosetup(autodef)
        } else {
            options-show
        }
    } else {
        incr ::autosetup(showhelp)
        if {[catch {use $what}]} {
            user-error "Unknown module: $what"
        } else {
            options-show
        }
    }
    exit 0
}

# If not already paged and stdout is a tty, pipe the output through the pager
# This is done by reinvoking autosetup with --nopager added
proc use_pager {} {
    if {![opt-bool nopager] && [getenv PAGER ""] ne "" && [isatty? stdin] && [isatty? stdout]} {
        if {[catch {
            exec [info nameofexecutable] $::argv0 --nopager {*}$::argv |& {*}[getenv PAGER] >@stdout <@stdin 2>@stderr
        } msg opts] == 1} {
            if {[dict get $opts -errorcode] eq "NONE"} {
                # an internal/exec error
                puts stderr $msg
                exit 1
            }
        }
        exit 0
    }
}

# Outputs the autosetup references in one of several formats
proc autosetup_reference {{type text}} {

    use_pager

    switch -glob -- $type {
        wiki {use wiki-formatting}
        ascii* {use asciidoc-formatting}
        md - markdown {use markdown-formatting}
        default {use text-formatting}
    }

    title "[autosetup_version] -- Command Reference"

    section {Introduction}

    p {
        See http://msteveb.github.com/autosetup/ for the online documentation for 'autosetup'
    }

    p {
        'autosetup' provides a number of built-in commands which
        are documented below. These may be used from 'auto.def' to test
        for features, define variables, create files from templates and
        other similar actions.
    }

    automf_command_reference

    exit 0
}

proc autosetup_output_block {type lines} {
    if {[llength $lines]} {
        switch $type {
            code {
                codelines $lines
            }
            p {
                p [join $lines]
            }
            list {
                foreach line $lines {
                    bullet $line
                }
                nl
            }
        }
    }
}

# Generate a command reference from inline documentation
proc automf_command_reference {} {
    lappend files $::autosetup(prog)
    lappend files {*}[lsort [glob -nocomplain $::autosetup(libdir)/*.tcl]]

    section "Core Commands"
    set type p
    set lines {}
    set cmd {}

    foreach file $files {
        set f [open $file]
        while {![eof $f]} {
            set line [gets $f]

            # Find lines starting with "# @*" and continuing through the remaining comment lines
            if {![regexp {^# @(.*)} $line -> cmd]} {
                continue
            }

            # Synopsis or command?
            if {$cmd eq "synopsis:"} {
                section "Module: [file rootname [file tail $file]]"
            } else {
                subsection $cmd
            }

            set lines {}
            set type p

            # Now the description
            while {![eof $f]} {
                set line [gets $f]

                if {![regexp {^#(#)? ?(.*)} $line -> hash cmd]} {
                    break
                }
                if {$hash eq "#"} {
                    set t code
                } elseif {[regexp {^- (.*)} $cmd -> cmd]} {
                    set t list
                } else {
                    set t p
                }

                #puts "hash=$hash, oldhash=$oldhash, lines=[llength $lines], cmd=$cmd"

                if {$t ne $type || $cmd eq ""} {
                    # Finish the current block
                    autosetup_output_block $type $lines
                    set lines {}
                    set type $t
                }
                if {$cmd ne ""} {
                    lappend lines $cmd
                }
            }

            autosetup_output_block $type $lines
        }
        close $f
    }
}
}

# ----- module init -----

set modsource(init) {
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Module to help create auto.def and configure

proc autosetup_init {type} {
	set help 0
	if {$type in {? help}} {
		incr help
	} elseif {![dict exists $::autosetup(inittypes) $type]} {
		puts "Unknown type, --init=$type"
		incr help
	}
	if {$help} {
		puts "Use one of the following types (e.g. --init=make)\n"
		foreach type [lsort [dict keys $::autosetup(inittypes)]] {
			lassign [dict get $::autosetup(inittypes) $type] desc
			# XXX: Use the options-show code to wrap the description
			puts [format "%-10s %s" $type $desc]
		}
		return
	}
	lassign [dict get $::autosetup(inittypes) $type] desc script

	puts "Initialising $type: $desc\n"

	# All initialisations happens in the top level srcdir
	cd $::autosetup(srcdir)

	uplevel #0 $script
}

proc autosetup_add_init_type {type desc script} {
	dict set ::autosetup(inittypes) $type [list $desc $script]
}

# This is for in creating build-system init scripts
#
# If the file doesn't exist, create it containing $contents
# If the file does exist, only overwrite if --force is specified.
#
proc autosetup_check_create {filename contents} {
	if {[file exists $filename]} {
		if {!$::autosetup(force)} {
			puts "I see $filename already exists."
			return
		} else {
			puts "I will overwrite the existing $filename because you used --force."
		}
	} else {
		puts "I don't see $filename, so I will create it."
	}
	writefile $filename $contents
}
}

# ----- module install -----

set modsource(install) {
# Copyright (c) 2006-2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Module which can install autosetup

proc autosetup_install {dir} {
	if {[catch {
		cd $dir
		file mkdir autosetup

		set f [open autosetup/autosetup w]

		set publicmodules [glob $::autosetup(libdir)/*.auto]

		# First the main script, but only up until "CUT HERE"
		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:
			puts $f "set autosetup(installed) 1"
			foreach file [lsort [glob $::autosetup(libdir)/*.tcl]] {
				set buf [readfile $file]
				if {[string match "*\n# @synopsis:*" $buf]} {
					lappend publicmodules $file
					continue
				}
				set modname [file rootname [file tail $file]]
				puts $f "# ----- module $modname -----"
				puts $f "\nset modsource($modname) \{"
				puts $f $buf
				puts $f "\}\n"
			}
		}
		close $in
		close $f
		exec chmod 755 autosetup/autosetup

		# Install public modules
		foreach file $publicmodules {
			autosetup_install_file $file autosetup
		}

		# Install support files
		foreach file {config.guess config.sub jimsh0.c find-tclsh test-tclsh LICENSE} {
			autosetup_install_file $::autosetup(dir)/$file autosetup
		}
		exec chmod 755 autosetup/config.sub autosetup/config.guess autosetup/find-tclsh

		writefile autosetup/README.autosetup \
			"This is [autosetup_version]. See http://msteveb.github.com/autosetup/\n"

	} error]} {
		user-error "Failed to install autosetup: $error"
	}
	puts "Installed [autosetup_version] to autosetup/"

	# Now create 'configure' if necessary
	autosetup_create_configure
}

proc autosetup_create_configure {} {
	if {[file exists configure]} {
		if {!$::autosetup(force)} {
			# Could this be an autosetup configure?
			if {![string match "*\nWRAPPER=*" [readfile configure]]} {
				puts "I see configure, but not created by autosetup, so I won't overwrite it."
				puts "Remove it or use --force to overwrite."
				return
			}
		} else {
			puts "I will overwrite the existing configure because you used --force."
		}
	} else {
		puts "I don't see configure, so I will create it."
	}
	writefile configure \
{#!/bin/sh
dir="`dirname "$0"`/autosetup"
WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/autosetup" "$@"
}
	catch {exec chmod 755 configure}
}

# Append the contents of $file to filehandle $f
proc autosetup_install_append {f file} {
	set in [open $file]
	puts $f [read $in]
	close $in
}

proc autosetup_install_file {file dir} {
	if {![file exists $file]} {
		error "Missing installation file '$file'"
	}
	writefile [file join $dir [file tail $file]] [readfile $file]\n
}

if {$::autosetup(installed)} {
	user-error "autosetup can only be installed from development source, not from installed copy"
}
}

# ----- module markdown-formatting -----

set modsource(markdown-formatting) {
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Module which provides text formatting
# markdown format (kramdown syntax)

use formatting

proc para {text} {
    regsub -all "\[ \t\n\]+" [string trim $text] " " text
    regsub -all {([^a-zA-Z])'([^']*)'} $text {\1**`\2`**} text
    regsub -all {^'([^']*)'} $text {**`\1`**} text
    regsub -all {(http[^ \t\n]*)} $text {[\1](\1)} text
    return $text
}
proc title {text} {
    underline [para $text] =
    nl
}
proc p {text} {
    puts [para $text]
    nl
}
proc codelines {lines} {
    puts "~~~~~~~~~~~~"
    foreach line $lines {
        puts $line
    }
    puts "~~~~~~~~~~~~"
    nl
}
proc code {text} {
    puts "~~~~~~~~~~~~"
    foreach line [parse_code_block $text] {
        puts $line
    }
    puts "~~~~~~~~~~~~"
    nl
}
proc nl {} {
    puts ""
}
proc underline {text char} {
    regexp "^(\[ \t\]*)(.*)" $text -> indent words
    puts $text
    puts $indent[string repeat $char [string length $words]]
}
proc section {text} {
    underline "[para $text]" -
    nl
}
proc subsection {text} {
    puts "### `$text`"
    nl
}
proc bullet {text} {
    puts "* [para $text]"
}
proc defn {first args} {
    puts "^"
    set defn [string trim [join $args \n]]
    if {$first ne ""} {
        puts "**${first}**"
        puts -nonewline ": "
        regsub -all "\n\n" $defn "\n: " defn
    }
    puts "$defn"
}
}

# ----- module misc -----

set modsource(misc) {
# Copyright (c) 2007-2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Module containing misc procs useful to modules
# Largely for platform compatibility

set autosetup(istcl) [info exists ::tcl_library]
set autosetup(iswin) [string equal windows $tcl_platform(platform)]

if {$autosetup(iswin)} {
	# mingw/windows separates $PATH with semicolons
	# and doesn't have an executable bit
	proc split-path {} {
		split [getenv PATH .] {;}
	}
	proc file-isexec {exec} {
		# Basic test for windows. We ignore .bat
		if {[file isfile $exec] || [file isfile $exec.exe]} {
			return 1
		}
		return 0
	}
} else {
	# unix separates $PATH with colons and has and executable bit
	proc split-path {} {
		split [getenv PATH .] :
	}
	proc file-isexec {exec} {
		file executable $exec
	}
}

# Assume that exec can return stdout and stderr
proc exec-with-stderr {args} {
	exec {*}$args 2>@1
}

if {$autosetup(istcl)} {
	# Tcl doesn't have the env command
	proc getenv {name args} {
		if {[info exists ::env($name)]} {
			return $::env($name)
		}
		if {[llength $args]} {
			return [lindex $args 0]
		}
		return -code error "environment variable \"$name\" does not exist"
	}
	proc isatty? {channel} {
		dict exists [fconfigure $channel] -xchar
	}
} else {
	if {$autosetup(iswin)} {
		# On Windows, backslash convert all environment variables
		# (Assume that Tcl does this for us)
		proc getenv {name args} {
			string map {\\ /} [env $name {*}$args]
		}
	} else {
		# Jim on unix is simple
		alias getenv env
	}
	proc isatty? {channel} {
		set tty 0
		catch {
			# isatty is a recent addition to Jim Tcl
			set tty [$channel isatty]
		}
		return $tty
	}
}

# In case 'file normalize' doesn't exist
#
proc file-normalize {path} {
	if {[catch {file normalize $path} result]} {
		if {$path eq ""} {
			return ""
		}
		set oldpwd [pwd]
		if {[file isdir $path]} {
			cd $path
			set result [pwd]
		} else {
			cd [file dirname $path]
			set result [file join [pwd] [file tail $path]]
		}
		cd $oldpwd
	}
	return $result
}

# If everything is working properly, the only errors which occur
# should be generated in user code (e.g. auto.def).
# By default, we only want to show the error location in user code.
# We use [info frame] to achieve this, but it works differently on Tcl and Jim.
#
# This is designed to be called for incorrect usage in auto.def, via autosetup-error
#
proc error-location {msg} {
	if {$::autosetup(debug)} {
		return -code error $msg
	}
	# Search back through the stack trace for the first error in a .def file
	for {set i 1} {$i < [info level]} {incr i} {
		if {$::autosetup(istcl)} {
			array set info [info frame -$i]
		} else {
			lassign [info frame -$i] info(caller) info(file) info(line)
		}
		if {[string match *.def $info(file)]} {
			return "[relative-path $info(file)]:$info(line): Error: $msg"
		}
		#puts "Skipping $info(file):$info(line)"
	}
	return $msg
}

# If everything is working properly, the only errors which occur
# should be generated in user code (e.g. auto.def).
# By default, we only want to show the error location in user code.
# We use [info frame] to achieve this, but it works differently on Tcl and Jim.
#
# This is designed to be called for incorrect usage in auto.def, via autosetup-error
#
proc error-stacktrace {msg} {
	if {$::autosetup(debug)} {
		return -code error $msg
	}
	# Search back through the stack trace for the first error in a .def file
	for {set i 1} {$i < [info level]} {incr i} {
		if {$::autosetup(istcl)} {
			array set info [info frame -$i]
		} else {
			lassign [info frame -$i] info(caller) info(file) info(line)
		}
		if {[string match *.def $info(file)]} {
			return "[relative-path $info(file)]:$info(line): Error: $msg"
		}
		#puts "Skipping $info(file):$info(line)"
	}
	return $msg
}

# Given the return from [catch {...} msg opts], returns an appropriate
# error message. A nice one for Jim and a less-nice one for Tcl.
# If 'fulltrace' is set, a full stack trace is provided.
# Otherwise a simple message is provided.
#
# This is designed for developer errors, e.g. in module code or auto.def code
#
#
proc error-dump {msg opts fulltrace} {
	if {$::autosetup(istcl)} {
		if {$fulltrace} {
			return "Error: [dict get $opts -errorinfo]"
		} else {
			return "Error: $msg"
		}
	} else {
		lassign $opts(-errorinfo) p f l
		if {$f ne ""} {
			set result "$f:$l: Error: "
		}
		append result "$msg\n"
		if {$fulltrace} {
			append result [stackdump $opts(-errorinfo)]
		}

		# Remove the trailing newline
		string trim $result
	}
}
}

# ----- module text-formatting -----

set modsource(text-formatting) {
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Module which provides text formatting

use formatting

proc wordwrap {text length {firstprefix ""} {nextprefix ""}} {
    set len 0
    set space $firstprefix
    foreach word [split $text] {
        set word [string trim $word]
        if {$word == ""} {
            continue
        }
        if {$len && [string length $space$word] + $len >= $length} {
            puts ""
            set len 0
            set space $nextprefix
        }
        incr len [string length $space$word]

        # Use man-page conventions for highlighting 'quoted' and *quoted*
        # single words.
        # Use x^Hx for *bold* and _^Hx for 'underline'.
        #
        # less and more will both understand this.
        # Pipe through 'col -b' to remove them.
        if {[regexp {^'(.*)'([^a-zA-Z0-9_]*)$} $word -> bareword dot]} {
            regsub -all . $bareword "_\b&" word
            append word $dot
        } elseif {[regexp {^[*](.*)[*]([^a-zA-Z0-9_]*)$} $word -> bareword dot]} {
            regsub -all . $bareword "&\b&" word
            append word $dot
        }
        puts -nonewline $space$word
        set space " "
    }
    if {$len} {
        puts ""
    }
}
proc title {text} {
    underline [string trim $text] =
    nl
}
proc p {text} {
    wordwrap $text 80
    nl
}
proc codelines {lines} {
    foreach line $lines {
        puts "    $line"
    }
    nl
}
proc nl {} {
    puts ""
}
proc underline {text char} {
    regexp "^(\[ \t\]*)(.*)" $text -> indent words
    puts $text
    puts $indent[string repeat $char [string length $words]]
}
proc section {text} {
    underline "[string trim $text]" -
    nl
}
proc subsection {text} {
    underline "$text" ~
    nl
}
proc bullet {text} {
    wordwrap $text 76 "  * " "    "
}
proc indent {text} {
    wordwrap $text 76 "    " "    "
}
proc defn {first args} {
    if {$first ne ""} {
        underline "    $first" ~
    }
    foreach p $args {
        if {$p ne ""} {
            indent $p
        }
    }
}
}

# ----- module wiki-formatting -----

set modsource(wiki-formatting) {
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Module which provides text formatting
# wiki.tcl.tk format output

use formatting

proc joinlines {text} {
    set lines {}
    foreach l [split [string trim $text] \n] {
        lappend lines [string trim $l]
    }
    join $lines
}
proc p {text} {
    puts [joinlines $text]
    puts ""
}
proc title {text} {
    puts "*** [joinlines $text] ***"
    puts ""
}
proc codelines {lines} {
    puts "======"
    foreach line $lines {
        puts "    $line"
    }
    puts "======"
}
proc code {text} {
    puts "======"
    foreach line [parse_code_block $text] {
        puts "    $line"
    }
    puts "======"
}
proc nl {} {
}
proc section {text} {
    puts "'''$text'''"
    puts ""
}
proc subsection {text} {
    puts "''$text''"
    puts ""
}
proc bullet {text} {
    puts "   * [joinlines $text]"
}
proc indent {text} {
    puts "    :    [joinlines $text]"
}
proc defn {first args} {
    if {$first ne ""} {
        indent '''$first'''
    }

    foreach p $args {
        p $p
    }
}
}


##################################################################
#
# Entry/Exit
#
if {$autosetup(debug)} {
	main $argv
}
if {[catch {main $argv} msg opts] == 1} {
	show-notices
	autosetup-full-error [error-dump $msg $opts $::autosetup(debug)]
	if {!$autosetup(debug)} {
		puts stderr "Try: '[file tail $autosetup(exe)] --debug' for a full stack trace"
	}
	exit 1
}
Added autosetup/cc-db.tcl.















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# @synopsis:
#
# The 'cc-db' module provides a knowledge based of system idiosyncrasies
# In general, this module can always be included

use cc

module-options {}

# openbsd needs sys/types.h to detect some system headers
cc-include-needs sys/socket.h sys/types.h
cc-include-needs netinet/in.h sys/types.h
Added autosetup/cc-lib.tcl.































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# @synopsis:
#
# Provides a library of common tests on top of the 'cc' module.

use cc

module-options {}

# @cc-check-lfs
#
# The equivalent of the AC_SYS_LARGEFILE macro
# 
# defines 'HAVE_LFS' if LFS is available,
# and defines '_FILE_OFFSET_BITS=64' if necessary
#
# Returns 1 if 'LFS' is available or 0 otherwise
#
proc cc-check-lfs {} {
	cc-check-includes sys/types.h
	msg-checking "Checking if -D_FILE_OFFSET_BITS=64 is needed..."
	set lfs 1
	if {[msg-quiet cc-with {-includes sys/types.h} {cc-check-sizeof off_t}] == 8} {
		msg-result no
	} elseif {[msg-quiet cc-with {-includes sys/types.h -cflags -D_FILE_OFFSET_BITS=64} {cc-check-sizeof off_t}] == 8} {
		define _FILE_OFFSET_BITS 64
		msg-result yes
	} else {
		set lfs 0
		msg-result none
	}
	define-feature lfs $lfs
	return $lfs
}

# @cc-check-endian
#
# The equivalent of the AC_C_BIGENDIAN macro
# 
# defines 'HAVE_BIG_ENDIAN' if endian is known to be big,
# or 'HAVE_LITTLE_ENDIAN' if endian is known to be little.
#
# Returns 1 if determined, or 0 if not.
#
proc cc-check-endian {} {
	cc-check-includes sys/types.h sys/param.h
	set rc 0
	msg-checking "Checking endian..."
	cc-with {-includes {sys/types.h sys/param.h}} {
		if {[cctest -code {
			#if !defined(BIG_ENDIAN) || !defined(BYTE_ORDER)
				#error unknown
			#elif BYTE_ORDER != BIG_ENDIAN
				#error little
			#endif
		}]} {
			define-feature big-endian
			msg-result "big"
			set rc 1
		} elseif {[cctest -code {
			#if !defined(LITTLE_ENDIAN) || !defined(BYTE_ORDER)
				#error unknown
			#elif BYTE_ORDER != LITTLE_ENDIAN
				#error big
			#endif
		}]} {
			define-feature little-endian
			msg-result "little"
			set rc 1
		} else {
			msg-result "unknown"
		}
	}
	return $rc
}

# @cc-check-flags flag ?...?
#
# Checks whether the given C/C++ compiler flags can be used. Defines feature
# names prefixed with 'HAVE_CFLAG' and 'HAVE_CXXFLAG' respectively, and
# appends working flags to '-cflags' and 'CFLAGS' or 'CXXFLAGS'.
proc cc-check-flags {args} {
    set result 1
    array set opts [cc-get-settings]
    switch -exact -- $opts(-lang) {
        c++ {
            set lang C++
            set prefix CXXFLAG
        }
        c {
            set lang C
            set prefix CFLAG
        }
        default {
            autosetup-error "cc-check-flags failed with unknown language: $opts(-lang)"
        }
    }
    foreach flag $args {
        msg-checking "Checking whether the $lang compiler accepts $flag..."
        if {[cctest -cflags $flag]} {
            msg-result yes
            define-feature $prefix$flag
            cc-with [list -cflags [list $flag]]
            define-append ${prefix}S $flag
        } else {
            msg-result no
            set result 0
        }
    }
    return $result
}

# @cc-check-standards ver ?...?
#
# Checks whether the C/C++ compiler accepts one of the specified '-std=$ver'
# options, and appends the first working one to '-cflags' and 'CFLAGS' or
# 'CXXFLAGS'.
proc cc-check-standards {args} {
    array set opts [cc-get-settings]
    foreach std $args {
        if {[cc-check-flags -std=$std]} {
            return $std
        }
    }
    return ""
}

# Checks whether $keyword is usable as alignof
proc cctest_alignof {keyword} {
    msg-checking "Checking for $keyword..."
    if {[cctest -code [subst -nobackslashes {
        printf("minimum alignment is %d == %d\n", ${keyword}(char), ${keyword}('x'));
    }]]} then {
        msg-result ok
        define-feature $keyword
    } else {
        msg-result "not found"
    }
}

# @cc-check-c11
#
# Checks for several C11/C++11 extensions and their alternatives. Currently
# checks for '_Static_assert', '_Alignof', '__alignof__', '__alignof'.
proc cc-check-c11 {} {
    msg-checking "Checking for _Static_assert..."
    if {[cctest -code {
        _Static_assert(1, "static assertions are available");
    }]} then {
        msg-result ok
        define-feature _Static_assert
    } else {
        msg-result "not found"
    }

    cctest_alignof _Alignof
    cctest_alignof __alignof__
    cctest_alignof __alignof
}

# @cc-check-alloca
#
# The equivalent of the AC_FUNC_ALLOCA macro
#
# Checks for the existence of alloca
# defines HAVE_ALLOCA and returns 1 if it exists
proc cc-check-alloca {} {
    cc-check-some-feature alloca {
        cctest -includes alloca.h -code { alloca (2 * sizeof (int)); }
    }
}

# @cc-signal-return-type
#
# The equivalent of the AC_TYPE_SIGNAL macro
#
# defines RETSIGTYPE to int or void
proc cc-signal-return-type {} {
    msg-checking "Checking return type of signal handlers..."
    cc-with {-includes {sys/types.h signal.h}} {
        if {[cctest -code {return *(signal (0, 0)) (0) == 1;}]} {
                set type int
        } else {
                set type void
        }
        define RETSIGTYPE $type
        msg-result $type
    }
}
Added autosetup/cc-shared.tcl.





















































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# @synopsis:
#
# The 'cc-shared' module provides support for shared libraries and shared objects.
# It defines the following variables:
#
## SH_CFLAGS         Flags to use compiling sources destined for a shared library
## SH_LDFLAGS        Flags to use linking (creating) a shared library
## SH_SOPREFIX       Prefix to use to set the soname when creating a shared library
## SH_SOEXT          Extension for shared libs
## SH_SOEXTVER       Format for versioned shared libs - %s = version
## SHOBJ_CFLAGS      Flags to use compiling sources destined for a shared object
## SHOBJ_LDFLAGS     Flags to use linking a shared object, undefined symbols allowed
## SHOBJ_LDFLAGS_R   - as above, but all symbols must be resolved
## SH_LINKFLAGS      Flags to use linking an executable which will load shared objects
## LD_LIBRARY_PATH   Environment variable which specifies path to shared libraries
## STRIPLIBFLAGS     Arguments to strip a dynamic library

module-options {}

# Defaults: gcc on unix
define SHOBJ_CFLAGS -fpic
define SHOBJ_LDFLAGS -shared
define SH_CFLAGS -fpic
define SH_LDFLAGS -shared
define SH_LINKFLAGS -rdynamic
define SH_SOEXT .so
define SH_SOEXTVER .so.%s
define SH_SOPREFIX -Wl,-soname,
define LD_LIBRARY_PATH LD_LIBRARY_PATH
define STRIPLIBFLAGS --strip-unneeded

# Note: This is a helpful reference for identifying the toolchain
#       http://sourceforge.net/apps/mediawiki/predef/index.php?title=Compilers

switch -glob -- [get-define host] {
	*-*-darwin* {
		define SHOBJ_CFLAGS "-dynamic -fno-common"
		define SHOBJ_LDFLAGS "-bundle -undefined dynamic_lookup"
		define SHOBJ_LDFLAGS_R -bundle
		define SH_CFLAGS -dynamic
		define SH_LDFLAGS -dynamiclib
		define SH_LINKFLAGS ""
		define SH_SOEXT .dylib
		define SH_SOEXTVER .%s.dylib
		define SH_SOPREFIX -Wl,-install_name,
		define LD_LIBRARY_PATH DYLD_LIBRARY_PATH
		define STRIPLIBFLAGS -x
	}
	*-*-ming* - *-*-cygwin - *-*-msys {
		define SHOBJ_CFLAGS ""
		define SHOBJ_LDFLAGS -shared
		define SH_CFLAGS ""
		define SH_LDFLAGS -shared
		define SH_LINKFLAGS ""
		define SH_SOEXT .dll
		define SH_SOEXTVER .dll
		define SH_SOPREFIX ""
		define LD_LIBRARY_PATH PATH
	}
	sparc* {
		if {[msg-quiet cc-check-decls __SUNPRO_C]} {
			msg-result "Found sun stdio compiler"
			# sun stdio compiler
			# XXX: These haven't been fully tested. 
			define SHOBJ_CFLAGS -KPIC
			define SHOBJ_LDFLAGS "-G"
			define SH_CFLAGS -KPIC
			define SH_LINKFLAGS -Wl,-export-dynamic
			define SH_SOPREFIX -Wl,-h,
		} else {
			# sparc has a very small GOT table limit, so use -fPIC
			define SH_CFLAGS -fPIC
			define SHOBJ_CFLAGS -fPIC
		}
	}
	*-*-solaris* {
		if {[msg-quiet cc-check-decls __SUNPRO_C]} {
			msg-result "Found sun stdio compiler"
			# sun stdio compiler
			# XXX: These haven't been fully tested. 
			define SHOBJ_CFLAGS -KPIC
			define SHOBJ_LDFLAGS "-G"
			define SH_CFLAGS -KPIC
			define SH_LINKFLAGS -Wl,-export-dynamic
			define SH_SOPREFIX -Wl,-h,
		}
	}
	*-*-hpux {
		# XXX: These haven't been tested
		define SHOBJ_CFLAGS "+O3 +z"
		define SHOBJ_LDFLAGS -b
		define SH_CFLAGS +z
		define SH_LINKFLAGS -Wl,+s
		define LD_LIBRARY_PATH SHLIB_PATH
	}
	*-*-haiku {
		define SHOBJ_CFLAGS ""
		define SHOBJ_LDFLAGS -shared
		define SH_CFLAGS ""
		define SH_LDFLAGS -shared
		define SH_LINKFLAGS ""
		define SH_SOPREFIX ""
		define LD_LIBRARY_PATH LIBRARY_PATH
	}
	microblaze* {
		# Microblaze generally needs -fPIC rather than -fpic
		define SHOBJ_CFLAGS -fPIC
		define SH_CFLAGS -fPIC
	}
}

if {![is-defined SHOBJ_LDFLAGS_R]} {
	define SHOBJ_LDFLAGS_R [get-define SHOBJ_LDFLAGS]
}
Added autosetup/cc.tcl.

































































































































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# @synopsis:
#
# The 'cc' module supports checking various 'features' of the C or C++
# compiler/linker environment. Common commands are cc-check-includes,
# cc-check-types, cc-check-functions, cc-with, make-autoconf-h and make-template.
#
# The following environment variables are used if set:
#
## CC       - C compiler
## CXX      - C++ compiler
## CCACHE   - Set to "none" to disable automatic use of ccache
## CFLAGS   - Additional C compiler flags
## CXXFLAGS - Additional C++ compiler flags
## LDFLAGS  - Additional compiler flags during linking
## LIBS     - Additional libraries to use (for all tests)
## CROSS    - Tool prefix for cross compilation
#
# The following variables are defined from the corresponding
# environment variables if set.
#
## CPPFLAGS
## LINKFLAGS
## 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
proc cctest_type {type} {
	cctest -code "$type _x;"
}

# Checks for the existence of the given type/structure member.
# e.g. "struct stat.st_mtime"
proc cctest_member {struct_member} {
	# split at the first dot
	regexp {^([^.]+)[.](.*)$} $struct_member -> struct member
	cctest -code "static $struct _s; return sizeof(_s.$member);"
}

# Checks for the existence of the given define by compiling
#
proc cctest_define {name} {
	cctest -code "#ifndef $name\n#error not defined\n#endif"
}

# Checks for the existence of the given name either as
# a macro (#define) or an rvalue (such as an enum)
#
proc cctest_decl {name} {
	cctest -code "#ifndef $name\n(void)$name;\n#endif"
}

# @cc-check-sizeof type ...
#
# Checks the size of the given types (between 1 and 32, inclusive).
# Defines a variable with the size determined, or "unknown" otherwise.
# e.g. for type 'long long', defines SIZEOF_LONG_LONG.
# Returns the size of the last type.
#
proc cc-check-sizeof {args} {
	foreach type $args {
		msg-checking "Checking for sizeof $type..."
		set size unknown
		# Try the most common sizes first
		foreach i {4 8 1 2 16 32} {
			if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} {
				set size $i
				break
			}
		}
		msg-result $size
		set define [feature-define-name $type SIZEOF_]
		define $define $size
	}
	# Return the last result
	get-define $define
}

# Checks for each feature in $list by using the given script.
#
# When the script is evaluated, $each is set to the feature
# being checked, and $extra is set to any additional cctest args.
#
# Returns 1 if all features were found, or 0 otherwise.
proc cc-check-some-feature {list script} {
	set ret 1
	foreach each $list {
		if {![check-feature $each $script]} {
			set ret 0
		}
	}
	return $ret
}

# @cc-check-includes includes ...
#
# Checks that the given include files can be used
proc cc-check-includes {args} {
	cc-check-some-feature $args {
		set with {}
		if {[dict exists $::autosetup(cc-include-deps) $each]} {
			set deps [dict keys [dict get $::autosetup(cc-include-deps) $each]]
			msg-quiet cc-check-includes {*}$deps
			foreach i $deps {
				if {[have-feature $i]} {
					lappend with $i
				}
			}
		}
		if {[llength $with]} {
			cc-with [list -includes $with] {
				cctest -includes $each
			}
		} else {
			cctest -includes $each
		}
	}
}

# @cc-include-needs include required ...
#
# Ensures that when checking for 'include', a check is first
# made for each 'required' file, and if found, it is #included
proc cc-include-needs {file args} {
	foreach depfile $args {
		dict set ::autosetup(cc-include-deps) $file $depfile 1
	}
}

# @cc-check-types type ...
#
# Checks that the types exist.
proc cc-check-types {args} {
	cc-check-some-feature $args {
		cctest_type $each
	}
}

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

# @cc-check-decls name ...
#
# Checks that each given name is either a preprocessor symbol or rvalue
# such as an enum. Note that the define used is HAVE_DECL_xxx
# rather than HAVE_xxx
proc cc-check-decls {args} {
	set ret 1
	foreach name $args {
		msg-checking "Checking for $name..."
		set r [cctest_decl $name]
		define-feature "decl $name" $r
		if {$r} {
			msg-result "ok"
		} else {
			msg-result "not found"
			set ret 0
		}
	}
	return $ret
}

# @cc-check-functions function ...
#
# Checks that the given functions exist (can be linked)
proc cc-check-functions {args} {
	cc-check-some-feature $args {
		cctest_function $each
	}
}

# @cc-check-members type.member ...
#
# Checks that the given type/structure members exist.
# A structure member is of the form "struct stat.st_mtime"
proc cc-check-members {args} {
	cc-check-some-feature $args {
		cctest_member $each
	}
}

# @cc-check-function-in-lib function libs ?otherlibs?
#
# Checks that the given function can be found in one of the libs.
#
# First checks for no library required, then checks each of the libraries
# in turn.
#
# If the function is found, the feature is defined and lib_$function is defined
# to -l$lib where the function was found, or "" if no library required.
# In addition, -l$lib is prepended to the LIBS define.
#
# If additional libraries may be needed for linking, they should be specified
# as $extralibs as "-lotherlib1 -lotherlib2".
# These libraries are not automatically added to LIBS.
#
# Returns 1 if found or 0 if not.
# 
proc cc-check-function-in-lib {function libs {otherlibs {}}} {
	msg-checking "Checking libs for $function..."
	set found 0
	cc-with [list -libs $otherlibs] {
		if {[cctest_function $function]} {
			msg-result "none needed"
			define lib_$function ""
			incr found
		} else {
			foreach lib $libs {
				cc-with [list -libs -l$lib] {
					if {[cctest_function $function]} {
						msg-result -l$lib
						define lib_$function -l$lib
						# prepend to LIBS
						define LIBS "-l$lib [get-define LIBS]"
						incr found
						break
					}
				}
			}
		}
	}
	if {$found} {
		define [feature-define-name $function]
	} else {
		msg-result "no"
	}
	return $found
}

# @cc-check-tools tool ...
#
# Checks for existence of the given compiler tools, taking
# into account any cross compilation prefix.
#
# For example, when checking for "ar", first AR is checked on the command
# line and then in the environment. If not found, "${host}-ar" or
# simply "ar" is assumed depending upon whether cross compiling.
# The path is searched for this executable, and if found AR is defined
# to the executable name.
# Note that even when cross compiling, the simple "ar" is used as a fallback,
# but a warning is generated. This is necessary for some toolchains.
#
# It is an error if the executable is not found.
#
proc cc-check-tools {args} {
	foreach tool $args {
		set TOOL [string toupper $tool]
		set exe [get-env $TOOL [get-define cross]$tool]
		if {[find-executable {*}$exe]} {
			define $TOOL $exe
			continue
		}
		if {[find-executable {*}$tool]} {
			msg-result "Warning: Failed to find $exe, falling back to $tool which may be incorrect"
			define $TOOL $tool
			continue
		}
		user-error "Failed to find $exe"
	}
}

# @cc-check-progs prog ...
#
# Checks for existence of the given executables on the path.
#
# For example, when checking for "grep", the path is searched for
# the executable, 'grep', and if found GREP is defined as "grep".
#
# If the executable is not found, the variable is defined as false.
# Returns 1 if all programs were found, or 0 otherwise.
#
proc cc-check-progs {args} {
	set failed 0
	foreach prog $args {
		set PROG [string toupper $prog]
		msg-checking "Checking for $prog..."
		if {![find-executable $prog]} {
			msg-result no
			define $PROG false
			incr failed
		} else {
			msg-result ok
			define $PROG $prog
		}
	}
	expr {!$failed}
}

# Adds the given settings to $::autosetup(ccsettings) and
# returns the old settings.
#
proc cc-add-settings {settings} {
	if {[llength $settings] % 2} {
		autosetup-error "settings list is missing a value: $settings"
	}

	set prev [cc-get-settings]
	# workaround a bug in some versions of jimsh by forcing
	# conversion of $prev to a list
	llength $prev

	array set new $prev

	foreach {name value} $settings {
		switch -exact -- $name {
			-cflags - -includes {
				# These are given as lists
				lappend new($name) {*}$value
			}
			-declare {
				lappend new($name) $value
			}
			-libs {
				# Note that new libraries are added before previous libraries
				set new($name) [list {*}$value {*}$new($name)]
			}
			-link - -lang - -nooutput {
				set new($name) $value
			}
			-source - -sourcefile - -code {
				# XXX: These probably are only valid directly from cctest
				set new($name) $value
			}
			default {
				autosetup-error "unknown cctest setting: $name"
			}
		}
	}

	cc-store-settings [array get new]

	return $prev
}

proc cc-store-settings {new} {
	set ::autosetup(ccsettings) $new
}

proc cc-get-settings {} {
	return $::autosetup(ccsettings)
}

# Similar to cc-add-settings, but each given setting
# simply replaces the existing value.
#
# Returns the previous settings
proc cc-update-settings {args} {
	set prev [cc-get-settings]
	cc-store-settings [dict merge $prev $args]
	return $prev
}

# @cc-with settings ?{ script }?
#
# Sets the given 'cctest' settings and then runs the tests in 'script'.
# Note that settings such as -lang replace the current setting, while
# those such as -includes are appended to the existing setting.
#
# If no script is given, the settings become the default for the remainder
# of the auto.def file.
#
## cc-with {-lang c++} {
##   # This will check with the C++ compiler
##   cc-check-types bool
##   cc-with {-includes signal.h} {
##     # This will check with the C++ compiler, signal.h and any existing includes.
##     ...
##   }
##   # back to just the C++ compiler
## }
#
# The -libs setting is special in that newer values are added *before* earlier ones.
#
## cc-with {-libs {-lc -lm}} {
##   cc-with {-libs -ldl} {
##     cctest -libs -lsocket ...
##     # libs will be in this order: -lsocket -ldl -lc -lm
##   }
## }
proc cc-with {settings args} {
	if {[llength $args] == 0} {
		cc-add-settings $settings
	} elseif {[llength $args] > 1} {
		autosetup-error "usage: cc-with settings ?script?"
	} else {
		set save [cc-add-settings $settings]
		set rc [catch {uplevel 1 [lindex $args 0]} result info]
		cc-store-settings $save
		if {$rc != 0} {
			return -code [dict get $info -code] $result
		}
		return $result
	}
}

# @cctest ?settings?
# 
# Low level C compiler checker. Compiles and or links a small C program
# according to the arguments and returns 1 if OK, or 0 if not.
#
# Supported settings are:
#
## -cflags cflags      A list of flags to pass to the compiler
## -includes list      A list of includes, e.g. {stdlib.h stdio.h}
## -declare code       Code to declare before main()
## -link 1             Don't just compile, link too
## -lang c|c++         Use the C (default) or C++ compiler
## -libs liblist       List of libraries to link, e.g. {-ldl -lm}
## -code code          Code to compile in the body of main()
## -source code        Compile a complete program. Ignore -includes, -declare and -code
## -sourcefile file    Shorthand for -source [readfile [get-define srcdir]/$file]
## -nooutput 1         Treat any compiler output (e.g. a warning) as an error
#
# Unless -source or -sourcefile is specified, the C program looks like:
#
## #include <firstinclude>   /* same for remaining includes in the list */
##
## declare-code              /* any code in -declare, verbatim */
##
## int main(void) {
##   code                    /* any code in -code, verbatim */
##   return 0;
## }
#
# Any failures are recorded in 'config.log'
#
proc cctest {args} {
	set src conftest__.c
	set tmp conftest__

	# Easiest way to merge in the settings
	cc-with $args {
		array set opts [cc-get-settings]
	}

	if {[info exists opts(-sourcefile)]} {
		set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"]
	}
	if {[info exists opts(-source)]} {
		set lines $opts(-source)
	} else {
		foreach i $opts(-includes) {
			if {$opts(-code) ne "" && ![feature-checked $i]} {
				# Compiling real code with an unchecked header file
				# Quickly (and silently) check for it now

				# Remove all -includes from settings before checking
				set saveopts [cc-update-settings -includes {}]
				msg-quiet cc-check-includes $i
				cc-store-settings $saveopts
			}
			if {$opts(-code) eq "" || [have-feature $i]} {
				lappend source "#include <$i>"
			}
		}
		lappend source {*}$opts(-declare)
		lappend source "int main(void) {"
		lappend source $opts(-code)
		lappend source "return 0;"
		lappend source "}"

		set lines [join $source \n]
	}

	# Build the command line
	set cmdline {}
	lappend cmdline {*}[get-define CCACHE]
	switch -exact -- $opts(-lang) {
		c++ {
			lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS]
		}
		c {
			lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS]
		}
		default {
			autosetup-error "cctest called with unknown language: $opts(-lang)"
		}
	}

	if {$opts(-link)} {
		lappend cmdline {*}[get-define LDFLAGS]
	} else {
		set tmp conftest__.o
		lappend cmdline -c
	}
	lappend cmdline {*}$opts(-cflags) {*}[get-define cc-default-debug ""]
	lappend cmdline $src -o $tmp {*}$opts(-libs)
	if {$opts(-link)} {
		lappend cmdline {*}[get-define LIBS]
	}

	# At this point we have the complete command line and the
	# complete source to be compiled. Get the result from cache if
	# we can
	if {[info exists ::cc_cache($cmdline,$lines)]} {
		msg-checking "(cached) "
		set ok $::cc_cache($cmdline,$lines)
		if {$::autosetup(debug)} {
			configlog "From cache (ok=$ok): [join $cmdline]"
			configlog "============"
			configlog $lines
			configlog "============"
		}
		return $ok
	}

	writefile $src $lines\n

	set ok 1
	set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
	if {$err || ($opts(-nooutput) && [string length $result])} {
		configlog "Failed: [join $cmdline]"
		configlog $result
		configlog "============"
		configlog "The failed code was:"
		configlog $lines
		configlog "============"
		set ok 0
	} elseif {$::autosetup(debug)} {
		configlog "Compiled OK: [join $cmdline]"
		configlog "============"
		configlog $lines
		configlog "============"
	}
	file delete $src
	file delete $tmp

	# cache it
	set ::cc_cache($cmdline,$lines) $ok

	return $ok
}

# @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*?
#
# Deprecated - see make-config-header
proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} {
	user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead"
	make-config-header $file -auto $autopatterns -bare $barepatterns
}

# @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ...
#
# Examines all defined variables which match the given patterns
# and writes an include file, $file, which defines each of these.
# Variables which match '-auto' are output as follows:
# - defines which have the value "0" are ignored.
# - defines which have integer values are defined as the integer value.
# - any other value is defined as a string, e.g. "value"
# Variables which match '-bare' are defined as-is.
# Variables which match '-str' are defined as a string, e.g. "value"
# Variables which match '-none' are omitted.
#
# Note that order is important. The first pattern which matches is selected
# Default behaviour is:
#
#  -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none *
#
# If the file would be unchanged, it is not written.
proc make-config-header {file args} {
	set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]]
	file mkdir [file dirname $file]
	set lines {}
	lappend lines "#ifndef $guard"
	lappend lines "#define $guard"

	# Add some defaults
	lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_*

	foreach n [lsort [dict keys [all-defines]]] {
		set value [get-define $n]
		set type [calc-define-output-type $n $args]
		switch -exact -- $type {
			-bare {
				# Just output the value unchanged
			}
			-none {
				continue
			}
			-str {
				set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
			}
			-auto {
				# Automatically determine the type
				if {$value eq "0"} {
					lappend lines "/* #undef $n */"
					continue
				}
				if {![string is integer -strict $value]} {
					set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
				}
			}
			"" {
				continue
			}
			default {
				autosetup-error "Unknown type in make-config-header: $type"
			}
		}
		lappend lines "#define $n $value"
	}
	lappend lines "#endif"
	set buf [join $lines \n]
	write-if-changed $file $buf {
		msg-result "Created $file"
	}
}

proc calc-define-output-type {name spec} {
	foreach {type patterns} $spec {
		foreach pattern $patterns {
			if {[string match $pattern $name]} {
				return $type
			}
		}
	}
	return ""
}

# Initialise some values from the environment or commandline or default settings
foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS {CFLAGS "-g -O2"}} {
	lassign $i var default
	define $var [get-env $var $default]
}

if {[env-is-set CC]} {
	# Set by the user, so don't try anything else
	set try [list [get-env CC ""]]
} else {
	# Try some reasonable options
	set try [list [get-define cross]cc [get-define cross]gcc]
}
define CC [find-an-executable {*}$try]
if {[get-define CC] eq ""} {
	user-error "Could not find a C compiler. Tried: [join $try ", "]"
}

define CPP [get-env CPP "[get-define CC] -E"]

# XXX: Could avoid looking for a C++ compiler until requested
# Note that if CXX isn't found, we just set it to "false". It might not be needed.
if {[env-is-set CXX]} {
	define CXX [find-an-executable -required [get-env CXX ""]]
} else {
	define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++ false]
}

# CXXFLAGS default to CFLAGS if not specified
define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]]

# May need a CC_FOR_BUILD, so look for one
define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false]

if {[get-define CC] eq ""} {
	user-error "Could not find a C compiler. Tried: [join $try ", "]"
}

define CCACHE [find-an-executable [get-env CCACHE ccache]]

# Initial cctest settings
cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0}
set autosetup(cc-include-deps) {}

msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS]"
if {[get-define CXX] ne "false"} {
	msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS]"
}
msg-result "Build C compiler...[get-define CC_FOR_BUILD]"

# On Darwin, we prefer to use -g0 to avoid creating .dSYM directories
# but some compilers may not support it, so test here.
switch -glob -- [get-define host] {
	*-*-darwin* {
		if {[cctest -cflags {-g0}]} {
			define cc-default-debug -g0
		}
	}
}

if {![cc-check-includes stdlib.h]} {
	user-error "Compiler does not work. See config.log"
}
Added autosetup/config.guess.













































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#! /bin/sh
# Attempt to guess a canonical system name.
#   Copyright 1992-2014 Free Software Foundation, Inc.

timestamp='2014-11-04'

# This file 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 <http://www.gnu.org/licenses/>.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that
# program.  This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
#
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
# Please send patches to <config-patches@gnu.org>.


me=`echo "$0" | sed -e 's,.*/,,'`

usage="\
Usage: $0 [OPTION]

Output the configuration name of the system \`$me' is run on.

Operation modes:
  -h, --help         print this help, then exit
  -t, --time-stamp   print date of last modification, then exit
  -v, --version      print version number, then exit

Report bugs and patches to <config-patches@gnu.org>."

version="\
GNU config.guess ($timestamp)

Originally written by Per Bothner.
Copyright 1992-2014 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

help="
Try \`$me --help' for more information."

# Parse command line
while test $# -gt 0 ; do
  case $1 in
    --time-stamp | --time* | -t )
       echo "$timestamp" ; exit ;;
    --version | -v )
       echo "$version" ; exit ;;
    --help | --h* | -h )
       echo "$usage"; exit ;;
    -- )     # Stop option processing
       shift; break ;;
    - )	# Use stdin as input.
       break ;;
    -* )
       echo "$me: invalid option $1$help" >&2
       exit 1 ;;
    * )
       break ;;
  esac
done

if test $# != 0; then
  echo "$me: too many arguments$help" >&2
  exit 1
fi

trap 'exit 1' 1 2 15

# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
# compiler to aid in system detection is discouraged as it requires
# temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion.

# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
# use `HOST_CC' if defined, but it is deprecated.

# Portable tmp directory creation inspired by the Autoconf team.

set_cc_for_build='
trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
: ${TMPDIR=/tmp} ;
 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
 { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
 { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
 { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
dummy=$tmp/dummy ;
tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
case $CC_FOR_BUILD,$HOST_CC,$CC in
 ,,)    echo "int x;" > $dummy.c ;
	for c in cc gcc c89 c99 ; do
	  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
	     CC_FOR_BUILD="$c"; break ;
	  fi ;
	done ;
	if test x"$CC_FOR_BUILD" = x ; then
	  CC_FOR_BUILD=no_compiler_found ;
	fi
	;;
 ,,*)   CC_FOR_BUILD=$CC ;;
 ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
esac ; set_cc_for_build= ;'

# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
# (ghazi@noc.rutgers.edu 1994-08-24)
if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
	PATH=$PATH:/.attbin ; export PATH
fi

UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown

case "${UNAME_SYSTEM}" in
Linux|GNU|GNU/*)
	# If the system lacks a compiler, then just pick glibc.
	# We could probably try harder.
	LIBC=gnu

	eval $set_cc_for_build
	cat <<-EOF > $dummy.c
	#include <features.h>
	#if defined(__UCLIBC__)
	LIBC=uclibc
	#elif defined(__dietlibc__)
	LIBC=dietlibc
	#else
	LIBC=gnu
	#endif
	EOF
	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
	;;
esac

# Note: order is significant - the case branches are not exclusive.

case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
    *:NetBSD:*:*)
	# NetBSD (nbsd) targets should (where applicable) match one or
	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
	# switched to ELF, *-*-netbsd* would select the old
	# object file format.  This provides both forward
	# compatibility and a consistent mechanism for selecting the
	# object file format.
	#
	# Note: NetBSD doesn't particularly care about the vendor
	# portion of the name.  We always set it to "unknown".
	sysctl="sysctl -n hw.machine_arch"
	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
	case "${UNAME_MACHINE_ARCH}" in
	    armeb) machine=armeb-unknown ;;
	    arm*) machine=arm-unknown ;;
	    sh3el) machine=shl-unknown ;;
	    sh3eb) machine=sh-unknown ;;
	    sh5el) machine=sh5le-unknown ;;
	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
	esac
	# The Operating System including object format, if it has switched
	# to ELF recently, or will in the future.
	case "${UNAME_MACHINE_ARCH}" in
	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
		eval $set_cc_for_build
		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
			| grep -q __ELF__
		then
		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
		    # Return netbsd for either.  FIX?
		    os=netbsd
		else
		    os=netbsdelf
		fi
		;;
	    *)
		os=netbsd
		;;
	esac
	# The OS release
	# Debian GNU/NetBSD machines have a different userland, and
	# thus, need a distinct triplet. However, they do not need
	# kernel version information, so it can be replaced with a
	# suitable tag, in the style of linux-gnu.
	case "${UNAME_VERSION}" in
	    Debian*)
		release='-gnu'
		;;
	    *)
		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
		;;
	esac
	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
	# contains redundant information, the shorter form:
	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
	echo "${machine}-${os}${release}"
	exit ;;
    *:Bitrig:*:*)
	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
	echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
	exit ;;
    *:OpenBSD:*:*)
	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
	echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
	exit ;;
    *:ekkoBSD:*:*)
	echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
	exit ;;
    *:SolidBSD:*:*)
	echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
	exit ;;
    macppc:MirBSD:*:*)
	echo powerpc-unknown-mirbsd${UNAME_RELEASE}
	exit ;;
    *:MirBSD:*:*)
	echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
	exit ;;
    alpha:OSF1:*:*)
	case $UNAME_RELEASE in
	*4.0)
		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
		;;
	*5.*)
		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
		;;
	esac
	# According to Compaq, /usr/sbin/psrinfo has been available on
	# OSF/1 and Tru64 systems produced since 1995.  I hope that
	# covers most systems running today.  This code pipes the CPU
	# types through head -n 1, so we only detect the type of CPU 0.
	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
	case "$ALPHA_CPU_TYPE" in
	    "EV4 (21064)")
		UNAME_MACHINE="alpha" ;;
	    "EV4.5 (21064)")
		UNAME_MACHINE="alpha" ;;
	    "LCA4 (21066/21068)")
		UNAME_MACHINE="alpha" ;;
	    "EV5 (21164)")
		UNAME_MACHINE="alphaev5" ;;
	    "EV5.6 (21164A)")
		UNAME_MACHINE="alphaev56" ;;
	    "EV5.6 (21164PC)")
		UNAME_MACHINE="alphapca56" ;;
	    "EV5.7 (21164PC)")
		UNAME_MACHINE="alphapca57" ;;
	    "EV6 (21264)")
		UNAME_MACHINE="alphaev6" ;;
	    "EV6.7 (21264A)")
		UNAME_MACHINE="alphaev67" ;;
	    "EV6.8CB (21264C)")
		UNAME_MACHINE="alphaev68" ;;
	    "EV6.8AL (21264B)")
		UNAME_MACHINE="alphaev68" ;;
	    "EV6.8CX (21264D)")
		UNAME_MACHINE="alphaev68" ;;
	    "EV6.9A (21264/EV69A)")
		UNAME_MACHINE="alphaev69" ;;
	    "EV7 (21364)")
		UNAME_MACHINE="alphaev7" ;;
	    "EV7.9 (21364A)")
		UNAME_MACHINE="alphaev79" ;;
	esac
	# A Pn.n version is a patched version.
	# A Vn.n version is a released version.
	# A Tn.n version is a released field test version.
	# A Xn.n version is an unreleased experimental baselevel.
	# 1.2 uses "1.2" for uname -r.
	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
	exitcode=$?
	trap '' 0
	exit $exitcode ;;
    Alpha\ *:Windows_NT*:*)
	# How do we know it's Interix rather than the generic POSIX subsystem?
	# Should we change UNAME_MACHINE based on the output of uname instead
	# of the specific Alpha model?
	echo alpha-pc-interix
	exit ;;
    21064:Windows_NT:50:3)
	echo alpha-dec-winnt3.5
	exit ;;
    Amiga*:UNIX_System_V:4.0:*)
	echo m68k-unknown-sysv4
	exit ;;
    *:[Aa]miga[Oo][Ss]:*:*)
	echo ${UNAME_MACHINE}-unknown-amigaos
	exit ;;
    *:[Mm]orph[Oo][Ss]:*:*)
	echo ${UNAME_MACHINE}-unknown-morphos
	exit ;;
    *:OS/390:*:*)
	echo i370-ibm-openedition
	exit ;;
    *:z/VM:*:*)
	echo s390-ibm-zvmoe
	exit ;;
    *:OS400:*:*)
	echo powerpc-ibm-os400
	exit ;;
    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
	echo arm-acorn-riscix${UNAME_RELEASE}
	exit ;;
    arm*:riscos:*:*|arm*:RISCOS:*:*)
	echo arm-unknown-riscos
	exit ;;
    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
	echo hppa1.1-hitachi-hiuxmpp
	exit ;;
    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
	if test "`(/bin/universe) 2>/dev/null`" = att ; then
		echo pyramid-pyramid-sysv3
	else
		echo pyramid-pyramid-bsd
	fi
	exit ;;
    NILE*:*:*:dcosx)
	echo pyramid-pyramid-svr4
	exit ;;
    DRS?6000:unix:4.0:6*)
	echo sparc-icl-nx6
	exit ;;
    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
	case `/usr/bin/uname -p` in
	    sparc) echo sparc-icl-nx7; exit ;;
	esac ;;
    s390x:SunOS:*:*)
	echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
	exit ;;
    sun4H:SunOS:5.*:*)
	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
	exit ;;
    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
	exit ;;
    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
	echo i386-pc-auroraux${UNAME_RELEASE}
	exit ;;
    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
	eval $set_cc_for_build
	SUN_ARCH="i386"
	# If there is a compiler, see if it is configured for 64-bit objects.
	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
	# This test works for both compilers.
	if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
		(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
		grep IS_64BIT_ARCH >/dev/null
	    then
		SUN_ARCH="x86_64"
	    fi
	fi
	echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
	exit ;;
    sun4*:SunOS:6*:*)
	# According to config.sub, this is the proper way to canonicalize
	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
	# it's likely to be more like Solaris than SunOS4.
	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
	exit ;;
    sun4*:SunOS:*:*)
	case "`/usr/bin/arch -k`" in
	    Series*|S4*)
		UNAME_RELEASE=`uname -v`
		;;
	esac
	# Japanese Language versions have a version number like `4.1.3-JL'.
	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
	exit ;;
    sun3*:SunOS:*:*)
	echo m68k-sun-sunos${UNAME_RELEASE}
	exit ;;
    sun*:*:4.2BSD:*)
	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
	case "`/bin/arch`" in
	    sun3)
		echo m68k-sun-sunos${UNAME_RELEASE}
		;;
	    sun4)
		echo sparc-sun-sunos${UNAME_RELEASE}
		;;
	esac
	exit ;;
    aushp:SunOS:*:*)
	echo sparc-auspex-sunos${UNAME_RELEASE}
	exit ;;
    # The situation for MiNT is a little confusing.  The machine name
    # can be virtually everything (everything which is not
    # "atarist" or "atariste" at least should have a processor
    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
    # to the lowercase version "mint" (or "freemint").  Finally
    # the system name "TOS" denotes a system which is actually not
    # MiNT.  But MiNT is downward compatible to TOS, so this should
    # be no problem.
    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
	echo m68k-atari-mint${UNAME_RELEASE}
	exit ;;
    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
	echo m68k-atari-mint${UNAME_RELEASE}
	exit ;;
    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
	echo m68k-atari-mint${UNAME_RELEASE}
	exit ;;
    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
	echo m68k-milan-mint${UNAME_RELEASE}
	exit ;;
    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
	echo m68k-hades-mint${UNAME_RELEASE}
	exit ;;
    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
	echo m68k-unknown-mint${UNAME_RELEASE}
	exit ;;
    m68k:machten:*:*)
	echo m68k-apple-machten${UNAME_RELEASE}
	exit ;;
    powerpc:machten:*:*)
	echo powerpc-apple-machten${UNAME_RELEASE}
	exit ;;
    RISC*:Mach:*:*)
	echo mips-dec-mach_bsd4.3
	exit ;;
    RISC*:ULTRIX:*:*)
	echo mips-dec-ultrix${UNAME_RELEASE}
	exit ;;
    VAX*:ULTRIX*:*:*)
	echo vax-dec-ultrix${UNAME_RELEASE}
	exit ;;
    2020:CLIX:*:* | 2430:CLIX:*:*)
	echo clipper-intergraph-clix${UNAME_RELEASE}
	exit ;;
    mips:*:*:UMIPS | mips:*:*:RISCos)
	eval $set_cc_for_build
	sed 's/^	//' << EOF >$dummy.c
#ifdef __cplusplus
#include <stdio.h>  /* for printf() prototype */
	int main (int argc, char *argv[]) {
#else
	int main (argc, argv) int argc; char *argv[]; {
#endif
	#if defined (host_mips) && defined (MIPSEB)
	#if defined (SYSTYPE_SYSV)
	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
	#endif
	#if defined (SYSTYPE_SVR4)
	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
	#endif
	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
	#endif
	#endif
	  exit (-1);
	}
EOF
	$CC_FOR_BUILD -o $dummy $dummy.c &&
	  dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
	  SYSTEM_NAME=`$dummy $dummyarg` &&
	    { echo "$SYSTEM_NAME"; exit; }
	echo mips-mips-riscos${UNAME_RELEASE}
	exit ;;
    Motorola:PowerMAX_OS:*:*)
	echo powerpc-motorola-powermax
	exit ;;
    Motorola:*:4.3:PL8-*)
	echo powerpc-harris-powermax
	exit ;;
    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
	echo powerpc-harris-powermax
	exit ;;
    Night_Hawk:Power_UNIX:*:*)
	echo powerpc-harris-powerunix
	exit ;;
    m88k:CX/UX:7*:*)
	echo m88k-harris-cxux7
	exit ;;
    m88k:*:4*:R4*)
	echo m88k-motorola-sysv4
	exit ;;
    m88k:*:3*:R3*)
	echo m88k-motorola-sysv3
	exit ;;
    AViiON:dgux:*:*)
	# DG/UX returns AViiON for all architectures
	UNAME_PROCESSOR=`/usr/bin/uname -p`
	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
	then
	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
	       [ ${TARGET_BINARY_INTERFACE}x = x ]
	    then
		echo m88k-dg-dgux${UNAME_RELEASE}
	    else
		echo m88k-dg-dguxbcs${UNAME_RELEASE}
	    fi
	else
	    echo i586-dg-dgux${UNAME_RELEASE}
	fi
	exit ;;
    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
	echo m88k-dolphin-sysv3
	exit ;;
    M88*:*:R3*:*)
	# Delta 88k system running SVR3
	echo m88k-motorola-sysv3
	exit ;;
    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
	echo m88k-tektronix-sysv3
	exit ;;
    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
	echo m68k-tektronix-bsd
	exit ;;
    *:IRIX*:*:*)
	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
	exit ;;
    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
	echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
	exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
    i*86:AIX:*:*)
	echo i386-ibm-aix
	exit ;;
    ia64:AIX:*:*)
	if [ -x /usr/bin/oslevel ] ; then
		IBM_REV=`/usr/bin/oslevel`
	else
		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
	fi
	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
	exit ;;
    *:AIX:2:3)
	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
		eval $set_cc_for_build
		sed 's/^		//' << EOF >$dummy.c
		#include <sys/systemcfg.h>

		main()
			{
			if (!__power_pc())
				exit(1);
			puts("powerpc-ibm-aix3.2.5");
			exit(0);
			}
EOF
		if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
		then
			echo "$SYSTEM_NAME"
		else
			echo rs6000-ibm-aix3.2.5
		fi
	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
		echo rs6000-ibm-aix3.2.4
	else
		echo rs6000-ibm-aix3.2
	fi
	exit ;;
    *:AIX:*:[4567])
	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
		IBM_ARCH=rs6000
	else
		IBM_ARCH=powerpc
	fi
	if [ -x /usr/bin/lslpp ] ; then
		IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
			   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
	else
		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
	fi
	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
	exit ;;
    *:AIX:*:*)
	echo rs6000-ibm-aix
	exit ;;
    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
	echo romp-ibm-bsd4.4
	exit ;;
    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
	exit ;;                             # report: romp-ibm BSD 4.3
    *:BOSX:*:*)
	echo rs6000-bull-bosx
	exit ;;
    DPX/2?00:B.O.S.:*:*)
	echo m68k-bull-sysv3
	exit ;;
    9000/[34]??:4.3bsd:1.*:*)
	echo m68k-hp-bsd
	exit ;;
    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
	echo m68k-hp-bsd4.4
	exit ;;
    9000/[34678]??:HP-UX:*:*)
	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
	case "${UNAME_MACHINE}" in
	    9000/31? )            HP_ARCH=m68000 ;;
	    9000/[34]?? )         HP_ARCH=m68k ;;
	    9000/[678][0-9][0-9])
		if [ -x /usr/bin/getconf ]; then
		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
		    case "${sc_cpu_version}" in
		      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
		      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
		      532)                      # CPU_PA_RISC2_0
			case "${sc_kernel_bits}" in
			  32) HP_ARCH="hppa2.0n" ;;
			  64) HP_ARCH="hppa2.0w" ;;
			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
			esac ;;
		    esac
		fi
		if [ "${HP_ARCH}" = "" ]; then
		    eval $set_cc_for_build
		    sed 's/^		//' << EOF >$dummy.c

		#define _HPUX_SOURCE
		#include <stdlib.h>
		#include <unistd.h>

		int main ()
		{
		#if defined(_SC_KERNEL_BITS)
		    long bits = sysconf(_SC_KERNEL_BITS);
		#endif
		    long cpu  = sysconf (_SC_CPU_VERSION);

		    switch (cpu)
			{
			case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
			case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
			case CPU_PA_RISC2_0:
		#if defined(_SC_KERNEL_BITS)
			    switch (bits)
				{
				case 64: puts ("hppa2.0w"); break;
				case 32: puts ("hppa2.0n"); break;
				default: puts ("hppa2.0"); break;
				} break;
		#else  /* !defined(_SC_KERNEL_BITS) */
			    puts ("hppa2.0"); break;
		#endif
			default: puts ("hppa1.0"); break;
			}
		    exit (0);
		}
EOF
		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
		    test -z "$HP_ARCH" && HP_ARCH=hppa
		fi ;;
	esac
	if [ ${HP_ARCH} = "hppa2.0w" ]
	then
	    eval $set_cc_for_build

	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
	    # generating 64-bit code.  GNU and HP use different nomenclature:
	    #
	    # $ CC_FOR_BUILD=cc ./config.guess
	    # => hppa2.0w-hp-hpux11.23
	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
	    # => hppa64-hp-hpux11.23

	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
		grep -q __LP64__
	    then
		HP_ARCH="hppa2.0w"
	    else
		HP_ARCH="hppa64"
	    fi
	fi
	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
	exit ;;
    ia64:HP-UX:*:*)
	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
	echo ia64-hp-hpux${HPUX_REV}
	exit ;;
    3050*:HI-UX:*:*)
	eval $set_cc_for_build
	sed 's/^	//' << EOF >$dummy.c
	#include <unistd.h>
	int
	main ()
	{
	  long cpu = sysconf (_SC_CPU_VERSION);
	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
	     results, however.  */
	  if (CPU_IS_PA_RISC (cpu))
	    {
	      switch (cpu)
		{
		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
		  default: puts ("hppa-hitachi-hiuxwe2"); break;
		}
	    }
	  else if (CPU_IS_HP_MC68K (cpu))
	    puts ("m68k-hitachi-hiuxwe2");
	  else puts ("unknown-hitachi-hiuxwe2");
	  exit (0);
	}
EOF
	$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
		{ echo "$SYSTEM_NAME"; exit; }
	echo unknown-hitachi-hiuxwe2
	exit ;;
    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
	echo hppa1.1-hp-bsd
	exit ;;
    9000/8??:4.3bsd:*:*)
	echo hppa1.0-hp-bsd
	exit ;;
    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
	echo hppa1.0-hp-mpeix
	exit ;;
    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
	echo hppa1.1-hp-osf
	exit ;;
    hp8??:OSF1:*:*)
	echo hppa1.0-hp-osf
	exit ;;
    i*86:OSF1:*:*)
	if [ -x /usr/sbin/sysversion ] ; then
	    echo ${UNAME_MACHINE}-unknown-osf1mk
	else
	    echo ${UNAME_MACHINE}-unknown-osf1
	fi
	exit ;;
    parisc*:Lites*:*:*)
	echo hppa1.1-hp-lites
	exit ;;
    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
	echo c1-convex-bsd
	exit ;;
    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
	if getsysinfo -f scalar_acc
	then echo c32-convex-bsd
	else echo c2-convex-bsd
	fi
	exit ;;
    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
	echo c34-convex-bsd
	exit ;;
    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
	echo c38-convex-bsd
	exit ;;
    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
	echo c4-convex-bsd
	exit ;;
    CRAY*Y-MP:*:*:*)
	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
	exit ;;
    CRAY*[A-Z]90:*:*:*)
	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
	      -e 's/\.[^.]*$/.X/'
	exit ;;
    CRAY*TS:*:*:*)
	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
	exit ;;
    CRAY*T3E:*:*:*)
	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
	exit ;;
    CRAY*SV1:*:*:*)
	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
	exit ;;
    *:UNICOS/mp:*:*)
	echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
	exit ;;
    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
	FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
	echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
	exit ;;
    5000:UNIX_System_V:4.*:*)
	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
	FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
	echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
	exit ;;
    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
	exit ;;
    sparc*:BSD/OS:*:*)
	echo sparc-unknown-bsdi${UNAME_RELEASE}
	exit ;;
    *:BSD/OS:*:*)
	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
	exit ;;
    *:FreeBSD:*:*)
	UNAME_PROCESSOR=`/usr/bin/uname -p`
	case ${UNAME_PROCESSOR} in
	    amd64)
		echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
	    *)
		echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
	esac
	exit ;;
    i*:CYGWIN*:*)
	echo ${UNAME_MACHINE}-pc-cygwin
	exit ;;
    *:MINGW64*:*)
	echo ${UNAME_MACHINE}-pc-mingw64
	exit ;;
    *:MINGW*:*)
	echo ${UNAME_MACHINE}-pc-mingw32
	exit ;;
    *:MSYS*:*)
	echo ${UNAME_MACHINE}-pc-msys
	exit ;;
    i*:windows32*:*)
	# uname -m includes "-pc" on this system.
	echo ${UNAME_MACHINE}-mingw32
	exit ;;
    i*:PW*:*)
	echo ${UNAME_MACHINE}-pc-pw32
	exit ;;
    *:Interix*:*)
	case ${UNAME_MACHINE} in
	    x86)
		echo i586-pc-interix${UNAME_RELEASE}
		exit ;;
	    authenticamd | genuineintel | EM64T)
		echo x86_64-unknown-interix${UNAME_RELEASE}
		exit ;;
	    IA64)
		echo ia64-unknown-interix${UNAME_RELEASE}
		exit ;;
	esac ;;
    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
	echo i${UNAME_MACHINE}-pc-mks
	exit ;;
    8664:Windows_NT:*)
	echo x86_64-pc-mks
	exit ;;
    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
	# How do we know it's Interix rather than the generic POSIX subsystem?
	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
	# UNAME_MACHINE based on the output of uname instead of i386?
	echo i586-pc-interix
	exit ;;
    i*:UWIN*:*)
	echo ${UNAME_MACHINE}-pc-uwin
	exit ;;
    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
	echo x86_64-unknown-cygwin
	exit ;;
    p*:CYGWIN*:*)
	echo powerpcle-unknown-cygwin
	exit ;;
    prep*:SunOS:5.*:*)
	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
	exit ;;
    *:GNU:*:*)
	# the GNU system
	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
	exit ;;
    *:GNU/*:*:*)
	# other systems with GNU libc and userland
	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
	exit ;;
    i*86:Minix:*:*)
	echo ${UNAME_MACHINE}-pc-minix
	exit ;;
    aarch64:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    aarch64_be:Linux:*:*)
	UNAME_MACHINE=aarch64_be
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    alpha:Linux:*:*)
	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
	  EV5)   UNAME_MACHINE=alphaev5 ;;
	  EV56)  UNAME_MACHINE=alphaev56 ;;
	  PCA56) UNAME_MACHINE=alphapca56 ;;
	  PCA57) UNAME_MACHINE=alphapca56 ;;
	  EV6)   UNAME_MACHINE=alphaev6 ;;
	  EV67)  UNAME_MACHINE=alphaev67 ;;
	  EV68*) UNAME_MACHINE=alphaev68 ;;
	esac
	objdump --private-headers /bin/sh | grep -q ld.so.1
	if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    arc:Linux:*:* | arceb:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    arm*:Linux:*:*)
	eval $set_cc_for_build
	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
	    | grep -q __ARM_EABI__
	then
	    echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	else
	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
		| grep -q __ARM_PCS_VFP
	    then
		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
	    else
		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
	    fi
	fi
	exit ;;
    avr32*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    cris:Linux:*:*)
	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
	exit ;;
    crisv32:Linux:*:*)
	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
	exit ;;
    frv:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    hexagon:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    i*86:Linux:*:*)
	echo ${UNAME_MACHINE}-pc-linux-${LIBC}
	exit ;;
    ia64:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    m32r*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    m68*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    mips:Linux:*:* | mips64:Linux:*:*)
	eval $set_cc_for_build
	sed 's/^	//' << EOF >$dummy.c
	#undef CPU
	#undef ${UNAME_MACHINE}
	#undef ${UNAME_MACHINE}el
	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
	CPU=${UNAME_MACHINE}el
	#else
	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
	CPU=${UNAME_MACHINE}
	#else
	CPU=
	#endif
	#endif
EOF
	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
	;;
    openrisc*:Linux:*:*)
	echo or1k-unknown-linux-${LIBC}
	exit ;;
    or32:Linux:*:* | or1k*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    padre:Linux:*:*)
	echo sparc-unknown-linux-${LIBC}
	exit ;;
    parisc64:Linux:*:* | hppa64:Linux:*:*)
	echo hppa64-unknown-linux-${LIBC}
	exit ;;
    parisc:Linux:*:* | hppa:Linux:*:*)
	# Look for CPU level
	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
	  PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
	  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
	  *)    echo hppa-unknown-linux-${LIBC} ;;
	esac
	exit ;;
    ppc64:Linux:*:*)
	echo powerpc64-unknown-linux-${LIBC}
	exit ;;
    ppc:Linux:*:*)
	echo powerpc-unknown-linux-${LIBC}
	exit ;;
    ppc64le:Linux:*:*)
	echo powerpc64le-unknown-linux-${LIBC}
	exit ;;
    ppcle:Linux:*:*)
	echo powerpcle-unknown-linux-${LIBC}
	exit ;;
    s390:Linux:*:* | s390x:Linux:*:*)
	echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
	exit ;;
    sh64*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    sh*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    sparc:Linux:*:* | sparc64:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    tile*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    vax:Linux:*:*)
	echo ${UNAME_MACHINE}-dec-linux-${LIBC}
	exit ;;
    x86_64:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    xtensa*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    i*86:DYNIX/ptx:4*:*)
	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
	# earlier versions are messed up and put the nodename in both
	# sysname and nodename.
	echo i386-sequent-sysv4
	exit ;;
    i*86:UNIX_SV:4.2MP:2.*)
	# Unixware is an offshoot of SVR4, but it has its own version
	# number series starting with 2...
	# I am not positive that other SVR4 systems won't match this,
	# I just have to hope.  -- rms.
	# Use sysv4.2uw... so that sysv4* matches it.
	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
	exit ;;
    i*86:OS/2:*:*)
	# If we were able to find `uname', then EMX Unix compatibility
	# is probably installed.
	echo ${UNAME_MACHINE}-pc-os2-emx
	exit ;;
    i*86:XTS-300:*:STOP)
	echo ${UNAME_MACHINE}-unknown-stop
	exit ;;
    i*86:atheos:*:*)
	echo ${UNAME_MACHINE}-unknown-atheos
	exit ;;
    i*86:syllable:*:*)
	echo ${UNAME_MACHINE}-pc-syllable
	exit ;;
    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
	echo i386-unknown-lynxos${UNAME_RELEASE}
	exit ;;
    i*86:*DOS:*:*)
	echo ${UNAME_MACHINE}-pc-msdosdjgpp
	exit ;;
    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
	else
		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
	fi
	exit ;;
    i*86:*:5:[678]*)
	# UnixWare 7.x, OpenUNIX and OpenServer 6.
	case `/bin/uname -X | grep "^Machine"` in
	    *486*)	     UNAME_MACHINE=i486 ;;
	    *Pentium)	     UNAME_MACHINE=i586 ;;
	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
	esac
	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
	exit ;;
    i*86:*:3.2:*)
	if test -f /usr/options/cb.name; then
		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
	elif /bin/uname -X 2>/dev/null >/dev/null ; then
		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
			&& UNAME_MACHINE=i586
		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
			&& UNAME_MACHINE=i686
		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
			&& UNAME_MACHINE=i686
		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
	else
		echo ${UNAME_MACHINE}-pc-sysv32
	fi
	exit ;;
    pc:*:*:*)
	# Left here for compatibility:
	# uname -m prints for DJGPP always 'pc', but it prints nothing about
	# the processor, so we play safe by assuming i586.
	# Note: whatever this is, it MUST be the same as what config.sub
	# prints for the "djgpp" host, or else GDB configury will decide that
	# this is a cross-build.
	echo i586-pc-msdosdjgpp
	exit ;;
    Intel:Mach:3*:*)
	echo i386-pc-mach3
	exit ;;
    paragon:*:*:*)
	echo i860-intel-osf1
	exit ;;
    i860:*:4.*:*) # i860-SVR4
	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
	else # Add other i860-SVR4 vendors below as they are discovered.
	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
	fi
	exit ;;
    mini*:CTIX:SYS*5:*)
	# "miniframe"
	echo m68010-convergent-sysv
	exit ;;
    mc68k:UNIX:SYSTEM5:3.51m)
	echo m68k-convergent-sysv
	exit ;;
    M680?0:D-NIX:5.3:*)
	echo m68k-diab-dnix
	exit ;;
    M68*:*:R3V[5678]*:*)
	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
	OS_REL=''
	test -r /etc/.relid \
	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
	  && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
	  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
	  && { echo i486-ncr-sysv4; exit; } ;;
    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
	OS_REL='.3'
	test -r /etc/.relid \
	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
	    && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
	echo m68k-unknown-lynxos${UNAME_RELEASE}
	exit ;;
    mc68030:UNIX_System_V:4.*:*)
	echo m68k-atari-sysv4
	exit ;;
    TSUNAMI:LynxOS:2.*:*)
	echo sparc-unknown-lynxos${UNAME_RELEASE}
	exit ;;
    rs6000:LynxOS:2.*:*)
	echo rs6000-unknown-lynxos${UNAME_RELEASE}
	exit ;;
    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
	echo powerpc-unknown-lynxos${UNAME_RELEASE}
	exit ;;
    SM[BE]S:UNIX_SV:*:*)
	echo mips-dde-sysv${UNAME_RELEASE}
	exit ;;
    RM*:ReliantUNIX-*:*:*)
	echo mips-sni-sysv4
	exit ;;
    RM*:SINIX-*:*:*)
	echo mips-sni-sysv4
	exit ;;
    *:SINIX-*:*:*)
	if uname -p 2>/dev/null >/dev/null ; then
		UNAME_MACHINE=`(uname -p) 2>/dev/null`
		echo ${UNAME_MACHINE}-sni-sysv4
	else
		echo ns32k-sni-sysv
	fi
	exit ;;
    PENTIUM:*:4.0*:*)	# Unisys `ClearPath HMP IX 4000' SVR4/MP effort
			# says <Richard.M.Bartel@ccMail.Census.GOV>
	echo i586-unisys-sysv4
	exit ;;
    *:UNIX_System_V:4*:FTX*)
	# From Gerald Hewes <hewes@openmarket.com>.
	# How about differentiating between stratus architectures? -djm
	echo hppa1.1-stratus-sysv4
	exit ;;
    *:*:*:FTX*)
	# From seanf@swdc.stratus.com.
	echo i860-stratus-sysv4
	exit ;;
    i*86:VOS:*:*)
	# From Paul.Green@stratus.com.
	echo ${UNAME_MACHINE}-stratus-vos
	exit ;;
    *:VOS:*:*)
	# From Paul.Green@stratus.com.
	echo hppa1.1-stratus-vos
	exit ;;
    mc68*:A/UX:*:*)
	echo m68k-apple-aux${UNAME_RELEASE}
	exit ;;
    news*:NEWS-OS:6*:*)
	echo mips-sony-newsos6
	exit ;;
    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
	if [ -d /usr/nec ]; then
		echo mips-nec-sysv${UNAME_RELEASE}
	else
		echo mips-unknown-sysv${UNAME_RELEASE}
	fi
	exit ;;
    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
	echo powerpc-be-beos
	exit ;;
    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
	echo powerpc-apple-beos
	exit ;;
    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
	echo i586-pc-beos
	exit ;;
    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
	echo i586-pc-haiku
	exit ;;
    x86_64:Haiku:*:*)
	echo x86_64-unknown-haiku
	exit ;;
    SX-4:SUPER-UX:*:*)
	echo sx4-nec-superux${UNAME_RELEASE}
	exit ;;
    SX-5:SUPER-UX:*:*)
	echo sx5-nec-superux${UNAME_RELEASE}
	exit ;;
    SX-6:SUPER-UX:*:*)
	echo sx6-nec-superux${UNAME_RELEASE}
	exit ;;
    SX-7:SUPER-UX:*:*)
	echo sx7-nec-superux${UNAME_RELEASE}
	exit ;;
    SX-8:SUPER-UX:*:*)
	echo sx8-nec-superux${UNAME_RELEASE}
	exit ;;
    SX-8R:SUPER-UX:*:*)
	echo sx8r-nec-superux${UNAME_RELEASE}
	exit ;;
    Power*:Rhapsody:*:*)
	echo powerpc-apple-rhapsody${UNAME_RELEASE}
	exit ;;
    *:Rhapsody:*:*)
	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
	exit ;;
    *:Darwin:*:*)
	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
	eval $set_cc_for_build
	if test "$UNAME_PROCESSOR" = unknown ; then
	    UNAME_PROCESSOR=powerpc
	fi
	if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
	    if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
		if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
		    (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
		    grep IS_64BIT_ARCH >/dev/null
		then
		    case $UNAME_PROCESSOR in
			i386) UNAME_PROCESSOR=x86_64 ;;
			powerpc) UNAME_PROCESSOR=powerpc64 ;;
		    esac
		fi
	    fi
	elif test "$UNAME_PROCESSOR" = i386 ; then
	    # Avoid executing cc on OS X 10.9, as it ships with a stub
	    # that puts up a graphical alert prompting to install
	    # developer tools.  Any system running Mac OS X 10.7 or
	    # later (Darwin 11 and later) is required to have a 64-bit
	    # processor. This is not true of the ARM version of Darwin
	    # that Apple uses in portable devices.
	    UNAME_PROCESSOR=x86_64
	fi
	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
	exit ;;
    *:procnto*:*:* | *:QNX:[0123456789]*:*)
	UNAME_PROCESSOR=`uname -p`
	if test "$UNAME_PROCESSOR" = "x86"; then
		UNAME_PROCESSOR=i386
		UNAME_MACHINE=pc
	fi
	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
	exit ;;
    *:QNX:*:4*)
	echo i386-pc-qnx
	exit ;;
    NEO-?:NONSTOP_KERNEL:*:*)
	echo neo-tandem-nsk${UNAME_RELEASE}
	exit ;;
    NSE-*:NONSTOP_KERNEL:*:*)
	echo nse-tandem-nsk${UNAME_RELEASE}
	exit ;;
    NSR-?:NONSTOP_KERNEL:*:*)
	echo nsr-tandem-nsk${UNAME_RELEASE}
	exit ;;
    *:NonStop-UX:*:*)
	echo mips-compaq-nonstopux
	exit ;;
    BS2000:POSIX*:*:*)
	echo bs2000-siemens-sysv
	exit ;;
    DS/*:UNIX_System_V:*:*)
	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
	exit ;;
    *:Plan9:*:*)
	# "uname -m" is not consistent, so use $cputype instead. 386
	# is converted to i386 for consistency with other x86
	# operating systems.
	if test "$cputype" = "386"; then
	    UNAME_MACHINE=i386
	else
	    UNAME_MACHINE="$cputype"
	fi
	echo ${UNAME_MACHINE}-unknown-plan9
	exit ;;
    *:TOPS-10:*:*)
	echo pdp10-unknown-tops10
	exit ;;
    *:TENEX:*:*)
	echo pdp10-unknown-tenex
	exit ;;
    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
	echo pdp10-dec-tops20
	exit ;;
    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
	echo pdp10-xkl-tops20
	exit ;;
    *:TOPS-20:*:*)
	echo pdp10-unknown-tops20
	exit ;;
    *:ITS:*:*)
	echo pdp10-unknown-its
	exit ;;
    SEI:*:*:SEIUX)
	echo mips-sei-seiux${UNAME_RELEASE}
	exit ;;
    *:DragonFly:*:*)
	echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
	exit ;;
    *:*VMS:*:*)
	UNAME_MACHINE=`(uname -p) 2>/dev/null`
	case "${UNAME_MACHINE}" in
	    A*) echo alpha-dec-vms ; exit ;;
	    I*) echo ia64-dec-vms ; exit ;;
	    V*) echo vax-dec-vms ; exit ;;
	esac ;;
    *:XENIX:*:SysV)
	echo i386-pc-xenix
	exit ;;
    i*86:skyos:*:*)
	echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
	exit ;;
    i*86:rdos:*:*)
	echo ${UNAME_MACHINE}-pc-rdos
	exit ;;
    i*86:AROS:*:*)
	echo ${UNAME_MACHINE}-pc-aros
	exit ;;
    x86_64:VMkernel:*:*)
	echo ${UNAME_MACHINE}-unknown-esx
	exit ;;
esac

cat >&2 <<EOF
$0: unable to guess system type

This script, last modified $timestamp, has failed to recognize
the operating system you are using. It is advised that you
download the most up to date version of the config scripts from

  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
and
  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD

If the version you run ($0) is already up to date, please
send the following data and any information you think might be
pertinent to <config-patches@gnu.org> in order to provide the needed
information to handle your system.

config.guess timestamp = $timestamp

uname -m = `(uname -m) 2>/dev/null || echo unknown`
uname -r = `(uname -r) 2>/dev/null || echo unknown`
uname -s = `(uname -s) 2>/dev/null || echo unknown`
uname -v = `(uname -v) 2>/dev/null || echo unknown`

/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`

hostinfo               = `(hostinfo) 2>/dev/null`
/bin/universe          = `(/bin/universe) 2>/dev/null`
/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
/bin/arch              = `(/bin/arch) 2>/dev/null`
/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`

UNAME_MACHINE = ${UNAME_MACHINE}
UNAME_RELEASE = ${UNAME_RELEASE}
UNAME_SYSTEM  = ${UNAME_SYSTEM}
UNAME_VERSION = ${UNAME_VERSION}
EOF

exit 1

# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
Added autosetup/config.sub.















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#! /bin/sh
# Configuration validation subroutine script.
#   Copyright 1992-2014 Free Software Foundation, Inc.

timestamp='2014-12-03'

# This file 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 <http://www.gnu.org/licenses/>.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that
# program.  This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").


# Please send patches to <config-patches@gnu.org>.
#
# Configuration subroutine to validate and canonicalize a configuration type.
# Supply the specified configuration type as an argument.
# If it is invalid, we print an error message on stderr and exit with code 1.
# Otherwise, we print the canonical config type on stdout and succeed.

# You can get the latest version of this script from:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD

# This file is supposed to be the same for all GNU packages
# and recognize all the CPU types, system types and aliases
# that are meaningful with *any* GNU software.
# Each package is responsible for reporting which valid configurations
# it does not support.  The user should be able to distinguish
# a failure to support a valid configuration from a meaningless
# configuration.

# The goal of this file is to map all the various variations of a given
# machine specification into a single specification in the form:
#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
# or in some cases, the newer four-part form:
#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
# It is wrong to echo any other type of specification.

me=`echo "$0" | sed -e 's,.*/,,'`

usage="\
Usage: $0 [OPTION] CPU-MFR-OPSYS
       $0 [OPTION] ALIAS

Canonicalize a configuration name.

Operation modes:
  -h, --help         print this help, then exit
  -t, --time-stamp   print date of last modification, then exit
  -v, --version      print version number, then exit

Report bugs and patches to <config-patches@gnu.org>."

version="\
GNU config.sub ($timestamp)

Copyright 1992-2014 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

help="
Try \`$me --help' for more information."

# Parse command line
while test $# -gt 0 ; do
  case $1 in
    --time-stamp | --time* | -t )
       echo "$timestamp" ; exit ;;
    --version | -v )
       echo "$version" ; exit ;;
    --help | --h* | -h )
       echo "$usage"; exit ;;
    -- )     # Stop option processing
       shift; break ;;
    - )	# Use stdin as input.
       break ;;
    -* )
       echo "$me: invalid option $1$help"
       exit 1 ;;

    *local*)
       # First pass through any local machine types.
       echo $1
       exit ;;

    * )
       break ;;
  esac
done

case $# in
 0) echo "$me: missing argument$help" >&2
    exit 1;;
 1) ;;
 *) echo "$me: too many arguments$help" >&2
    exit 1;;
esac

# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
# Here we must recognize all the valid KERNEL-OS combinations.
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
case $maybe_os in
  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
  linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
  knetbsd*-gnu* | netbsd*-gnu* | \
  kopensolaris*-gnu* | \
  storm-chaos* | os2-emx* | rtmk-nova*)
    os=-$maybe_os
    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
    ;;
  android-linux)
    os=-linux-android
    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
    ;;
  *)
    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
    if [ $basic_machine != $1 ]
    then os=`echo $1 | sed 's/.*-/-/'`
    else os=; fi
    ;;
esac

### Let's recognize common machines as not being operating systems so
### that things like config.sub decstation-3100 work.  We also
### recognize some manufacturers as not being operating systems, so we
### can provide default operating systems below.
case $os in
	-sun*os*)
		# Prevent following clause from handling this invalid input.
		;;
	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
	-apple | -axis | -knuth | -cray | -microblaze*)
		os=
		basic_machine=$1
		;;
	-bluegene*)
		os=-cnk
		;;
	-sim | -cisco | -oki | -wec | -winbond)
		os=
		basic_machine=$1
		;;
	-scout)
		;;
	-wrs)
		os=-vxworks
		basic_machine=$1
		;;
	-chorusos*)
		os=-chorusos
		basic_machine=$1
		;;
	-chorusrdb)
		os=-chorusrdb
		basic_machine=$1
		;;
	-hiux*)
		os=-hiuxwe2
		;;
	-sco6)
		os=-sco5v6
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-sco5)
		os=-sco3.2v5
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-sco4)
		os=-sco3.2v4
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-sco3.2.[4-9]*)
		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-sco3.2v[4-9]*)
		# Don't forget version if it is 3.2v4 or newer.
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-sco5v6*)
		# Don't forget version if it is 3.2v4 or newer.
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-sco*)
		os=-sco3.2v2
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-udk*)
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-isc)
		os=-isc2.2
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-clix*)
		basic_machine=clipper-intergraph
		;;
	-isc*)
		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
		;;
	-lynx*178)
		os=-lynxos178
		;;
	-lynx*5)
		os=-lynxos5
		;;
	-lynx*)
		os=-lynxos
		;;
	-ptx*)
		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
		;;
	-windowsnt*)
		os=`echo $os | sed -e 's/windowsnt/winnt/'`
		;;
	-psos*)
		os=-psos
		;;
	-mint | -mint[0-9]*)
		basic_machine=m68k-atari
		os=-mint
		;;
esac

# Decode aliases for certain CPU-COMPANY combinations.
case $basic_machine in
	# Recognize the basic CPU types without company name.
	# Some are omitted here because they have special meanings below.
	1750a | 580 \
	| a29k \
	| aarch64 | aarch64_be \
	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
	| am33_2.0 \
	| arc | arceb \
	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
	| avr | avr32 \
	| be32 | be64 \
	| bfin \
	| c4x | c8051 | clipper \
	| d10v | d30v | dlx | dsp16xx \
	| epiphany \
	| fido | fr30 | frv \
	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
	| hexagon \
	| i370 | i860 | i960 | ia64 \
	| ip2k | iq2000 \
	| k1om \
	| le32 | le64 \
	| lm32 \
	| m32c | m32r | m32rle | m68000 | m68k | m88k \
	| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
	| mips | mipsbe | mipseb | mipsel | mipsle \
	| mips16 \
	| mips64 | mips64el \
	| mips64octeon | mips64octeonel \
	| mips64orion | mips64orionel \
	| mips64r5900 | mips64r5900el \
	| mips64vr | mips64vrel \
	| mips64vr4100 | mips64vr4100el \
	| mips64vr4300 | mips64vr4300el \
	| mips64vr5000 | mips64vr5000el \
	| mips64vr5900 | mips64vr5900el \
	| mipsisa32 | mipsisa32el \
	| mipsisa32r2 | mipsisa32r2el \
	| mipsisa32r6 | mipsisa32r6el \
	| mipsisa64 | mipsisa64el \
	| mipsisa64r2 | mipsisa64r2el \
	| mipsisa64r6 | mipsisa64r6el \
	| mipsisa64sb1 | mipsisa64sb1el \
	| mipsisa64sr71k | mipsisa64sr71kel \
	| mipsr5900 | mipsr5900el \
	| mipstx39 | mipstx39el \
	| mn10200 | mn10300 \
	| moxie \
	| mt \
	| msp430 \
	| nds32 | nds32le | nds32be \
	| nios | nios2 | nios2eb | nios2el \
	| ns16k | ns32k \
	| open8 | or1k | or1knd | or32 \
	| pdp10 | pdp11 | pj | pjl \
	| powerpc | powerpc64 | powerpc64le | powerpcle \
	| pyramid \
	| riscv32 | riscv64 \
	| rl78 | rx \
	| score \
	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
	| sh64 | sh64le \
	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
	| spu \
	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
	| ubicom32 \
	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
	| visium \
	| we32k \
	| x86 | xc16x | xstormy16 | xtensa \
	| z8k | z80)
		basic_machine=$basic_machine-unknown
		;;
	c54x)
		basic_machine=tic54x-unknown
		;;
	c55x)
		basic_machine=tic55x-unknown
		;;
	c6x)
		basic_machine=tic6x-unknown
		;;
	leon|leon[3-9])
		basic_machine=sparc-$basic_machine
		;;
	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
		basic_machine=$basic_machine-unknown
		os=-none
		;;
	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
		;;
	ms1)
		basic_machine=mt-unknown
		;;

	strongarm | thumb | xscale)
		basic_machine=arm-unknown
		;;
	xgate)
		basic_machine=$basic_machine-unknown
		os=-none
		;;
	xscaleeb)
		basic_machine=armeb-unknown
		;;

	xscaleel)
		basic_machine=armel-unknown
		;;

	# We use `pc' rather than `unknown'
	# because (1) that's what they normally are, and
	# (2) the word "unknown" tends to confuse beginning users.
	i*86 | x86_64)
	  basic_machine=$basic_machine-pc
	  ;;
	# Object if more than one company name word.
	*-*-*)
		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
		exit 1
		;;
	# Recognize the basic CPU types with company name.
	580-* \
	| a29k-* \
	| aarch64-* | aarch64_be-* \
	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
	| avr-* | avr32-* \
	| be32-* | be64-* \
	| bfin-* | bs2000-* \
	| c[123]* | c30-* | [cjt]90-* | c4x-* \
	| c8051-* | clipper-* | craynv-* | cydra-* \
	| d10v-* | d30v-* | dlx-* \
	| elxsi-* \
	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
	| h8300-* | h8500-* \
	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
	| hexagon-* \
	| i*86-* | i860-* | i960-* | ia64-* \
	| ip2k-* | iq2000-* \
	| k1om-* \
	| le32-* | le64-* \
	| lm32-* \
	| m32c-* | m32r-* | m32rle-* \
	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
	| microblaze-* | microblazeel-* \
	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
	| mips16-* \
	| mips64-* | mips64el-* \
	| mips64octeon-* | mips64octeonel-* \
	| mips64orion-* | mips64orionel-* \
	| mips64r5900-* | mips64r5900el-* \
	| mips64vr-* | mips64vrel-* \
	| mips64vr4100-* | mips64vr4100el-* \
	| mips64vr4300-* | mips64vr4300el-* \
	| mips64vr5000-* | mips64vr5000el-* \
	| mips64vr5900-* | mips64vr5900el-* \
	| mipsisa32-* | mipsisa32el-* \
	| mipsisa32r2-* | mipsisa32r2el-* \
	| mipsisa32r6-* | mipsisa32r6el-* \
	| mipsisa64-* | mipsisa64el-* \
	| mipsisa64r2-* | mipsisa64r2el-* \
	| mipsisa64r6-* | mipsisa64r6el-* \
	| mipsisa64sb1-* | mipsisa64sb1el-* \
	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
	| mipsr5900-* | mipsr5900el-* \
	| mipstx39-* | mipstx39el-* \
	| mmix-* \
	| mt-* \
	| msp430-* \
	| nds32-* | nds32le-* | nds32be-* \
	| nios-* | nios2-* | nios2eb-* | nios2el-* \
	| none-* | np1-* | ns16k-* | ns32k-* \
	| open8-* \
	| or1k*-* \
	| orion-* \
	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
	| pyramid-* \
	| rl78-* | romp-* | rs6000-* | rx-* \
	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
	| sparclite-* \
	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
	| tahoe-* \
	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
	| tile*-* \
	| tron-* \
	| ubicom32-* \
	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
	| vax-* \
	| visium-* \
	| we32k-* \
	| x86-* | x86_64-* | xc16x-* | xps100-* \
	| xstormy16-* | xtensa*-* \
	| ymp-* \
	| z8k-* | z80-*)
		;;
	# Recognize the basic CPU types without company name, with glob match.
	xtensa*)
		basic_machine=$basic_machine-unknown
		;;
	# Recognize the various machine names and aliases which stand
	# for a CPU type and a company and sometimes even an OS.
	386bsd)
		basic_machine=i386-unknown
		os=-bsd
		;;
	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
		basic_machine=m68000-att
		;;
	3b*)
		basic_machine=we32k-att
		;;
	a29khif)
		basic_machine=a29k-amd
		os=-udi
		;;
	abacus)
		basic_machine=abacus-unknown
		;;
	adobe68k)
		basic_machine=m68010-adobe
		os=-scout
		;;
	alliant | fx80)
		basic_machine=fx80-alliant
		;;
	altos | altos3068)
		basic_machine=m68k-altos
		;;
	am29k)
		basic_machine=a29k-none
		os=-bsd
		;;
	amd64)
		basic_machine=x86_64-pc
		;;
	amd64-*)
		basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	amdahl)
		basic_machine=580-amdahl
		os=-sysv
		;;
	amiga | amiga-*)
		basic_machine=m68k-unknown
		;;
	amigaos | amigados)
		basic_machine=m68k-unknown
		os=-amigaos
		;;
	amigaunix | amix)
		basic_machine=m68k-unknown
		os=-sysv4
		;;
	apollo68)
		basic_machine=m68k-apollo
		os=-sysv
		;;
	apollo68bsd)
		basic_machine=m68k-apollo
		os=-bsd
		;;
	aros)
		basic_machine=i386-pc
		os=-aros
		;;
	aux)
		basic_machine=m68k-apple
		os=-aux
		;;
	balance)
		basic_machine=ns32k-sequent
		os=-dynix
		;;
	blackfin)
		basic_machine=bfin-unknown
		os=-linux
		;;
	blackfin-*)
		basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
		os=-linux
		;;
	bluegene*)
		basic_machine=powerpc-ibm
		os=-cnk
		;;
	c54x-*)
		basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	c55x-*)
		basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	c6x-*)
		basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	c90)
		basic_machine=c90-cray
		os=-unicos
		;;
	cegcc)
		basic_machine=arm-unknown
		os=-cegcc
		;;
	convex-c1)
		basic_machine=c1-convex
		os=-bsd
		;;
	convex-c2)
		basic_machine=c2-convex
		os=-bsd
		;;
	convex-c32)
		basic_machine=c32-convex
		os=-bsd
		;;
	convex-c34)
		basic_machine=c34-convex
		os=-bsd
		;;
	convex-c38)
		basic_machine=c38-convex
		os=-bsd
		;;
	cray | j90)
		basic_machine=j90-cray
		os=-unicos
		;;
	craynv)
		basic_machine=craynv-cray
		os=-unicosmp
		;;
	cr16 | cr16-*)
		basic_machine=cr16-unknown
		os=-elf
		;;
	crds | unos)
		basic_machine=m68k-crds
		;;
	crisv32 | crisv32-* | etraxfs*)
		basic_machine=crisv32-axis
		;;
	cris | cris-* | etrax*)
		basic_machine=cris-axis
		;;
	crx)
		basic_machine=crx-unknown
		os=-elf
		;;
	da30 | da30-*)
		basic_machine=m68k-da30
		;;
	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
		basic_machine=mips-dec
		;;
	decsystem10* | dec10*)
		basic_machine=pdp10-dec
		os=-tops10
		;;
	decsystem20* | dec20*)
		basic_machine=pdp10-dec
		os=-tops20
		;;
	delta | 3300 | motorola-3300 | motorola-delta \
	      | 3300-motorola | delta-motorola)
		basic_machine=m68k-motorola
		;;
	delta88)
		basic_machine=m88k-motorola
		os=-sysv3
		;;
	dicos)
		basic_machine=i686-pc
		os=-dicos
		;;
	djgpp)
		basic_machine=i586-pc
		os=-msdosdjgpp
		;;
	dpx20 | dpx20-*)
		basic_machine=rs6000-bull
		os=-bosx
		;;
	dpx2* | dpx2*-bull)
		basic_machine=m68k-bull
		os=-sysv3
		;;
	ebmon29k)
		basic_machine=a29k-amd
		os=-ebmon
		;;
	elxsi)
		basic_machine=elxsi-elxsi
		os=-bsd
		;;
	encore | umax | mmax)
		basic_machine=ns32k-encore
		;;
	es1800 | OSE68k | ose68k | ose | OSE)
		basic_machine=m68k-ericsson
		os=-ose
		;;
	fx2800)
		basic_machine=i860-alliant
		;;
	genix)
		basic_machine=ns32k-ns
		;;
	gmicro)
		basic_machine=tron-gmicro
		os=-sysv
		;;
	go32)
		basic_machine=i386-pc
		os=-go32
		;;
	h3050r* | hiux*)
		basic_machine=hppa1.1-hitachi
		os=-hiuxwe2
		;;
	h8300hms)
		basic_machine=h8300-hitachi
		os=-hms
		;;
	h8300xray)
		basic_machine=h8300-hitachi
		os=-xray
		;;
	h8500hms)
		basic_machine=h8500-hitachi
		os=-hms
		;;
	harris)
		basic_machine=m88k-harris
		os=-sysv3
		;;
	hp300-*)
		basic_machine=m68k-hp
		;;
	hp300bsd)
		basic_machine=m68k-hp
		os=-bsd
		;;
	hp300hpux)
		basic_machine=m68k-hp
		os=-hpux
		;;
	hp3k9[0-9][0-9] | hp9[0-9][0-9])
		basic_machine=hppa1.0-hp
		;;
	hp9k2[0-9][0-9] | hp9k31[0-9])
		basic_machine=m68000-hp
		;;
	hp9k3[2-9][0-9])
		basic_machine=m68k-hp
		;;
	hp9k6[0-9][0-9] | hp6[0-9][0-9])
		basic_machine=hppa1.0-hp
		;;
	hp9k7[0-79][0-9] | hp7[0-79][0-9])
		basic_machine=hppa1.1-hp
		;;
	hp9k78[0-9] | hp78[0-9])
		# FIXME: really hppa2.0-hp
		basic_machine=hppa1.1-hp
		;;
	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
		# FIXME: really hppa2.0-hp
		basic_machine=hppa1.1-hp
		;;
	hp9k8[0-9][13679] | hp8[0-9][13679])
		basic_machine=hppa1.1-hp
		;;
	hp9k8[0-9][0-9] | hp8[0-9][0-9])
		basic_machine=hppa1.0-hp
		;;
	hppa-next)
		os=-nextstep3
		;;
	hppaosf)
		basic_machine=hppa1.1-hp
		os=-osf
		;;
	hppro)
		basic_machine=hppa1.1-hp
		os=-proelf
		;;
	i370-ibm* | ibm*)
		basic_machine=i370-ibm
		;;
	i*86v32)
		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
		os=-sysv32
		;;
	i*86v4*)
		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
		os=-sysv4
		;;
	i*86v)
		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
		os=-sysv
		;;
	i*86sol2)
		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
		os=-solaris2
		;;
	i386mach)
		basic_machine=i386-mach
		os=-mach
		;;
	i386-vsta | vsta)
		basic_machine=i386-unknown
		os=-vsta
		;;
	iris | iris4d)
		basic_machine=mips-sgi
		case $os in
		    -irix*)
			;;
		    *)
			os=-irix4
			;;
		esac
		;;
	isi68 | isi)
		basic_machine=m68k-isi
		os=-sysv
		;;
	leon-*|leon[3-9]-*)
		basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
		;;
	m68knommu)
		basic_machine=m68k-unknown
		os=-linux
		;;
	m68knommu-*)
		basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
		os=-linux
		;;
	m88k-omron*)
		basic_machine=m88k-omron
		;;
	magnum | m3230)
		basic_machine=mips-mips
		os=-sysv
		;;
	merlin)
		basic_machine=ns32k-utek
		os=-sysv
		;;
	microblaze*)
		basic_machine=microblaze-xilinx
		;;
	mingw64)
		basic_machine=x86_64-pc
		os=-mingw64
		;;
	mingw32)
		basic_machine=i686-pc
		os=-mingw32
		;;
	mingw32ce)
		basic_machine=arm-unknown
		os=-mingw32ce
		;;
	miniframe)
		basic_machine=m68000-convergent
		;;
	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
		basic_machine=m68k-atari
		os=-mint
		;;
	mips3*-*)
		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
		;;
	mips3*)
		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
		;;
	monitor)
		basic_machine=m68k-rom68k
		os=-coff
		;;
	morphos)
		basic_machine=powerpc-unknown
		os=-morphos
		;;
	moxiebox)
		basic_machine=moxie-unknown
		os=-moxiebox
		;;
	msdos)
		basic_machine=i386-pc
		os=-msdos
		;;
	ms1-*)
		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
		;;
	msys)
		basic_machine=i686-pc
		os=-msys
		;;
	mvs)
		basic_machine=i370-ibm
		os=-mvs
		;;
	nacl)
		basic_machine=le32-unknown
		os=-nacl
		;;
	ncr3000)
		basic_machine=i486-ncr
		os=-sysv4
		;;
	netbsd386)
		basic_machine=i386-unknown
		os=-netbsd
		;;
	netwinder)
		basic_machine=armv4l-rebel
		os=-linux
		;;
	news | news700 | news800 | news900)
		basic_machine=m68k-sony
		os=-newsos
		;;
	news1000)
		basic_machine=m68030-sony
		os=-newsos
		;;
	news-3600 | risc-news)
		basic_machine=mips-sony
		os=-newsos
		;;
	necv70)
		basic_machine=v70-nec
		os=-sysv
		;;
	next | m*-next )
		basic_machine=m68k-next
		case $os in
		    -nextstep* )
			;;
		    -ns2*)
		      os=-nextstep2
			;;
		    *)
		      os=-nextstep3
			;;
		esac
		;;
	nh3000)
		basic_machine=m68k-harris
		os=-cxux
		;;
	nh[45]000)
		basic_machine=m88k-harris
		os=-cxux
		;;
	nindy960)
		basic_machine=i960-intel
		os=-nindy
		;;
	mon960)
		basic_machine=i960-intel
		os=-mon960
		;;
	nonstopux)
		basic_machine=mips-compaq
		os=-nonstopux
		;;
	np1)
		basic_machine=np1-gould
		;;
	neo-tandem)
		basic_machine=neo-tandem
		;;
	nse-tandem)
		basic_machine=nse-tandem
		;;
	nsr-tandem)
		basic_machine=nsr-tandem
		;;
	op50n-* | op60c-*)
		basic_machine=hppa1.1-oki
		os=-proelf
		;;
	openrisc | openrisc-*)
		basic_machine=or32-unknown
		;;
	os400)
		basic_machine=powerpc-ibm
		os=-os400
		;;
	OSE68000 | ose68000)
		basic_machine=m68000-ericsson
		os=-ose
		;;
	os68k)
		basic_machine=m68k-none
		os=-os68k
		;;
	pa-hitachi)
		basic_machine=hppa1.1-hitachi
		os=-hiuxwe2
		;;
	paragon)
		basic_machine=i860-intel
		os=-osf
		;;
	parisc)
		basic_machine=hppa-unknown
		os=-linux
		;;
	parisc-*)
		basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
		os=-linux
		;;
	pbd)
		basic_machine=sparc-tti
		;;
	pbb)
		basic_machine=m68k-tti
		;;
	pc532 | pc532-*)
		basic_machine=ns32k-pc532
		;;
	pc98)
		basic_machine=i386-pc
		;;
	pc98-*)
		basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	pentium | p5 | k5 | k6 | nexgen | viac3)
		basic_machine=i586-pc
		;;
	pentiumpro | p6 | 6x86 | athlon | athlon_*)
		basic_machine=i686-pc
		;;
	pentiumii | pentium2 | pentiumiii | pentium3)
		basic_machine=i686-pc
		;;
	pentium4)
		basic_machine=i786-pc
		;;
	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	pentiumpro-* | p6-* | 6x86-* | athlon-*)
		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	pentium4-*)
		basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	pn)
		basic_machine=pn-gould
		;;
	power)	basic_machine=power-ibm
		;;
	ppc | ppcbe)	basic_machine=powerpc-unknown
		;;
	ppc-* | ppcbe-*)
		basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	ppcle | powerpclittle | ppc-le | powerpc-little)
		basic_machine=powerpcle-unknown
		;;
	ppcle-* | powerpclittle-*)
		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	ppc64)	basic_machine=powerpc64-unknown
		;;
	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
		basic_machine=powerpc64le-unknown
		;;
	ppc64le-* | powerpc64little-*)
		basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	ps2)
		basic_machine=i386-ibm
		;;
	pw32)
		basic_machine=i586-unknown
		os=-pw32
		;;
	rdos | rdos64)
		basic_machine=x86_64-pc
		os=-rdos
		;;
	rdos32)
		basic_machine=i386-pc
		os=-rdos
		;;
	rom68k)
		basic_machine=m68k-rom68k
		os=-coff
		;;
	rm[46]00)
		basic_machine=mips-siemens
		;;
	rtpc | rtpc-*)
		basic_machine=romp-ibm
		;;
	s390 | s390-*)
		basic_machine=s390-ibm
		;;
	s390x | s390x-*)
		basic_machine=s390x-ibm
		;;
	sa29200)
		basic_machine=a29k-amd
		os=-udi
		;;
	sb1)
		basic_machine=mipsisa64sb1-unknown
		;;
	sb1el)
		basic_machine=mipsisa64sb1el-unknown
		;;
	sde)
		basic_machine=mipsisa32-sde
		os=-elf
		;;
	sei)
		basic_machine=mips-sei
		os=-seiux
		;;
	sequent)
		basic_machine=i386-sequent
		;;
	sh)
		basic_machine=sh-hitachi
		os=-hms
		;;
	sh5el)
		basic_machine=sh5le-unknown
		;;
	sh64)
		basic_machine=sh64-unknown
		;;
	sparclite-wrs | simso-wrs)
		basic_machine=sparclite-wrs
		os=-vxworks
		;;
	sps7)
		basic_machine=m68k-bull
		os=-sysv2
		;;
	spur)
		basic_machine=spur-unknown
		;;
	st2000)
		basic_machine=m68k-tandem
		;;
	stratus)
		basic_machine=i860-stratus
		os=-sysv4
		;;
	strongarm-* | thumb-*)
		basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
		;;
	sun2)
		basic_machine=m68000-sun
		;;
	sun2os3)
		basic_machine=m68000-sun
		os=-sunos3
		;;
	sun2os4)
		basic_machine=m68000-sun
		os=-sunos4
		;;
	sun3os3)
		basic_machine=m68k-sun
		os=-sunos3
		;;
	sun3os4)
		basic_machine=m68k-sun
		os=-sunos4
		;;
	sun4os3)
		basic_machine=sparc-sun
		os=-sunos3
		;;
	sun4os4)
		basic_machine=sparc-sun
		os=-sunos4
		;;
	sun4sol2)
		basic_machine=sparc-sun
		os=-solaris2
		;;
	sun3 | sun3-*)
		basic_machine=m68k-sun
		;;
	sun4)
		basic_machine=sparc-sun
		;;
	sun386 | sun386i | roadrunner)
		basic_machine=i386-sun
		;;
	sv1)
		basic_machine=sv1-cray
		os=-unicos
		;;
	symmetry)
		basic_machine=i386-sequent
		os=-dynix
		;;
	t3e)
		basic_machine=alphaev5-cray
		os=-unicos
		;;
	t90)
		basic_machine=t90-cray
		os=-unicos
		;;
	tile*)
		basic_machine=$basic_machine-unknown
		os=-linux-gnu
		;;
	tx39)
		basic_machine=mipstx39-unknown
		;;
	tx39el)
		basic_machine=mipstx39el-unknown
		;;
	toad1)
		basic_machine=pdp10-xkl
		os=-tops20
		;;
	tower | tower-32)
		basic_machine=m68k-ncr
		;;
	tpf)
		basic_machine=s390x-ibm
		os=-tpf
		;;
	udi29k)
		basic_machine=a29k-amd
		os=-udi
		;;
	ultra3)
		basic_machine=a29k-nyu
		os=-sym1
		;;
	v810 | necv810)
		basic_machine=v810-nec
		os=-none
		;;
	vaxv)
		basic_machine=vax-dec
		os=-sysv
		;;
	vms)
		basic_machine=vax-dec
		os=-vms
		;;
	vpp*|vx|vx-*)
		basic_machine=f301-fujitsu
		;;
	vxworks960)
		basic_machine=i960-wrs
		os=-vxworks
		;;
	vxworks68)
		basic_machine=m68k-wrs
		os=-vxworks
		;;
	vxworks29k)
		basic_machine=a29k-wrs
		os=-vxworks
		;;
	w65*)
		basic_machine=w65-wdc
		os=-none
		;;
	w89k-*)
		basic_machine=hppa1.1-winbond
		os=-proelf
		;;
	xbox)
		basic_machine=i686-pc
		os=-mingw32
		;;
	xps | xps100)
		basic_machine=xps100-honeywell
		;;
	xscale-* | xscalee[bl]-*)
		basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
		;;
	ymp)
		basic_machine=ymp-cray
		os=-unicos
		;;
	z8k-*-coff)
		basic_machine=z8k-unknown
		os=-sim
		;;
	z80-*-coff)
		basic_machine=z80-unknown
		os=-sim
		;;
	none)
		basic_machine=none-none
		os=-none
		;;

# Here we handle the default manufacturer of certain CPU types.  It is in
# some cases the only manufacturer, in others, it is the most popular.
	w89k)
		basic_machine=hppa1.1-winbond
		;;
	op50n)
		basic_machine=hppa1.1-oki
		;;
	op60c)
		basic_machine=hppa1.1-oki
		;;
	romp)
		basic_machine=romp-ibm
		;;
	mmix)
		basic_machine=mmix-knuth
		;;
	rs6000)
		basic_machine=rs6000-ibm
		;;
	vax)
		basic_machine=vax-dec
		;;
	pdp10)
		# there are many clones, so DEC is not a safe bet
		basic_machine=pdp10-unknown
		;;
	pdp11)
		basic_machine=pdp11-dec
		;;
	we32k)
		basic_machine=we32k-att
		;;
	sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
		basic_machine=sh-unknown
		;;
	sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
		basic_machine=sparc-sun
		;;
	cydra)
		basic_machine=cydra-cydrome
		;;
	orion)
		basic_machine=orion-highlevel
		;;
	orion105)
		basic_machine=clipper-highlevel
		;;
	mac | mpw | mac-mpw)
		basic_machine=m68k-apple
		;;
	pmac | pmac-mpw)
		basic_machine=powerpc-apple
		;;
	*-unknown)
		# Make sure to match an already-canonicalized machine name.
		;;
	*)
		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
		exit 1
		;;
esac

# Here we canonicalize certain aliases for manufacturers.
case $basic_machine in
	*-digital*)
		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
		;;
	*-commodore*)
		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
		;;
	*)
		;;
esac

# Decode manufacturer-specific aliases for certain operating systems.

if [ x"$os" != x"" ]
then
case $os in
	# First match some system type aliases
	# that might get confused with valid system types.
	# -solaris* is a basic system type, with this one exception.
	-auroraux)
		os=-auroraux
		;;
	-solaris1 | -solaris1.*)
		os=`echo $os | sed -e 's|solaris1|sunos4|'`
		;;
	-solaris)
		os=-solaris2
		;;
	-svr4*)
		os=-sysv4
		;;
	-unixware*)
		os=-sysv4.2uw
		;;
	-gnu/linux*)
		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
		;;
	# First accept the basic system types.
	# The portable systems comes first.
	# Each alternative MUST END IN A *, to match a version number.
	# -sysv* is not here because it comes later, after sysvr4.
	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
	      | -sym* | -kopensolaris* | -plan9* \
	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
	      | -aos* | -aros* \
	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
	      | -bitrig* | -openbsd* | -solidbsd* \
	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
	      | -chorusos* | -chorusrdb* | -cegcc* \
	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
	# Remember, each alternative MUST END IN *, to match a version number.
		;;
	-qnx*)
		case $basic_machine in
		    x86-* | i*86-*)
			;;
		    *)
			os=-nto$os
			;;
		esac
		;;
	-nto-qnx*)
		;;
	-nto*)
		os=`echo $os | sed -e 's|nto|nto-qnx|'`
		;;
	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
		;;
	-mac*)
		os=`echo $os | sed -e 's|mac|macos|'`
		;;
	-linux-dietlibc)
		os=-linux-dietlibc
		;;
	-linux*)
		os=`echo $os | sed -e 's|linux|linux-gnu|'`
		;;
	-sunos5*)
		os=`echo $os | sed -e 's|sunos5|solaris2|'`
		;;
	-sunos6*)
		os=`echo $os | sed -e 's|sunos6|solaris3|'`
		;;
	-opened*)
		os=-openedition
		;;
	-os400*)
		os=-os400
		;;
	-wince*)
		os=-wince
		;;
	-osfrose*)
		os=-osfrose
		;;
	-osf*)
		os=-osf
		;;
	-utek*)
		os=-bsd
		;;
	-dynix*)
		os=-bsd
		;;
	-acis*)
		os=-aos
		;;
	-atheos*)
		os=-atheos
		;;
	-syllable*)
		os=-syllable
		;;
	-386bsd)
		os=-bsd
		;;
	-ctix* | -uts*)
		os=-sysv
		;;
	-nova*)
		os=-rtmk-nova
		;;
	-ns2 )
		os=-nextstep2
		;;
	-nsk*)
		os=-nsk
		;;
	# Preserve the version number of sinix5.
	-sinix5.*)
		os=`echo $os | sed -e 's|sinix|sysv|'`
		;;
	-sinix*)
		os=-sysv4
		;;
	-tpf*)
		os=-tpf
		;;
	-triton*)
		os=-sysv3
		;;
	-oss*)
		os=-sysv3
		;;
	-svr4)
		os=-sysv4
		;;
	-svr3)
		os=-sysv3
		;;
	-sysvr4)
		os=-sysv4
		;;
	# This must come after -sysvr4.
	-sysv*)
		;;
	-ose*)
		os=-ose
		;;
	-es1800*)
		os=-ose
		;;
	-xenix)
		os=-xenix
		;;
	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
		os=-mint
		;;
	-aros*)
		os=-aros
		;;
	-zvmoe)
		os=-zvmoe
		;;
	-dicos*)
		os=-dicos
		;;
	-nacl*)
		;;
	-none)
		;;
	*)
		# Get rid of the `-' at the beginning of $os.
		os=`echo $os | sed 's/[^-]*-//'`
		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
		exit 1
		;;
esac
else

# Here we handle the default operating systems that come with various machines.
# The value should be what the vendor currently ships out the door with their
# machine or put another way, the most popular os provided with the machine.

# Note that if you're going to try to match "-MANUFACTURER" here (say,
# "-sun"), then you have to tell the case statement up towards the top
# that MANUFACTURER isn't an operating system.  Otherwise, code above
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.

case $basic_machine in
	score-*)
		os=-elf
		;;
	spu-*)
		os=-elf
		;;
	*-acorn)
		os=-riscix1.2
		;;
	arm*-rebel)
		os=-linux
		;;
	arm*-semi)
		os=-aout
		;;
	c4x-* | tic4x-*)
		os=-coff
		;;
	c8051-*)
		os=-elf
		;;
	hexagon-*)
		os=-elf
		;;
	tic54x-*)
		os=-coff
		;;
	tic55x-*)
		os=-coff
		;;
	tic6x-*)
		os=-coff
		;;
	# This must come before the *-dec entry.
	pdp10-*)
		os=-tops20
		;;
	pdp11-*)
		os=-none
		;;
	*-dec | vax-*)
		os=-ultrix4.2
		;;
	m68*-apollo)
		os=-domain
		;;
	i386-sun)
		os=-sunos4.0.2
		;;
	m68000-sun)
		os=-sunos3
		;;
	m68*-cisco)
		os=-aout
		;;
	mep-*)
		os=-elf
		;;
	mips*-cisco)
		os=-elf
		;;
	mips*-*)
		os=-elf
		;;
	or32-*)
		os=-coff
		;;
	*-tti)	# must be before sparc entry or we get the wrong os.
		os=-sysv3
		;;
	sparc-* | *-sun)
		os=-sunos4.1.1
		;;
	*-be)
		os=-beos
		;;
	*-haiku)
		os=-haiku
		;;
	*-ibm)
		os=-aix
		;;
	*-knuth)
		os=-mmixware
		;;
	*-wec)
		os=-proelf
		;;
	*-winbond)
		os=-proelf
		;;
	*-oki)
		os=-proelf
		;;
	*-hp)
		os=-hpux
		;;
	*-hitachi)
		os=-hiux
		;;
	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
		os=-sysv
		;;
	*-cbm)
		os=-amigaos
		;;
	*-dg)
		os=-dgux
		;;
	*-dolphin)
		os=-sysv3
		;;
	m68k-ccur)
		os=-rtu
		;;
	m88k-omron*)
		os=-luna
		;;
	*-next )
		os=-nextstep
		;;
	*-sequent)
		os=-ptx
		;;
	*-crds)
		os=-unos
		;;
	*-ns)
		os=-genix
		;;
	i370-*)
		os=-mvs
		;;
	*-next)
		os=-nextstep3
		;;
	*-gould)
		os=-sysv
		;;
	*-highlevel)
		os=-bsd
		;;
	*-encore)
		os=-bsd
		;;
	*-sgi)
		os=-irix
		;;
	*-siemens)
		os=-sysv4
		;;
	*-masscomp)
		os=-rtu
		;;
	f30[01]-fujitsu | f700-fujitsu)
		os=-uxpv
		;;
	*-rom68k)
		os=-coff
		;;
	*-*bug)
		os=-coff
		;;
	*-apple)
		os=-macos
		;;
	*-atari*)
		os=-mint
		;;
	*)
		os=-none
		;;
esac
fi

# Here we handle the case where we know the os, and the CPU type, but not the
# manufacturer.  We pick the logical manufacturer.
vendor=unknown
case $basic_machine in
	*-unknown)
		case $os in
			-riscix*)
				vendor=acorn
				;;
			-sunos*)
				vendor=sun
				;;
			-cnk*|-aix*)
				vendor=ibm
				;;
			-beos*)
				vendor=be
				;;
			-hpux*)
				vendor=hp
				;;
			-mpeix*)
				vendor=hp
				;;
			-hiux*)
				vendor=hitachi
				;;
			-unos*)
				vendor=crds
				;;
			-dgux*)
				vendor=dg
				;;
			-luna*)
				vendor=omron
				;;
			-genix*)
				vendor=ns
				;;
			-mvs* | -opened*)
				vendor=ibm
				;;
			-os400*)
				vendor=ibm
				;;
			-ptx*)
				vendor=sequent
				;;
			-tpf*)
				vendor=ibm
				;;
			-vxsim* | -vxworks* | -windiss*)
				vendor=wrs
				;;
			-aux*)
				vendor=apple
				;;
			-hms*)
				vendor=hitachi
				;;
			-mpw* | -macos*)
				vendor=apple
				;;
			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
				vendor=atari
				;;
			-vos*)
				vendor=stratus
				;;
		esac
		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
		;;
esac

echo $basic_machine$os
exit

# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
Added autosetup/default.auto.

























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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Copyright (c) 2012 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Auto-load module for 'make' build system integration

use init

autosetup_add_init_type make {Simple "make" build system} {
	autosetup_check_create auto.def \
{# Initial auto.def created by 'autosetup --init=make'

use cc

# Add any user options here
options {
}

make-config-header config.h
make-template Makefile.in
}

	if {![file exists Makefile.in]} {
		puts "Note: I don't see Makefile.in. You will probably need to create one."
	}
}
Added autosetup/find-tclsh.
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/sh
# Looks for a suitable tclsh or jimsh in the PATH
# If not found, builds a bootstrap jimsh from source
d=`dirname "$0"`
{ "$d/jimsh0" "$d/test-tclsh"; } 2>/dev/null && exit 0
PATH="$PATH:$d"; export PATH
for tclsh in jimsh tclsh tclsh8.5 tclsh8.6; do
	{ $tclsh "$d/test-tclsh"; } 2>/dev/null && exit 0
done
echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0"
for cc in ${CC_FOR_BUILD:-cc} gcc; do
	{ $cc -o "$d/jimsh0" "$d/jimsh0.c"; } 2>/dev/null || continue
	"$d/jimsh0" "$d/test-tclsh" && exit 0
done
echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc."
echo false
Added autosetup/jimsh0.c.

more than 10,000 changes

Added autosetup/pkg-config.tcl.






































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Copyright (c) 2016 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# @synopsis:
#
# The 'pkg-config' module allows package information to be found via pkg-config
#
# If not cross-compiling, the package path should be determined automatically
# by pkg-config.
# If cross-compiling, the default package path is the compiler sysroot.
# If the C compiler doesn't support -print-sysroot, the path can be supplied
# by the --sysroot option or by defining SYSROOT.
#
# PKG_CONFIG may be set to use an alternative to pkg-config

use cc

module-options {
	sysroot:dir => "Override compiler sysroot for pkg-config search path"
}

# @pkg-config-init ?required?
#
# Initialises the pkg-config system. Unless required is set to 0,
# it is a fatal error if the pkg-config
# This command will normally be called automatically as required,
# but it may be invoked explicitly if lack of pkg-config is acceptable.
#
# Returns 1 if ok, or 0 if pkg-config not found/usable (only if required=0)
#
proc pkg-config-init {{required 1}} {
	if {[is-defined HAVE_PKG_CONFIG]} {
		return [get-define HAVE_PKG_CONFIG]
	}
	set found 0

	define PKG_CONFIG [get-env PKG_CONFIG pkg-config]
	msg-checking "Checking for pkg-config..."

	if {[catch {exec [get-define PKG_CONFIG] --version} version]} {
		msg-result "[get-define PKG_CONFIG] (not found)"
		if {$required} {
			user-error "No usable pkg-config"
		}
	} else {
		msg-result $version
		define PKG_CONFIG_VERSION $version

		set found 1

		if {[opt-val sysroot] ne ""} {
			define SYSROOT [file-normalize [opt-val sysroot]]
			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 {
				set msg "pkg-config: Cross compiling, but no compiler sysroot and no --sysroot supplied"
				if {$required} {
					user-error $msg
				} else {
					msg-result $msg
				}
				set found 0
			}
		}
		if {[is-defined SYSROOT]} {
			set sysroot [get-define SYSROOT]

			# XXX: It's possible that these should be set only when invoking pkg-config
			global env
			set env(PKG_CONFIG_DIR) ""
			# Do we need to try /usr/local as well or instead?
			set env(PKG_CONFIG_LIBDIR) $sysroot/usr/lib/pkgconfig:$sysroot/usr/share/pkgconfig
			set env(PKG_CONFIG_SYSROOT_DIR) $sysroot
		}
	}
	define HAVE_PKG_CONFIG $found
	return $found
}

# @pkg-config module ?requirements?
#
# Use pkg-config to find the given module meeting the given requirements.
# e.g.
#
## pkg-config pango >= 1.37.0
#
# If found, returns 1 and sets HAVE_PKG_PANGO to 1 along with:
#
## PKG_PANGO_VERSION to the found version
## PKG_PANGO_LIBS to the required libs (--libs-only-l)
## PKG_PANGO_LDFLAGS to the required linker flags (--libs-only-L)
## PKG_PANGO_CFLAGS to the required compiler flags (--cflags)
#
# If not found, returns 0.
#
proc pkg-config {module args} {
	set ok [pkg-config-init]

	msg-checking "Checking for $module $args..."

	if {!$ok} {
		msg-result "no pkg-config"
		return 0
	}

	if {[catch {exec [get-define PKG_CONFIG] --modversion "$module $args"} version]} {
		msg-result "not found"
		configlog "pkg-config --modversion $module $args: $version"
		return 0
	}
	msg-result $version
	set prefix [feature-define-name $module PKG_]
	define HAVE_${prefix}
	define ${prefix}_VERSION $version
	define ${prefix}_LIBS [exec pkg-config --libs-only-l $module]
	define ${prefix}_LDFLAGS [exec pkg-config --libs-only-L $module]
	define ${prefix}_CFLAGS [exec pkg-config --cflags $module]
	return 1
}

# @pkg-config-get module setting
#
# Convenience access to the results of pkg-config
#
# For example, [pkg-config-get pango CFLAGS] returns
# the value of PKG_PANGO_CFLAGS, or "" if not defined.
proc pkg-config-get {module name} {
	set prefix [feature-define-name $module PKG_]
	get-define ${prefix}_${name} ""
}
Added autosetup/system.tcl.



























































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# @synopsis:
#
# This module supports common system interrogation and options
# such as --host, --build, --prefix, and setting srcdir, builddir, and EXEEXT
#
# It also support the 'feature' naming convention, where searching
# for a feature such as sys/type.h defines HAVE_SYS_TYPES_H
#
# It defines the following variables, based on --prefix unless overridden by the user:
#
## datadir
## sysconfdir
## sharedstatedir
## localstatedir
## infodir
## mandir
## includedir

# Do "define defaultprefix myvalue" to set the default prefix *before* the first "use"
set defaultprefix [get-define defaultprefix /usr/local]

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

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

# Returns 1 if exists, or 0 if  not
#
proc check-feature {name code} {
	msg-checking "Checking for $name..."
	set r [uplevel 1 $code]
	define-feature $name $r
	if {$r} {
		msg-result "ok"
	} else {
		msg-result "not found"
	}
	return $r
}

# @have-feature name ?default=0?
#
# Returns the value of the feature if defined, or $default if not.
# See 'feature-define-name' for how the feature name
# is translated into the define name.
#
proc have-feature {name {default 0}} {
	get-define [feature-define-name $name] $default
}

# @define-feature name ?value=1?
#
# Sets the feature 'define' to the given value.
# See 'feature-define-name' for how the feature name
# is translated into the define name.
#
proc define-feature {name {value 1}} {
	define [feature-define-name $name] $value
}

# @feature-checked name
#
# Returns 1 if the feature has been checked, whether true or not
#
proc feature-checked {name} {
	is-defined [feature-define-name $name]
}

# @feature-define-name name ?prefix=HAVE_?
#
# Converts a name to the corresponding define,
# e.g. sys/stat.h becomes HAVE_SYS_STAT_H.
#
# Converts * to P and all non-alphanumeric to underscore.
#
proc feature-define-name {name {prefix HAVE_}} {
	string toupper $prefix[regsub -all {[^a-zA-Z0-9]} [regsub -all {[*]} $name p] _]
}

# If $file doesn't exist, or it's contents are different than $buf,
# the file is written and $script is executed.
# Otherwise a "file is unchanged" message is displayed.
proc write-if-changed {file buf {script {}}} {
	set old [readfile $file ""]
	if {$old eq $buf && [file exists $file]} {
		msg-result "$file is unchanged"
	} else {
		writefile $file $buf\n
		uplevel 1 $script
	}
}

# @make-template template ?outfile?
#
# Reads the input file <srcdir>/$template and writes the output file $outfile.
# 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
# path to the source directory from the directory where the output
# file is created, while the special value @top_srcdir@ is substituted
# with the relative path to the top level source directory.
#
# Conditional sections may be specified as follows:
## @if name == value
## lines
## @else
## lines
## @endif
#
# Where 'name' is a defined variable name and @else is optional.
# If the expression does not match, all lines through '@endif' are ignored.
#
# The alternative forms may also be used:
## @if name
## @if name != value
#
# Where the first form is true if the variable is defined, but not empty or 0
#
# Currently these expressions can't be nested.
#
proc make-template {template {out {}}} {
	set infile [file join $::autosetup(srcdir) $template]

	if {![file exists $infile]} {
		user-error "Template $template is missing"
	}

	# Define this as late as possible
	define AUTODEPS $::autosetup(deps)

	if {$out eq ""} {
		if {[file ext $template] ne ".in"} {
			autosetup-error "make_template $template has no target file and can't guess"
		}
		set out [file rootname $template]
	}

	set outdir [file dirname $out]

	# Make sure the directory exists
	file mkdir $outdir

	# Set up srcdir and top_srcdir to be relative to the target dir
	define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir]
	define top_srcdir [relative-path $::autosetup(srcdir) $outdir]

	set mapping {}
	foreach {n v} [array get ::define] {
		lappend mapping @$n@ $v
	}
	set result {}
	foreach line [split [readfile $infile] \n] {
		if {[info exists cond]} {
			set l [string trimright $line]
			if {$l eq "@endif"} {
				unset cond
				continue
			}
			if {$l eq "@else"} {
				set cond [expr {!$cond}]
				continue
			}
			if {$cond} {
				lappend result $line
			}
			continue
		}
		if {[regexp {^@if\s+(\w+)(.*)} $line -> name expression]} {
			lassign $expression equal value
			set varval [get-define $name ""]
			if {$equal eq ""} {
				set cond [expr {$varval ni {"" 0}}]
			} else {
				set cond [expr {$varval eq $value}]
				if {$equal ne "=="} {
					set cond [expr {!$cond}]
				}
			}
			continue
		}
		lappend result $line
	}
	writefile $out [string map $mapping [join $result \n]]\n

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

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

set host [opt-val host]
define host_alias $host
if {$host eq ""} {
	define host [get-define build]
	set cross ""
} else {
	define host [config_sub $host]
	set cross $host-
}
define cross [get-env CROSS $cross]

set prefix [opt-val prefix $defaultprefix]

# These are for compatibility with autoconf
define target [get-define host]
define prefix $prefix
define builddir $autosetup(builddir)
define srcdir $autosetup(srcdir)
# Allow this to come from the environment
define top_srcdir [get-env top_srcdir [get-define srcdir]]

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

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

# Windows vs. non-Windows
switch -glob -- [get-define host] {
	*-*-ming* - *-*-cygwin - *-*-msys {
		define-feature windows
		define EXEEXT .exe
	}
	default {
		define EXEEXT ""
	}
}

# Display
msg-result "Host System...[get-define host]"
msg-result "Build System...[get-define build]"
Added autosetup/test-tclsh.




















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# A small Tcl script to verify that the chosen
# interpreter works. Sometimes we might e.g. pick up
# an interpreter for a different arch.
# Outputs the full path to the interpreter

if {[catch {info version} version] == 0} {
	# This is Jim Tcl
	if {$version >= 0.72} {
		# Ensure that regexp works
		regexp (a.*?) a
		puts [info nameofexecutable]
		exit 0
	}
} elseif {[catch {info tclversion} version] == 0} {
	if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} {
		puts [info nameofexecutable]
		exit 0
	}
}
exit 1
Added autosetup/tmake.auto.


































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Copyright (c) 2016 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# Auto-load module for 'tmake' build system integration

use init

autosetup_add_init_type tmake "Tcl-based tmake build system" {
	autosetup_check_create auto.def \
{# Initial auto.def created by 'autosetup --init=tmake'
# vim:set syntax=tcl:

use cc cc-lib cc-db cc-shared
use tmake

# Add any user options here
# Really want a --configure that takes over the rest of the command line
options {
}

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]*}
}

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

# 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

# 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
IncludePaths include

ifconfig CONFIGURED

# 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 autosetup/tmake.tcl.




















































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# @synopsis:
#
# The 'tmake' module makes it easy to support the tmake build system.
#
# The following variables are set:
#
## CONFIGURED  - to indicate that the project is configured

use system

module-options {}

define CONFIGURED

# @make-tmake-settings outfile patterns ...
#
# Examines all defined variables which match the given patterns (defaults to "*")
# and writes a tmake-compatible .conf file defining those variables.
# For example, if ABC is "3 monkeys" and ABC matches a pattern, then the file will include:
#
## define ABC {3 monkeys}
#
# If the file would be unchanged, it is not written.
#
# Typical usage is:
#
# make-tmake-settings [get-env BUILDDIR objdir]/settings.conf {[A-Z]*}
proc make-tmake-settings {file args} {
	file mkdir [file dirname $file]
	set lines {}

	if {[llength $args] == 0} {
		set args *
	}

	foreach n [lsort [dict keys [all-defines]]] {
		foreach p $args {
			if {[string match $p $n]} {
				set value [get-define $n]
				lappend lines "define $n [list $value]"
				break
			}
		}
	}
	set buf [join $lines \n]
	write-if-changed $file $buf {
		msg-result "Created $file"
	}
}
Added bin/pidp8i.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/sh
########################################################################
# pidp8i.in - Attach the current terminal to the screen(1) session
#	started by the SysV init script in etc/pidp8i-ini.
#
# Copyright © 2015-2017 Oscar Vermeulen 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.
########################################################################

if [ "$USER" != "@INSTUSR@" ] ; then exec su -c "$0" @INSTUSR@ ; fi

procs=`screen -ls pidp8i | egrep '[0-9]+\.pidp8i' | wc -l`
if [ $procs -ne 0 ]; then
	echo Joining simulator session already in progress...
	screen -r
else
	cat <<ERROR
Either the simulator isn't running, or it isn't running under a screen(1)
session owned by @INSTUSR@.  Did you start it via the init script?

ERROR
fi
Added boot/0.script.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
; This script initializes a populated OS/8 environment on an
; RK05 cartridge disk pack.  That's 10 whole megabytes, so big 
; the OS requires that you split it into two partitions in order
; to address the whole disk!  Thus the "RKB0:" references you
; will find in tutorials, as that refers to the second half ("B")
; of the first ("0") RK05 cartridge disk.  The default location
; the OS uses is formally called "RKA0:", alias "SYS:".
;
; See 3.script if you want to load OS/8 via DECtape instead.
;
; This file is read on simulator startup when the IF switches are
; set to 0, so it defines the default starting environment for the
; simulator.
;
; If you need to soft-restart the simulator back into OS/8 from some
; other state -- that is, you initially started the simulator with
; IF != 0 -- you can set IF = 7 and toggle the Sing_Step switch on
; and back off, which restarts the simulator with 7.script, which in
; turn loads this one.  This somewhat clumsy arrangement is required
; because toggling Sing_Step with IF = 0 must not be made to restart
; the simulator, else there would be no way to use Sing_Step for any
; of its other functions.  See the PiDP-8/I instructions for details.
;
reset
echo Loading OS/8 from the RK05 cartridge disk...
set cpu 32k
set cpu noidle
@SET_THROTTLE@
att rk0 @MEDIADIR@/os8/os8.rk05
boot rk0
Added boot/2.script.in.












1
2
3
4
5
6
7
8
9
10
11
12
+
+
+
+
+
+
+
+
+
+
+
+
; This script initializes a populated TSS/8 environment on an
; RS08 fixed-head hard disk drive (384 kB!) controlled by the
; RF08 disk controller.
;
echo Loading TSS/8 from the RS08 fixed-head disk...
load @MEDIADIR@/tss8/tss8_init.bin
set rf enabled
set df disabled
@SET_THROTTLE@
attach rf @MEDIADIR@/tss8/tss8_rf.dsk
attach ttix 4000
run 24200
Added boot/3.script.in.









1
2
3
4
5
6
7
8
9
+
+
+
+
+
+
+
+
+
; Loads OS/8 from DECtape, as opposed to the RK05 based environment
; in 0.script.
;
echo Loading OS/8 from DECtape...
set dt disabled
set td enabled
@SET_THROTTLE@
attach td0 @MEDIADIR@/os8/os8.tu56
boot td0
Added boot/4.script.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
; This script loads Spacewar! directly into core, without a supporting
; OS.
;
echo Spacewar!
echo
echo Keyboard controls from the VC8E program:
echo
echo Player 1  Player 2  Command
echo 1         9         fire weapon
echo 2         0         rotate CCW
echo 3         -         rotate CW
echo 4         =         thrusters
echo
echo Press both rotate keys simulatenously to warp into hyperspace.
echo
echo Press Ctrl-E to pause the simulator and return to the SimH
echo command prompt, where you can say "quit", "help" and other
echo things.  See the SimH manual for details.
echo
l @MEDIADIR@/spacewar/spacewar.bin
@SET_THROTTLE@
at ttix 2222
set ttox0 8b

g 200
Added boot/6.script.in.












1
2
3
4
5
6
7
8
9
10
11
12
+
+
+
+
+
+
+
+
+
+
+
+
; This script loads ETOS V5B from the RK05 cartridge disk drive.
;
echo Loading ETOS from the RK05 cartridge disk drive...
reset
set cpu 32k
set cpu noidle
@SET_THROTTLE@
set tsc enabled
attach ttix 4000
att rk0 @MEDIADIR@/etos/etosv5b-demo.rk05
boot -d rk0

Added boot/7.script.in.


1
2
+
+
echo Restarting the PiDP-8/I into its default operating environment...
do @BOOTDIR@/0.script
Added boot/readme.txt.

















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
On startup, scanswitch passes the script number to run on to pidp8,
based on the IF switch settings.

Standard boot scripts:

IF switches   filename   description
----------------------------------------------------------------------
   000        0.script : OS/8 on 32K PDP-8 with RK05 disk cartridge
   001        1.script : RIM Loader installed at 7756
   010        2.script : TSS/8
   011        3.script : OS/8 on DECtape. This uses SLOW td0, not dt0
   100        4.script : spacewar! with vc8e output on localhost:2222
   101        5.script : (empty)
   110        6.script : ETOS Multi-user on OS/8 boot disk
   111        7.script : OS/8. Same as 0.script.
----------------------------------------------------------------------
Default initial startup script - when no IF switches are set is 0.script
Added configure.



1
2
3
+
+
+
#!/bin/sh
dir="`dirname "$0"`/autosetup"
WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/autosetup" "$@"
Added doc/led-decay.cpp.








































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/***********************************************************************
 led-decay.cpp - Proof of concept for an LED brightness decay algorithm 
	that simulates the appearance of incandescent light bulbs when fed
	a stream of samples over a given time period.

 Copyright © 2016 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.
***********************************************************************/

#include <iostream>
#include <vector>

using namespace std;

typedef vector<bool> vb;
typedef vb::const_iterator vbc;

// Keeping n samples.  To think about this concretely, imagine that it
// is 1 sample per millisecond over 0.1 sec, but realize that this is
// scaleable, so that how long 1/n seconds is doesn't affect the math.
static const size_t n = 100;

// Decay function is 1 - x^2, meaning the most recent event is
// considered 100%, with older events having increasingly lesser effect
// on the overall brightness until we hit 0% consideration at the end of
// the sample set.
//
// We need to scale that so that the total area under the decay
// function's curve is 1, so that if we feed a 50% duty cycle in, we
// get 50% out, but if we skew the 1s toward the front of the sample
// set (i.e. closer to "now"), we get greater brightness than if they
// are skewed toward the past.
//
// The Pi ships with Mathematica, which answers this question with:
//
//    Solve[Integrate[z * (1 - x^2), {x, 0, 1}] == 1, z]
//
// We get z = 1.5.
//
// If you want a different decay function, it needs to substitute for
// the 1 - x^2 bit.  It needs to start at 1 and decay to 0 over the
// range [0 <= x <= 1].  Run that through Mathematica to find the
// resulting value of z that gives a total of 1 over the sample span.
static double f(double x, bool v)
{
	return v ? (1.5 * (1 - x * x)) : 0;
}

// Given n bits representing the state of the LED at time x=1/n, return
// the total of applications of f on each bit.  Order is most recent
// event first, so it takes the strongest effect.
static double cdf(const vb& vl)
{
	double t = 0;
	for (size_t i = 0; i < n; ++i) {
		// We divide each f() return by n because it represents only 1/n
		// of the total area under the curve.  This is a crude form of
		// numeric integration.
		t += f(i / double(n), vl[i]) / n;
	}
	return t;
}

// Generate a series of sampled LED values, then run those sample sets
// through the above and show what brightness level that would generate.
int main()
{
	vb values(n);

	values.clear();
	for (size_t i = 0; i < n; ++i) {
		 values.push_back(true);
	}
	cout << "100% duty cycle: CDF = " << cdf(values) << endl;

	values.clear();
	for (size_t i = 0; i < n; ++i) {
		 values.push_back(i % 2 == 0);
	}
	cout << "50% duty cycle: CDF = " << cdf(values) << endl;

	values.clear();
	for (size_t i = 0; i < n; ++i) {
		 values.push_back(i % 4 == 0);
	}
	cout << "25% duty cycle: CDF = " << cdf(values) << endl;

	values.clear();
	for (size_t i = 0; i < n; ++i) {
		 values.push_back(i % 10 == 0);
	}
	cout << "10% duty cycle: CDF = " << cdf(values) << endl;

	values.assign(n, false);
	for (size_t i = 0; i < n / 2; ++i) {
		 values[i] = true;
	}
	cout << "First half 'on': CDF = " << cdf(values) << endl;

	values.assign(n, false);
	for (size_t i = n / 2; i < n; ++i) {
		 values[i] = true;
	}
	cout << "Second half 'on': CDF = " << cdf(values) << endl;

	values.assign(n, false);
	values[0] = true;
	cout << "1ms spike at the start: CDF = " << cdf(values) << endl;

	values.assign(n, false);
	values[n - 1] = true;
	cout << "1ms spike at the end: CDF = " << cdf(values) << endl;
}
Added doc/vtedit-keypad.png.

cannot compute difference between binary files

Added doc/vtedit-keypad.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<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="673.51404"
   height="613.08057"
   viewBox="0 0 673.51405 613.08058"
   id="svg2"
   version="1.1"
   inkscape:version="0.91 r13725"
   sodipodi:docname="vtedit-keypad.svg"
   inkscape:export-filename="/usr/local/src/pidp8i/trunk/doc/vtedit-keypad.png"
   inkscape:export-xdpi="80.18"
   inkscape:export-ydpi="80.18">
  <title
     id="title4295">VTEDIT Keypad Diagram</title>
  <defs
     id="defs4">
    <linearGradient
       inkscape:collect="always"
       id="linearGradient4138">
      <stop
         style="stop-color:#dbb56a;stop-opacity:0.70833331"
         offset="0"
         id="stop4140" />
      <stop
         style="stop-color:#f0e6da;stop-opacity:1"
         offset="1"
         id="stop4142" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4144"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(-72.931483,8.918583)" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4254"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(39.142981,8.918583)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4272"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(151.21744,8.918583)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4324"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(-72.931483,120.43745)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4326"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(39.142981,120.43745)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4328"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(151.21744,120.43745)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4380"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(-72.931483,231.9563)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4382"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(39.142981,231.9563)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4384"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(151.21744,231.9563)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4434"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(263.2919,8.761662)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4436"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(263.2919,120.1236)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4438"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1,0,0,2.1178002,263.2919,83.80932)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4504"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(2.1255986,0,0,1,-190.0452,343.47519)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4508"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(151.21744,343.47519)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4611"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(-72.931483,-102.60028)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4613"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(39.142981,-102.60028)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4615"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(151.21744,-102.60028)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient4138"
       id="linearGradient4617"
       gradientUnits="userSpaceOnUse"
       gradientTransform="translate(263.2919,-102.60028)"
       x1="153.5432"
       y1="135.14371"
       x2="154.55334"
       y2="231.10818" />
  </defs>
  <sodipodi:namedview
     id="base"
     pagecolor="#fcfaf0"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="1"
     inkscape:pageshadow="2"
     inkscape:zoom="1.4"
     inkscape:cx="237.26909"
     inkscape:cy="345.9566"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     showgrid="false"
     inkscape:window-width="2560"
     inkscape:window-height="1391"
     inkscape:window-x="0"
     inkscape:window-y="1"
     inkscape:window-maximized="1"
     showguides="true"
     inkscape:guide-bbox="true"
     units="px"
     fit-margin-top="32"
     fit-margin-left="32"
     fit-margin-right="32"
     fit-margin-bottom="32" />
  <metadata
     id="metadata7">
    <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>VTEDIT Keypad Diagram</dc:title>
        <cc:license
           rdf:resource="https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md" />
        <dc:date>December 2016</dc:date>
        <dc:creator>
          <cc:Agent>
            <dc:title>Warren Young</dc:title>
          </cc:Agent>
        </dc:creator>
        <dc:contributor>
          <cc:Agent>
            <dc:title>This graphical diagram was created based on an ASCII diagram that came with the version of VTEDIT patched for VT100/ANSI terminals.</dc:title>
          </cc:Agent>
        </dc:contributor>
        <dc:rights>
          <cc:Agent>
            <dc:title>see ../SIMH-LICENSE.md</dc:title>
          </cc:Agent>
        </dc:rights>
        <dc:language>Englis</dc:language>
        <dc:subject>
          <rdf:Bag>
            <rdf:li>VTEDIT</rdf:li>
            <rdf:li>PDP-8</rdf:li>
            <rdf:li>diagram</rdf:li>
            <rdf:li>keypad</rdf:li>
          </rdf:Bag>
        </dc:subject>
        <dc:description>Diagram showing the function of the keypad keys when pressed in the version of VTEDIT patched for VT100/ANSI terminals.</dc:description>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:groupmode="layer"
     id="layer5"
     inkscape:label="key caps"
     style="display:inline"
     transform="translate(17.282791,22)">
    <rect
       transform="translate(-14.897034,-18.012959)"
       style="opacity:1;fill:url(#linearGradient4144);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="rect4136"
       width="98.994949"
       height="100.0051"
       x="31.114243"
       y="141.03181"
       ry="10.357149"
       rx="11.25" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.25"
       ry="10.357149"
       y="141.03181"
       x="143.18871"
       height="100.0051"
       width="98.994949"
       id="rect4240"
       style="opacity:1;fill:url(#linearGradient4254);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       style="opacity:1;fill:url(#linearGradient4272);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="rect4258"
       width="98.994949"
       height="100.0051"
       x="255.26317"
       y="141.03181"
       ry="10.357149"
       rx="11.25" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.25"
       ry="10.357149"
       y="252.55069"
       x="31.114243"
       height="100.0051"
       width="98.994949"
       id="rect4278"
       style="opacity:1;fill:url(#linearGradient4324);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       style="opacity:1;fill:url(#linearGradient4326);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="rect4294"
       width="98.994949"
       height="100.0051"
       x="143.18871"
       y="252.55069"
       ry="10.357149"
       rx="11.25" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.25"
       ry="10.357149"
       y="252.55069"
       x="255.26317"
       height="100.0051"
       width="98.994949"
       id="rect4310"
       style="opacity:1;fill:url(#linearGradient4328);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       style="opacity:1;fill:url(#linearGradient4380);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="rect4334"
       width="98.994949"
       height="100.0051"
       x="31.114243"
       y="364.06955"
       ry="10.357149"
       rx="11.25" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.25"
       ry="10.357149"
       y="364.06955"
       x="143.18871"
       height="100.0051"
       width="98.994949"
       id="rect4350"
       style="opacity:1;fill:url(#linearGradient4382);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       style="opacity:1;fill:url(#linearGradient4384);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="rect4366"
       width="98.994949"
       height="100.0051"
       x="255.26317"
       y="364.06955"
       ry="10.357149"
       rx="11.25" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.25"
       ry="10.357149"
       y="140.87489"
       x="367.33762"
       height="100.0051"
       width="98.994949"
       id="rect4388"
       style="opacity:1;fill:url(#linearGradient4434);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       style="opacity:1;fill:url(#linearGradient4436);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="rect4404"
       width="98.994949"
       height="100.0051"
       x="367.33762"
       y="252.23683"
       ry="10.357149"
       rx="11.25" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.25"
       ry="10.35715"
       y="363.59879"
       x="367.33762"
       height="211.79082"
       width="98.994949"
       id="rect4420"
       style="opacity:1;fill:url(#linearGradient4438);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3.00000024;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.249999"
       ry="10.357149"
       y="475.58841"
       x="31.114243"
       height="100.0051"
       width="210.42351"
       id="rect4442"
       style="opacity:1;fill:url(#linearGradient4504);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:2.99999976;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.25"
       ry="10.357149"
       y="475.58844"
       x="255.26317"
       height="100.0051"
       width="98.994949"
       id="rect4474"
       style="opacity:1;fill:url(#linearGradient4508);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.25"
       ry="10.357149"
       y="29.512959"
       x="31.114243"
       height="100.0051"
       width="98.994949"
       id="rect4549"
       style="opacity:1;fill:url(#linearGradient4611);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       style="opacity:1;fill:url(#linearGradient4613);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="rect4565"
       width="98.994949"
       height="100.0051"
       x="143.18871"
       y="29.512959"
       ry="10.357149"
       rx="11.25" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       rx="11.25"
       ry="10.357149"
       y="29.512959"
       x="255.26317"
       height="100.0051"
       width="98.994949"
       id="rect4581"
       style="opacity:1;fill:url(#linearGradient4615);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
    <rect
       transform="translate(-14.897034,-18.012959)"
       style="opacity:1;fill:url(#linearGradient4617);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="rect4597"
       width="98.994949"
       height="100.0051"
       x="367.33762"
       y="29.512959"
       ry="10.357149"
       rx="11.25" />
  </g>
  <g
     inkscape:groupmode="layer"
     id="layer6"
     inkscape:label="key labels"
     style="display:inline"
     transform="translate(17.282791,22)">
    <flowRoot
       xml:space="preserve"
       id="flowRoot4217"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       transform="translate(-86.06075,-4.043613)"><flowRegion
         id="flowRegion4219"><rect
           id="rect4221"
           width="86.785713"
           height="87.678574"
           x="110"
           y="137.71935"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         id="flowPara4223"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">7</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4227">Open</flowPara><flowPara
         id="flowPara4623"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Line◆</flowPara></flowRoot>    <flowRoot
       transform="translate(26.013714,-4.043613)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4242"
       xml:space="preserve"><flowRegion
         id="flowRegion4244"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4246"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4248">8</flowPara><flowPara
         id="flowPara4252"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Page◆</flowPara></flowRoot>    <flowRoot
       xml:space="preserve"
       id="flowRoot4260"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       transform="translate(138.08818,-4.043613)"><flowRegion
         id="flowRegion4262"><rect
           id="rect4264"
           width="86.785713"
           height="87.678574"
           x="110"
           y="137.71935"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         id="flowPara4266"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">9</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4270">Mark/</flowPara><flowPara
         id="flowPara4629"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Quote◆</flowPara></flowRoot>    <flowRoot
       transform="translate(-86.06075,107.47526)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4280"
       xml:space="preserve"><flowRegion
         id="flowRegion4282"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4284"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4286">4</flowPara><flowPara
         id="flowPara4290"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Up</flowPara><flowPara
         id="flowPara4633"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Line◆</flowPara></flowRoot>    <flowRoot
       xml:space="preserve"
       id="flowRoot4296"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       transform="translate(26.013714,107.47526)"><flowRegion
         id="flowRegion4298"><rect
           id="rect4300"
           width="86.785713"
           height="87.678574"
           x="110"
           y="137.71935"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         id="flowPara4302"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">5</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4306">Delete</flowPara><flowPara
         id="flowPara4637"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Char◆</flowPara></flowRoot>    <flowRoot
       transform="translate(138.08818,107.47526)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4312"
       xml:space="preserve"><flowRegion
         id="flowRegion4314"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4316"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4318">6</flowPara><flowPara
         id="flowPara4322"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Delete/</flowPara><flowPara
         id="flowPara4641"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Restore</flowPara></flowRoot>    <flowRoot
       xml:space="preserve"
       id="flowRoot4336"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       transform="translate(-86.06075,218.99411)"><flowRegion
         id="flowRegion4338"><rect
           id="rect4340"
           width="86.785713"
           height="87.678574"
           x="110"
           y="137.71935"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         id="flowPara4342"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">1</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4344">Top of</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4346">Page◆•</flowPara></flowRoot>    <flowRoot
       transform="translate(26.013714,218.99411)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4352"
       xml:space="preserve"><flowRegion
         id="flowRegion4354"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4356"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4358">2</flowPara><flowPara
         id="flowPara4360"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Bottom</flowPara><flowPara
         id="flowPara4362"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">of Page</flowPara></flowRoot>    <flowRoot
       xml:space="preserve"
       id="flowRoot4368"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       transform="translate(138.08818,218.99411)"><flowRegion
         id="flowRegion4370"><rect
           id="rect4372"
           width="86.785713"
           height="87.678574"
           x="110"
           y="137.71935"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         id="flowPara4374"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">3</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4376">Start</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4378">of Line</flowPara></flowRoot>    <flowRoot
       transform="translate(250.16264,-4.200534)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4390"
       xml:space="preserve"><flowRegion
         id="flowRegion4392"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4394"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4396">-</flowPara><flowPara
         id="flowPara4398"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Search</flowPara><flowPara
         id="flowPara4400"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Arg◆</flowPara></flowRoot>    <flowRoot
       xml:space="preserve"
       id="flowRoot4406"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       transform="translate(250.16264,107.16141)"><flowRegion
         id="flowRegion4408"><rect
           id="rect4410"
           width="86.785713"
           height="87.678574"
           x="110"
           y="137.71935"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         id="flowPara4412"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">,</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4414">End of</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4416">Line</flowPara></flowRoot>    <flowRoot
       transform="translate(250.16264,276.73764)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4422"
       xml:space="preserve"><flowRegion
         id="flowRegion4424"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4426"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4428">Enter</flowPara><flowPara
         id="flowPara4430"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Search</flowPara><flowPara
         id="flowPara4432"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Arg◆</flowPara></flowRoot>    <flowRoot
       transform="translate(-33.203607,330.513)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4444"
       xml:space="preserve"><flowRegion
         id="flowRegion4446"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4448"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4450">0</flowPara><flowPara
         id="flowPara4454"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Down</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4523">Line◆</flowPara></flowRoot>    <flowRoot
       transform="translate(138.08818,330.513)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4476"
       xml:space="preserve"><flowRegion
         id="flowRegion4478"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4480"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4482">.</flowPara><flowPara
         id="flowPara4484"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Search</flowPara><flowPara
         id="flowPara4486"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Again◆</flowPara></flowRoot>    <flowRoot
       transform="translate(-86.06075,-115.56247)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4551"
       xml:space="preserve"><flowRegion
         id="flowRegion4553"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4555"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4557">PF1</flowPara><flowPara
         id="flowPara4559"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Save</flowPara><flowPara
         id="flowPara4561"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Text◆•</flowPara></flowRoot>    <flowRoot
       xml:space="preserve"
       id="flowRoot4567"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       transform="translate(26.013714,-115.56247)"><flowRegion
         id="flowRegion4569"><rect
           id="rect4571"
           width="86.785713"
           height="87.678574"
           x="110"
           y="137.71935"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         id="flowPara4573"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">PF2</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4575">TECO</flowPara><flowPara
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4577">Cmd◆</flowPara></flowRoot>    <flowRoot
       transform="translate(138.08818,-115.56247)"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="flowRoot4583"
       xml:space="preserve"><flowRegion
         id="flowRegion4585"><rect
           y="137.71935"
           x="110"
           height="87.678574"
           width="86.785713"
           id="rect4587"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4589">PF3</flowPara><flowPara
         id="flowPara4591"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Paste</flowPara><flowPara
         id="flowPara4593"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Text</flowPara></flowRoot>    <flowRoot
       xml:space="preserve"
       id="flowRoot4599"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       transform="translate(250.16264,-115.56247)"><flowRegion
         id="flowRegion4601"><rect
           id="rect4603"
           width="86.785713"
           height="87.678574"
           x="110"
           y="137.71935"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1"
         id="flowPara4609">PF4</flowPara></flowRoot>  </g>
  <g
     inkscape:label="KEY"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(2.3857574,3.9870415)"
     style="display:inline">
    <text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="153.57143"
       y="180.93362"
       id="text4213"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan4215"
         x="153.57143"
         y="180.93362" /></text>
    <text
       xml:space="preserve"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="503.61438"
       y="56.824276"
       id="text4643"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         x="503.61438"
         y="56.824276"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         id="tspan4701">KEY</tspan></text>
    <text
       sodipodi:linespacing="125%"
       id="text5371"
       y="183.68143"
       x="503.61438"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       xml:space="preserve"><tspan
         id="tspan5385"
         dy="1"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         y="183.68143"
         x="503.61438"
         sodipodi:role="line">•  command</tspan><tspan
         id="tspan5387"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         y="205.55643"
         x="503.61438"
         sodipodi:role="line">    operates</tspan><tspan
         id="tspan5389"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         y="227.43143"
         x="503.61438"
         sodipodi:role="line">    from Dot to</tspan><tspan
         id="tspan5391"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         y="249.30643"
         x="503.61438"
         sodipodi:role="line">    Mark if Mark</tspan><tspan
         id="tspan5393"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         y="271.18143"
         x="503.61438"
         sodipodi:role="line">    is set</tspan></text>
    <text
       xml:space="preserve"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="503.61438"
       y="88.967133"
       id="text5395"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         x="503.61438"
         y="88.967133"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         id="tspan5399">◆  takes opt arg</tspan><tspan
         sodipodi:role="line"
         x="503.61438"
         y="111.48743"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         id="tspan5401">    as: ESC [-]</tspan><tspan
         sodipodi:role="line"
         x="503.61438"
         y="133.36243"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         id="tspan5403">    &lt;digits&gt;</tspan><tspan
         sodipodi:role="line"
         x="503.61438"
         y="155.23743"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         id="tspan5405">    &lt;key(s)&gt;</tspan><tspan
         sodipodi:role="line"
         x="503.61438"
         y="177.11243"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1"
         id="tspan5417" /></text>
  </g>
</svg>
Added etc/pidp8i-init.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/sh
### BEGIN INIT INFO
# Provides:		pidp8i
# Required-Start:	$syslog
# Required-Stop:	$syslog
# Default-Start:	2 3 4 5
# Default-Stop:		0 6
# Short-Description:PiDP-8/I simulator
# Description:      The PiDP-8/I simulator is a modified version of
#                   the SimH PDP-8 simulator for the PiDP-8/I front
#                   panel project for the Raspberry Pi.
### END INIT INFO

########################################################################
# Init script for Oscar Vermeulen's PiDP-8/I emulator front panel.  
#
# Original author: Mark G Thomas <mark@misty.com> 2015-05-09
#
# Copyright © 2015 Mark G Thomas
# 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.
########################################################################

PATH=/sbin:/usr/sbin:/bin:/usr/bin
umask 022
. /lib/lsb/init-functions

prefix="@ABSPREFIX@"
sim="$prefix/bin/pidp8i-sim"
scanswitch="$prefix/libexec/scanswitch"

# Requires screen utility for detached pidp8i console functionality.
test -x /usr/bin/screen || ( echo "screen not found" && exit 0 )

# Also check for other needed binaries
test -x $scanswitch || ( echo "$scanswitch not found" && exit 0 )
test -x $sim || ( echo "$sim not found" && exit 0 )

# Check if pidp8i is already runnning under screen.
#
is_running() {
	procs=`screenu -ls pidp8i | egrep '[0-9]+\.pidp8i' | wc -l`
	test $procs -gt 0 && return 0 || return 1
}

# Wrapper around screen(1) to drop privileges and pass given args
screenu() {
	if [ "$USER" = "@INSTUSR@" ]
	then
		/usr/bin/screen $*
	else
		su -c "/usr/bin/screen $*" @INSTUSR@
	fi
}

do_start() {
	if is_running ; then
	    echo "PiDP-8/I is already running, not starting again." >&2
	    exit 0
	fi

    # Regenerate SSH host keys if this is the first run on a fresh image
    if [ ! -f /etc/ssh/ssh_host_ecdsa_key -a -x /usr/sbin/dpkg-reconfigure ]
    then
        log_daemon_msg "Regenerating SSH host keys..." "pidp8i"
        /usr/sbin/dpkg-reconfigure openssh-server
    fi

	$scanswitch >/dev/null 2>&1
	script=$?
	if [ $script -eq 8 ]; then
	    echo "PiDP-8/I STOP switch detected, aborting." >&2
	    exit 0
	elif [ $script -lt 8 ]; then
	    bscript="@BOOTDIR@/""$script"".script"
	    echo "Booting from $bscript..."
	else
		echo "Bad return value $script from $scanswitch!"
		exit 1
	fi

	log_daemon_msg "Starting PiDP-8/I simulator" "pidp8i"
	screenu -dmS pidp8i "$sim" $bscript
	status=$?
	log_end_msg $status
	return $status
}

do_stop() {
	if ! is_running ; then
	    echo "PiDP-8/I is already stopped." >&2
	    status=1
	else
	    log_daemon_msg "Stopping PiDP-8/I simulator" "pidp8i"
	    screenu -S pidp8i -X quit
	    status=$?
	    log_end_msg $status
	fi
	return $status
}

case "$1" in
  start)
	do_start
	;;

  stop)
	do_stop
	;;

  restart)
	do_stop
	do_start
	;;

  status)
	screenu -ls pidp8i | egrep '[0-9]+\.pidp8i'
	;;

  *)
	log_action_msg "Usage: /etc/init.d/pidp8i {start|stop|restart|status}" || true
	exit 1
esac

exit 0
Added etc/sudoers.in.


1
2
+
+
@INSTUSR@ ALL=NOPASSWD: /bin/systemctl poweroff
@INSTUSR@ ALL=NOPASSWD: /bin/systemctl reboot
Added examples/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 examples/ 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 the examples 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 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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Example Programs

## What's Provided

The `examples` directory holds short example programs for your PiDP-8/I,
plus a number of subroutines you may find helpful in writing your own
programs:

| Example           | What It Does
-----------------------------
| `add.pal`         | 2 + 3 = 5  The simplest program here; used below as a meta-example
| `hello.pal`       | writes "HELLO, WORLD!" to the console; tests PRINTS subroutine
| `pep001.*`        | Project Euler Problem #1 solutions, various languages
| `routines/decprt` | prints an unsigned 12-bit decimal integer to the console
| `routines/prints` | prints an ASCIIZ string stored as a series of 8-bit bytes to the console

The `pep001.*` files are a case study series in solving a simple
problem, which lets you compare the solutions along several axes. Some
are much longer than others, but some will run faster and/or take less
memory. It is interesting to compare them. There are writeups on each of
these:

*   [**`pep001.pal`**][pal] — PAL8 Assembly Language
*   [**`pep001.bas`**][bas] — OS/8 BASIC

[pal]:  https://tangentsoft.com/pidp8i/wiki?name=PEP001.PA
[bas]:  https://tangentsoft.com/pidp8i/wiki?name=PEP001.BA


## How to Use the BASIC Examples

To use the example BASIC program, simply transcribe it into OS/8 BASIC:

    .R BASIC
	NEW OR OLD--NEW
	FILE NAME--PAL001.BA

	READY
	10 FOR I = 1 TO 999
	10 FOR I = 1 TO 999
	20 A = I / 3 \ B = I / 5
	30 IF INT(A) = A GOTO 60
	40 IF INT(B) = B GOTO 60
	50 GOTO 70
	60 T = T + I
	70 NEXT I
	80 PRINT "TOTAL: "; T
	90 END
	SAVE

	READY
	RUN

	PAL001  BA    4A    

	TOTAL:  xxxxxxx

	READY
	BYE

If you're SSH'd into the PiDP-8/I, "transcribing" is simply a matter of
cut-and-paste into the terminal window.

I've obscured the output on purpose, since I don't want this page to be
a spoiler for the Project Euler site.

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:

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

There are three ways to run these on your PiDP-8/I, each starting with
one of the above three files:

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
    [automatic media mounting feature][howto]. This is the fastest method.

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

To transcribe [`examples/add.pal`][pal] into the OS/8 simulation on a
PiDP-8/I:

    .R EDIT
    *ADD.PA<

    #A                          ← append to ADD.PA
    *0200   CLA CLL
    MAIN,   TAD A
            TAD B
            DCA C
            HLT
    A,      2
    B,      3
    C,
                                ← hit Ctrl-L to leave text edit mode
    #E                          ← saves program text to disk

    .PAL ADD-LS
    ERRORS DETECTED: 0
    LINKS GENERATED: 0

    .DIR ADD.* /A

    ADD   .PA   1             ADD   .BN   1             ADD   .LS    1

     399 FREE BLOCKS

If you see some cryptic line from the assembler like `DE C` instead
of the `ERRORS DETECTED: 0` bit, an error has occurred. Table 3-3 in
my copy the OS/8 Handbook explains these. You will also have an `ADD.ER`
file explaining what happened.

You can instead say `EXE ADD` to assemble and execute that program in a
single step, but beware that because the program halts the processor,
your OS/8 session also halts. If you take the opportunity as intended to
examine memory location `C` — 0207 — pressing `Start` to resume will
cause the processor to try executing the instruction at 0210, and who
knows what that will do? Even if you pass up the opportunity to examine
`C`, pressing `Start` immediately after the halt will do the same,
except that we know what it will do: it will try to execute the 0002
value stored at `A` as an instruction! (I believe it means `AND` the
accumulator with memory location 2.)

The solution to these problems is simple:

    .EDIT ADD                   ← don't need "R" because file exists
    #R                          ← read first page in; isn't automatic!
    #4D                         ← get rid of that pesky DCA line
    #5I                         ← insert above "A" def'n, now on line 5
            JMP 7600            ← Ctrl-L again to exit edit mode
    #E                          ← save and exit

    .EXE ADD

As before, the processor stops, but this time because we didn't move the
result from the accumulator to memory location `C`, we can see the
answer on the accumulator line on the front panel. Pressing `Start` this
time continues to the next instruction which re-enters OS/8. Much nicer!

As you can see, this option is the most educational, as it matches
the working experience of PDP-8 assembly language programmers back
in the day. The tools may differ — the user may prefer `TECO` over
`EDIT` or MACRO-8 over PAL8 — but the idea is the same regardless.

If you have the finished assembly code already on your computer and are
SSH'd into the PiDP-8/I machine, there is a shortcut for all of the
above. At the OS/8 command line, say:

    .R PIP
	*ADD.PA<TTY:

Now you can simply copy the assembly language text in your desktop PC's
text editor, paste it into the SSH window, and then hit Ctrl-Z to tell
`PIP` that the text input from the terminal (`TTY:`) is finished. This
is not only a smidge simpler than doing the same thing via `EDIT`, it
also avoids a certain limitation of `EDIT` that starts to bite you once
your program text exceeds about 5,600 characters.


## Option 2: Toggling a 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:

    10 00200 7300
    11 00201 1205
    12 00202 1206
    13 00203 3207
    14 00204 7402
    16 00205 0002
    17 00206 0003

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`

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:

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
0200 to observe that this memory location does, indeed, get overwritten
with 0005.

We only need one `Load Add` operation in the table above because all of
the memory addresses in this program are sequential; there are no jumps
in the values in the second column. Not all programs are that way, so
pay attention!

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

The `palbart` assembly process described above also produces paper tape
output files in RIM format in `bin/*.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
    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.


## 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/ac-mq-blinker.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
50
51
52
53
54
55
56
57
58
59
60
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ ac-mq-blinker.pal - Rapidly modify AC and MQ
/
/ This program twiddles AC and MQ rapidly, with a small amount of delay
/ between each update so the lights aren't just a solid blur.
/
/ While this program runs at full speed, only AC and MQ appear to a
/ human to really change.  PC also changes, of course, but since the
/ program spends so much of its time in the delay loop at the top, it
/ appears to be stuck at PC=1.
/
/ It also modifies MB rapidly, but the pattern we use means it looks
/ like the lamps aren't changing, but are all on, dimmed by varying
/ amounts.
/
/ From: http://dustyoldcomputers.com/pdp8/pdp8i/testprogs/acmqblinker.html
/
/ SIMH: SET THROTTLE 30k
/
/ Copyright © 2000 Robert Krten
/
/ 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.
////////////////////////////////////////////////////////////////////////

	PAGE 0
loop,	ISZ delay	/ create a delay
	JMP loop
	CLA		/ clear AC so we can load it
	TAD value	/ get value
	MQL		/ stash AC into MQ
	TAD value	/ fetch value again
	CMA		/ complement AC
	ISZ value	/ get to next value
	NOP		/ ignore possible "skip" from ISZ
	JMP loop	/ and do it all again

	*20		/ skip over the autoincrement registers
delay,	0
value,	0
$
Added examples/add.pal.



















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ add.pal - Add two numbers and halt, with sum in location 0207
/
/ This is a more space-efficient alternative to the program given at:
/
/ http://mrhowson.edublogs.org/2016/11/27/pidp-8i-second-toggle-some-assembly-code/

	PAGE 1		/ code starts at core page 1; must avoid page 0
	CLA CLL		/ clear AC and Link; two OPRs, one instruction!
	TAD A		/ add A to AC, which is zero, so "load A"
	TAD B		/ add B to AC
	DCA C		/ store sum in AC at C 
	HLT		/ halt program 

A,      2		/ set "A" variable to 2
B,      3		/ and "B" to 3
C,	   		/ "C" result variable lives immediately past B,
			/ and has no initial value because it is always
			/ overwritten with the answer
$
Added examples/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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ 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...

	*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 examples/bit-rotate.pal.















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ bit-rotate.pal - Bit rotation and HLT demo
/
/ This example demonstrates the bit rotation and HLT instructions of the
/ processor.  It is meant to be run with the simulator in free-running
/ mode, since the embedded HLT instruction lets you see the state of AC
/ after each rotation.  There are two HLT instructions so that you can
/ see the initial 1 value, and then see it change after each rotation.

	PAGE 1
	CLA CLL IAC     / clear link and AC, then bump AC: AC=1
	HLT		/ let user see initial AC=1 value
LOOP,	RAL		/ rotate AC left
	HLT             / and halt again
	JMP LOOP        / on CONT, around it goes...
$
Added examples/hello.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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ HELLO - "Hello, World!" program for PAL8 assembly, which also
/         happens to test the PRINTS routine, included inline below.
/
/ Created by Warren Young of tangentsoft.com, 2016.11.30
/
/ Copyright © 2016 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.
////////////////////////////////////////////////////////////////////////


//// MAIN /////////////////////////////////////////////////////////////

PAGE 1
	CLA
	TLS		/ send null character to terminal to prepare it
	TAD (HWSTR)
	JMS PRINTS
	HLT

// "HELLO, WORLD!\r\n" in octal ASCIIZ
HWSTR,	110; 105; 114; 114; 117;	/ HELLO
	54;				/ comma
	40;				/ space
	127; 117; 122; 114; 104;	/ WORLD
	41;				/ bang
	15; 12;				/ CRLF
	0				/ null string terminator


//// PRINTS ////////////////////////////////////////////////////////////

PRINTS,0
	DCA SADDR	/ save AC as string address
PSNEXT,	TAD I SADDR	/ load next character
	SNA
	JMP I PRINTS	/ found the null terminator; leave

	TSF		/ wait for terminal to be ready
	JMP .-1
	TLS		/ write character to the terminal

	CLA		/ increment string address pointer
	TAD SADDR
	IAC
	DCA SADDR
		
	JMP PSNEXT	/ look at next character
SADDR,	0


//// END ///////////////////////////////////////////////////////////////
$
Added examples/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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ 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...

	*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 examples/pep001.bas.












1
2
3
4
5
6
7
8
9
10
11
12
+
+
+
+
+
+
+
+
+
+
+
+
1 REM Copyright (c) 2016 by Warren Young
2 REM Released under the terms of ../SIMH-LICENSE.md
3 REM ------------------------------------------------------------------
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
Added examples/pep001.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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ Project Euler Problem #1, Multiples of 3 and 5:
/
/   If we list all the natural numbers below 10 that are multiples of
/   3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
/   Find the sum of all the multiples of 3 or 5 below 1000.
/
/ Initial solution by Warren Young of tangentsoft.com, 2016.11.30
/ Optimized by Rick Murphy of the mailing list, 2016.12.04
/
/ Copyright © 2016-2017 Warren Young and Rick Murphy
/
/ 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.
////////////////////////////////////////////////////////////////////////


//// DIY Assembler Instructions ////////////////////////////////////////

/ Our assembler doesn't know the EAE instructions, so teach it
DVI=7407		/ integer divide .+1 into {AC:MQ}, answer in MQ

/ Combined microcoded instruction aliases
CLR=CLA CLL		/ clear both AC and L
AC3=CLA CLL CML IAC RAL	/ set AC to 3


//// MAIN //////////////////////////////////////////////////////////////
/ Program entry point.   We purposely reinitialize global variables and
/ processor state in case we're restarting this program in-core.

PAGE 1
MAIN,   AC3		/ start with 3, because we know 1 & 2 can't work
	DCA CURR		
	DCA STOTAL	/ reset total to 0
	TAD (ANSWER-1)	/ write "ANSWER: " to the terminal
	JMS PRINTS


//// MLCORE ////////////////////////////////////////////////////////////
/ The core of the main loop.  MAIN just inits the globals and falls
/ through to us.
 
MLCORE, AC3		/ try dividing 3 into CURR first
	JMS ISMOD0
	SNA CLA		/ if ISMOD0 left AC = 0, CURR divided evenly by
	JMP NXITER	/ 3, so skip 5 lest we count multiples of 15 2x

	TAD (5)		/ no good; try dividing 5 into CURR instead
	JMS ISMOD0

NXITER, CLA		/ loop cleanup
	TAD CURR
	CIA
	TAD MAX		/ = 0 if CURR == MAX
	SNA CLA		/ if so, leave calculation loop
	JMP MLDONE

	ISZ CURR	/ CURR still < MAX, so increment CURR; never skips

	TAD STOTAL	/ if STOTAL is getting too big, print...
	CIA		/ a subtotal and zero STOTAL so we don't...
	TAD STMAX	/ overflow the 12-bit limit
	SNL
	JMP MLCORE	/ STMAX - STOTAL > 0 so re-enter loop core
	JMS SHOWST	/ exceeded threshold, so display subtotal and " + "
	DCA STOTAL	/ take advantage of free zero left by SHOWST
	TAD (PLUS-1)
	JMS PRINTS
	JMP MLCORE

MLDONE, JMS SHOWST	/ done; show answer
	TAD (CRLF-1)	/ don't need CLA; SHOWST left AC = 0
	JMS PRINTS

	/ End program gracefully, either re-entering OS/8 if we can see
	/ that its entry point looks sane, or halting with the answer in
	/ AC so the user can see the answer on the front panel.
OS8ENT,			/ 7600, OS/8's entry point, happens to also be...
ENDG,   7600		/ ...the group 2 variant of CLA; yes, we know, yuck!
	TAD I OS8ENT
	TAD OS8INS1	/ add its negative
	SNA CLA		/ if it's zero'd out, then...
	JMP I OS8ENT	/ re-enter OS/8
	TAD STOTAL	/ else not running under OS/8...
	HLT		/ so halt with STOTAL displayed in AC lights
OS8INS1,-4207		/ first OS/8 instruction at entry point, negated


//// ISMOD0 ////////////////////////////////////////////////////////////
/ If passed AC divides evenly into CURR (in C-speak, CURR % AC == 0)
/ add CURR to STOTAL and return 0 in AC.  Else, return nonzero in AC and
/ leave STOTAL untouched.

ISMOD0, 0
	DCA DIVISOR	/ Divide CURR by DIVISOR, passed as AC
	TAD CURR	/ load CURR into just-cleared AC
	MQL DVI		/ move CURR to MQ, divide by DIVISOR...
DIVISOR,0		/ ...quotient in MQ, remainder in AC
	SZA
	JMP I ISMOD0	/ remainder nonzero, so leave early

	/ Division left AC empty, so CURR divides evenly by DIVISOR!
	TAD CURR	/ don't need to clear AC; prior test says AC == 0
	TAD STOTAL
	DCA STOTAL
	JMP I ISMOD0


//// SHOWST ////////////////////////////////////////////////////////////
/ Write STOTAL value to terminal in decimal.  We purposely do not follow
/ it with anything, as our callers variously follow it with " + " or a
/ CRLF pair.  Leaves AC = 0 because DECRPT does.

SHOWST, 0
	CLR
	TAD STOTAL
	JMS DECPRT	/ print answer on console, in decimal
	JMP I SHOWST	/ and done


//// TYPE //////////////////////////////////////////////////////////////
/ Send a character out to the terminal.  Shared core of PRINTS and
/ DECPRT.

TYPE,   0
	TSF
	JMP .-1
	TLS
	CLA
	JMP I TYPE


//// PRINTS ////////////////////////////////////////////////////////////
/ Write an ASCIIZ string to the terminal.  Expects to receive the
/ address of the string - 1 in AC.  (The -1 hassle saves an instruction
/ or two in our use of an autoincrement register.)  Uses the autoinc
/ register at location 10.

SADDR=10		/ autoinc register for walking the string
PRINTS, 0
	DCA SADDR	/ save AC as string address
PSNEXT, TAD I SADDR	/ load next character
	SNA
	JMP I PRINTS	/ found the null terminator; leave
	JMS TYPE	/ Print that character
	JMP PSNEXT	/ look at next character


//// DECPRT ////////////////////////////////////////////////////////////
/ Decimal number printer; variant of examples/routines/decprt.pal
/ Leaves AC = 0.

DECPRT, 0
	DCA VALUE	/SAVE INPUT
	DCA DIGIT	/CLEAR
	TAD CNTRZA
	DCA CNTRZB	/SET COUNTER TO FOUR
	TAD ADDRZA
	DCA ARROW	/SET TABLE POINTER
	SKP
	DCA VALUE	/SAVE
	CLL
	TAD VALUE
ARROW,  TAD TENPWR	/SUBTRACT POWER OF TEN
	SZL
	ISZ DIGIT	/DEVELOP BCD DIGIT
	SZL
	JMP ARROW-3	/LOOP
	CLA		/HAVE BCD DIGIT
	TAD DIGIT	/GET DIGIT
	TAD K260	/MAKE IT ASCII
	JMS TYPE
	DCA DIGIT	/CLEAR
	ISZ ARROW	/UPDATE POINTER
	ISZ CNTRZB	/DONE ALL FOUR?
	JMP ARROW-1	/NO: CONTINUE
	JMP I DECPRT	/YES: EXIT

ADDRZA, TAD TENPWR
CNTRZA, -4
TENPWR, -1750		/ONE THOUSAND
	-0144		/ONE HUNDRED
	-0012		/TEN
	-0001		/ONE
K260,   260
VALUE,  0
DIGIT,  0
CNTRZB, 0


//// Global Variables //////////////////////////////////////////////////

CURR,   0		/ current number we're checking
STOTAL, 0		/ subtotal, printed and reset occasionally


//// Constants /////////////////////////////////////////////////////////

	DECIMAL
MAX,    999		/ check natural numbers CURR to MAX; must be < 2048!
STMAX,  1024		/ subtotal max; avoids overflow of 12-bit signed int

	OCTAL
CRLF,   15;12;0		/ ASCII character values, zero-terminated
PLUS,   40;53;40;0
ANSWER, 101;116;123;127;105;122;72;40;0


//// END ///////////////////////////////////////////////////////////////
/ Assembler-generated constants will appear below this in the list file
$
Added examples/routines/decprt.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/COPYRIGHT 1971, DIGITAL EQUIPMENT CORPORATION
/MAYNARD, MASSACHUSETTS
/DIGITAL 8-22-U
/UNSIGNED DECIMAL PRINT
/CALL WITH NUMBER TO BE TYPED IN C(AC)
/RETURN TO LOCATION FOLLOWING THE JMS
DECPRT,	0
	DCA VALUE	/SAVE INPUT
	DCA DIGIT	/CLEAR
	TAD CNTRZA
	DCA CNTRZB	/SET COUNTER TO FOUR
	TAD ADDRZA
	DCA ARROW	/SET TABLE POINTER
	SKP
	DCA VALUE	/SAVE
	CLL
	TAD VALUE
ARROW,	TAD TENPWR	/SUBTRACT POWER OF TEN
	SZL
	ISZ DIGIT	/DEVELOP BCD DIGIT
	SZL
	JMP ARROW-3	/LOOP
	CLA		/HAVE BCD DIGIT
	TAD DIGIT	/GET DIGIT
	TAD K260	/MAKE IT ASCII
	TSF		/OR TAD DIGIT
	JMP .-1		/JMS TDIGIT(SEE 8-19-U)
	TLS		/TYPE DIGIT
	CLA
	DCA DIGIT	/CLEAR
	ISZ ARROW	/UPDATE POINTER
	ISZ CNTRZB	/DONE ALL FOUR?
	JMP ARROW-1	/NO: CONTINUE
	JMP I DECPRT	/YES: EXIT
ADDRZA,	TAD TENPWR
CNTRZA,	-4
TENPWR,	-1750		/ONE THOUSAND
	-0144		/ONE HUNDRED
	-0012		/TEN
	-0001		/ONE
K260,	260
VALUE,	0
DIGIT,	0
CNTRZB,	0
Added examples/routines/prints.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
50
51
52
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/ PRINTS - Print an ASCIIZ string to the terminal
/
/ It expects to receive the address of the string - 1 in AC.  (The -1
/ hassle saves an instruction or two in our use of an autoincrement
/ register.)
/
/ This routine uses the autoinc register at location 10.
/
/ Created by Warren Young of tangentsoft.com, 2016.11.30
/ Improved by Rick Murphy of the PiDP-8/I mailing list, 2016.12.03
/
/ Copyright © 2016 Warren Young and Rick Murphy
/
/ 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.
////////////////////////////////////////////////////////////////////////

TYPE,   0		/ helper routine for sending a single character
	TSF
	JMP .-1
	TLS
	CLA
	JMP I TYPE

SADDR=10		/ autoinc register for walking the string
PRINTS, 0
	DCA SADDR	/ save AC as string address
PSNEXT, TAD I SADDR	/ load next character
	SNA
	JMP I PRINTS	/ found the null terminator; leave
	JMS TYPE	/ print that character
	JMP PSNEXT	/ look at next character
Added labels/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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# USB Stick Labels

## What It Is

This directory contains an Inkscape document (`*.svg`) containing the
DECtape logo and USB stick labels with graphics based on that label.
There are three example labels in the document:

*   **BIN Loader** for `media/copytoUSBsticks/binloader.pt`
*   **FOCAL-69** for `media/copytoUSBsticks/focal.pt`
*   **ADD.PA** for `examples/add.pt`

These labels use the "DECtape" graphics even though they're paper tapes,
primarily because it's a nice graphic and I haven't bothered to draw
something appropriate to paper tapes yet.

The labels print out at approximately 30×16mm each, which fits the USB
sticks I have here, but you may need to scale the printout for your
particular USB sticks.


## Affixing the Labels

This document is not designed with any particular self-adhesive label
stock in mind. Instead, I simply use rubber cement as a contact adhesive
to affix these labels to the USB stick.

Simply cut the label(s) you want to use out with scissors, paint both
the back of the label and the top of the USB stick with rubber cement,
and let it dry for a minute or so. When the glue is dry-looking,
carefully place the label where you want it on the USB stick. You won't
have much of a chance to move the label around after the two dried glue
patches touch, so be careful with your placement.

Press the label firmly against the stick, pressing repeatedly to cover
the entire surface, then rub around the label gently to brush away any
excess cement.

Protip: Use the part of the page you cut the labels out of as a
protective mat to work on. It will let you apply cement to the label
fully edge-to-edge without messing up your work surface. The labels will
be much more durable if there is no unglued bit near the edge for
fingernails and such to snag on. You were going to throw this excess
material away, so you might as well get one final use out of it, yes?


## Fonts

This SVG file uses a non-free font called [Dottie][font] for the faux
dot matrix text. There are [free alternatives][alt], but none of the
ones I liked allow redistribution, so I couldn't include one of them in
this repository.

[font]: https://www.fonts.com/font/ingrimayne-type/dottie/regular
[alt]:  http://www.1001fonts.com/digital+dot-matrix-fonts.html


## PDF Version

If you don't want to use one of those alternative fonts or simply like
the look of Dottie and don't need custom labels, this directory also
includes a PDF of the same design with the necessary subset of Dottie
embedded, so that you can print it out.
Added labels/dectape-usb-key.pdf.

cannot compute difference between binary files

Added labels/dectape-usb-key.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   id="svg2"
   version="1.1"
   inkscape:version="0.91 r13725"
   xml:space="preserve"
   width="765"
   height="990"
   viewBox="0 0 765 990"
   sodipodi:docname="dectape-usb-key.svg"><title
     id="title4244">PiDP-8/I USB key labels</title><metadata
     id="metadata8"><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>PiDP-8/I USB key labels</dc:title><dc:date>December 2016</dc:date><dc:creator><cc:Agent><dc:title>Warren Young</dc:title></cc:Agent></dc:creator><dc:rights><cc:Agent><dc:title>see license</dc:title></cc:Agent></dc:rights><dc:language>English</dc:language><dc:subject><rdf:Bag><rdf:li>label</rdf:li><rdf:li>USB</rdf:li><rdf:li>DEC</rdf:li><rdf:li>PDP-8</rdf:li><rdf:li>dot matrix</rdf:li><rdf:li>DECtape</rdf:li></rdf:Bag></dc:subject><dc:description>Graphical labels for use on USB sticks containing binary media images suitable for use with the PiDP-8/I's USB stick auto-attaching feature.</dc:description><cc:license
           rdf:resource="https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md" /></cc:Work></rdf:RDF></metadata><defs
     id="defs6"><clipPath
       clipPathUnits="userSpaceOnUse"
       id="clipPath18"><path
         d="M 0,0 612,0 612,792 0,792 0,0 Z"
         id="path20"
         inkscape:connector-curvature="0" /></clipPath><filter
       inkscape:label="Opacity"
       style="color-interpolation-filters:sRGB;"
       id="filter3471"><feColorMatrix
         values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 -0 "
         result="colormatrix"
         id="feColorMatrix3473" /><feComposite
         in2="colormatrix"
         operator="arithmetic"
         k2="0.343085"
         result="composite"
         id="feComposite3475" /></filter></defs><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="namedview4"
     showgrid="false"
     inkscape:zoom="3.17"
     inkscape:cx="117.85871"
     inkscape:cy="792.26542"
     inkscape:window-x="0"
     inkscape:window-y="1"
     inkscape:window-maximized="1"
     inkscape:current-layer="layer2"
     showguides="true"
     inkscape:guide-bbox="true"><sodipodi:guide
       position="278.68734,454.12002"
       orientation="0,1"
       id="guide3550" /><sodipodi:guide
       position="294.28136,404.66471"
       orientation="0,1"
       id="guide3552" /></sodipodi:namedview><g
     id="g10"
     inkscape:groupmode="layer"
     inkscape:label="reference"
     transform="matrix(1.25,0,0,-1.25,0,990)"
     style="display:inline"><g
       id="g12" /></g><g
     inkscape:groupmode="layer"
     id="layer1"
     inkscape:label="inner elements"
     style="display:inline" /><g
     inkscape:groupmode="layer"
     id="layer2"
     inkscape:label="ring"
     style="display:inline"><g
       id="g4253"
       transform="translate(-3.4700316,-242.58675)"><ellipse
         ry="37.22398"
         rx="39.077209"
         style="display:inline;fill:none;fill-opacity:1;stroke:none;stroke-width:0.99528235;stroke-opacity:1"
         id="circle4384"
         cx="88.770988"
         cy="306.1496" /><g
         inkscape:label="#g3477"
         style="display:inline;fill:#1e97ec;fill-opacity:1;stroke:none"
         id="digital-logo"
         transform="matrix(0.05536257,0,0,-0.05536257,75.852793,325.57701)"><g
           style="fill:#1e97ec;fill-opacity:1;stroke:none"
           id="g3479"
           clip-path="url(#clipPath18)"><path
             d="m 101.3,107.13 c 0.02,-2.96 -3.43,-7.55 -7.63,-7.56 -4.48,0.02 -8.87,4.08 -8.87,12.2 0,8.24 4.38,12.48 8.73,12.5 4.34,-0.02 7.79,-4.67 7.77,-8.2 l 0,-8.94 z m 0.23,22.74 0,18.13 8.9,0 0,-56.4 -9.2,0 0,2.6 c -0.75,-0.76 -3.92,-4.07 -9.33,-4.07 -5.64,-0.01 -15.73,4.69 -15.73,21.64 0,14.37 7.54,21.8 16.5,21.8 3.32,0 6.58,-1.18 8.86,-3.7 z M 72,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m 7,0 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 61.18,-20.72 c 0,3.59 -4.04,7.3 -7.78,7.3 -3.22,0 -8.2,-2.05 -8.2,-11.24 0,-9.79 6.12,-11.22 8.15,-11.22 4.03,0 7.83,2.77 7.83,7.05 l 0,8.11 z M 171,172 l 42.5,0 0,-100 -42.5,0 0,100 z m 28.52,-43.91 c -0.93,1.33 -3.98,4.35 -8.34,4.35 -6.77,-0.01 -16.21,-3.92 -16.21,-20.89 0.01,-13.38 10.7,-17.87 15.09,-17.87 4.74,-0.01 8.26,2.75 9.22,3.79 l 0,-4.21 c 0,-3.11 -3.19,-6.15 -8.53,-6.15 -4.6,0.01 -5.08,2.96 -5.08,4.42 l -9.4,0 c 0,-4.67 3.01,-11.49 13.91,-11.5 9.76,0 12.98,3.73 14.37,5.13 1.17,1.18 2.92,4.42 2.92,7.01 l 0,39.83 -7.94,0 -0.01,-3.91 z M 220.5,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 56.03,9.75 -8.23,0 0,-14.54 -4.31,0 0,-6.75 4.31,0 0,-25.44 c 0,-4.85 2.49,-8.66 9.43,-8.65 3.25,0 3.12,-0.02 5.94,0.47 l 0,8.4 c -1.84,-0.32 -2.41,-0.45 -3.31,-0.42 -1.73,0.05 -3.83,0.66 -3.83,3.74 l 0,21.89 6.52,0 0,6.76 -6.52,0 0,14.54 z M 312.5,72 270,72 l 0,100 42.5,0 0,-100 z m 49.5,0 -42.5,0 0,100 42.5,0 0,-100 z m -42.5,0 z m 6.99,47.28 7.71,0 c 0,4.1 2.12,6.25 6.16,6.25 6.44,0.01 6.23,-2.86 6.22,-6.45 -3.98,-1.55 -3.61,-1.51 -9.68,-2.89 -7.71,-1.78 -12.43,-5.08 -12.44,-13.07 0.01,-7.81 5.57,-11.98 11.84,-11.97 6.28,-0.01 7.58,2.02 10.18,3.75 l 0,-3.12 9.84,0 c -2.32,3.74 -1.71,4.47 -1.73,7.28 0.01,2.2 0,23.74 0,23.74 0.01,4.27 -2.21,6.88 -3.66,7.75 -3.62,2.16 -8.09,2.42 -10.57,2.43 -8.81,-0.02 -13.88,-4.45 -13.87,-13.7 z m 20.09,-7.82 0,-5.45 c 0,-2.49 -3.03,-7.31 -8.98,-7.32 -2.9,-0.01 -4.52,2.14 -4.53,4.93 0.01,2.8 2.38,4.95 4.39,5.35 2.09,0.42 6.9,1.65 9.12,2.49 z M 369,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,56.9 9,0 0,-56.9 z"
             style="fill:#1e97ec;fill-opacity:1;fill-rule:nonzero;stroke:none"
             id="path3481"
             inkscape:connector-curvature="0" /></g></g><g
         transform="matrix(0.140057,0,0,0.140057,28.143932,232.97391)"
         inkscape:label="#g4177"
         id="text"
         style="display:inline"><text
           xml:space="preserve"
           style="font-style:normal;font-weight:normal;font-size:32px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#1e97ec;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           x="234.70551"
           y="-270.57205"
           id="text3511"
           sodipodi:linespacing="125%"
           transform="matrix(1.25,0,0,1.25,0,990)"><tspan
             sodipodi:role="line"
             id="tspan3513"
             x="234.70551"
             y="-270.57205"
             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';fill:#1e97ec;fill-opacity:1;stroke:none">DIGITAL EQUIPMENT CORPORATION</tspan></text>
<text
           transform="matrix(1.25,0,0,1.25,0,990)"
           sodipodi:linespacing="125%"
           id="text3515"
           y="-258.09683"
           x="252.52724"
           style="font-style:normal;font-weight:normal;font-size:32px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#1e97ec;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           xml:space="preserve"><tspan
             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:11px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';fill:#1e97ec;fill-opacity:1;stroke:none"
             y="-258.09683"
             x="252.52724"
             id="tspan3517"
             sodipodi:role="line">MAYNARD, MASSACHUSETTS, 01754</tspan></text>
<text
           transform="matrix(1.25,0,0,1.25,0,990)"
           sodipodi:linespacing="125%"
           id="text3519"
           y="-237.42363"
           x="236.84412"
           style="font-style:normal;font-weight:normal;font-size:32px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#1e97ec;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           xml:space="preserve"><tspan
             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:15px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:0.80000001px;fill:#1e97ec;fill-opacity:1;stroke:none"
             y="-237.42363"
             x="236.84412"
             id="tspan3521"
             sodipodi:role="line">REEL NO.</tspan></text>
<text
           xml:space="preserve"
           style="font-style:normal;font-weight:normal;font-size:32px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#1e97ec;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           x="422.72479"
           y="-237.42363"
           id="text3523"
           sodipodi:linespacing="125%"
           transform="matrix(1.25,0,0,1.25,0,990)"><tspan
             sodipodi:role="line"
             id="tspan3525"
             x="422.72479"
             y="-237.42363"
             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:15px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:0.80000001px;fill:#1e97ec;fill-opacity:1;stroke:none">DATE</tspan></text>
</g><path
         inkscape:connector-curvature="0"
         d="m 72.199362,308.19926 0,6.84856 1.404033,-0.0156 c 0,0 4.448809,0.2496 4.461705,-3.60369 0.01175,-3.51016 -4.430504,-3.27607 -4.430504,-3.27607 z m 1.326032,1.31043 0,4.1965 0.686416,0 c 0,0 2.523387,0.12492 2.542859,-2.30886 0.0156,-1.95004 -2.402456,-1.91884 -2.402456,-1.91884 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         id="path3539" /><path
         inkscape:connector-curvature="0"
         id="path3554"
         d="m 78.252303,308.02765 0,6.92656 3.588086,0 0,-1.29483 -2.106051,0 0,-1.54443 2.028048,0 0,-1.31043 -2.074848,0 0,-1.41964 2.059249,0 0,-1.35723 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
         inkscape:connector-curvature="0"
         id="path3556"
         d="m 87.48772,310.0089 1.450835,0 c 0,0 -0.702017,-2.23085 -3.416481,-2.23085 -2.714464,0 -3.572483,2.38685 -3.572483,3.74408 0,1.35724 1.294829,3.43208 3.775287,3.43208 2.480459,0 3.229277,-2.04364 3.229277,-2.04364 l -1.466435,0 c 0,0 -0.358807,0.71761 -1.747241,0.71761 -1.388432,0 -2.402456,-0.99842 -2.402456,-2.23085 0,-1.23243 0.951623,-2.16845 2.230851,-2.16845 1.279232,0 1.918846,0.78002 1.918846,0.78002 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
         inkscape:connector-curvature="0"
         id="path3558"
         d="m 89.219362,308.02765 0,6.92656 1.669238,0 0,-5.46012 -1.201229,0 0,-1.46644 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
         inkscape:connector-curvature="0"
         id="path3560"
         d="m 92.152229,314.95421 0,-5.47572 1.29483,0 0.0312,-1.45084 3.697286,0 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
         inkscape:connector-curvature="0"
         id="path3562"
         d="m 93.868269,314.95421 1.357232,-1.90324 1.560036,0 0,1.90324 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
         sodipodi:nodetypes="cccc"
         inkscape:connector-curvature="0"
         id="path3564"
         d="m 96.052321,311.84974 0.748816,-1.06082 0,1.04522 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
         inkscape:connector-curvature="0"
         id="path3566"
         d="m 98.080369,308.02765 0,6.92656 0.530412,0 0,-6.92656 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
         inkscape:connector-curvature="0"
         id="path3568"
         d="m 99.87441,309.32248 0,1.79404 0.67082,0.0156 c 0,0 0.99842,-0.1248 0.99842,-0.92042 0,-0.79562 -1.02962,-0.85802 -1.02962,-0.85802 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
         inkscape:connector-curvature="0"
         id="path3570"
         d="m 99.936811,312.28655 0,2.66766 3.057669,0 0,-6.92656 -1.84084,0 c 0,0 1.56004,0.35881 1.56004,2.32446 0,1.96564 -1.90325,1.95004 -1.90325,1.95004 z"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><g
         transform="matrix(0.140057,0,0,0.140057,28.143932,232.97391)"
         inkscape:label="#g4171"
         id="dectape-graphics"
         style="display:inline"><path
           transform="matrix(1.25,0,0,-1.25,0,990)"
           style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 139.98514,433.20896 410.6593,-5.07131 c 0,0 -5.97508,23.16953 -13.43181,38.13146 -167.11395,1.42458 -326.54993,2.93466 -382.95298,3.28558 -9.80882,-18.26995 -14.27451,-36.34573 -14.27451,-36.34573 z"
           id="path3574"
           inkscape:connector-curvature="0"
           sodipodi:nodetypes="ccccc" /><path
           transform="matrix(1.25,0,0,-1.25,0,990)"
           style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 163.42461,486.64088 c 0,0 17.16387,25.4632 30.545,37.27097 14.36613,-10e-6 304.87219,-2.95063 304.87219,-2.95063 0,0 18.39873,-19.40688 28.73226,-37.55356 -41.33413,-0.25205 -364.14945,3.23322 -364.14945,3.23322 z"
           id="path3576"
           inkscape:connector-curvature="0"
           sodipodi:nodetypes="ccccc" /><path
           transform="matrix(1.25,0,0,-1.25,0,990)"
           style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 208.21343,538.27798 277.34946,-3.48528 c 0,0 -34.54921,30.89095 -64.85267,39.09636 -20.66706,0.50408 -148.33167,1.15579 -148.33167,1.15579 0,0 -35.4761,-11.99339 -64.16512,-36.76687 z"
           id="path3578"
           inkscape:connector-curvature="0"
           sodipodi:nodetypes="ccccc" /><path
           transform="matrix(1.25,0,0,-1.25,0,990)"
           style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 446.79083,363.29602 0,-7.57424 -11.58413,0 0,-9.53463 11.49503,0 0,-6.23761 -11.76235,0 0,-9.62373 11.85145,0 0,-6.59404 56.4949,0 c 0,0 38.49493,6.86137 38.49493,45.35631 0,38.49494 -42.59393,44.82166 -42.59393,44.82166 l -363.02869,3.74256 c 0,0 -5.43563,-22.45538 -5.43563,-40.00979 15.77223,0 363.1178,-2.67326 363.1178,-2.67326 0,0 6.86137,0.35644 6.86137,-6.1485 0,-6.50493 -6.23761,-5.52473 -6.23761,-5.52473 z"
           id="path3572"
           inkscape:connector-curvature="0"
           sodipodi:nodetypes="cccccccccccsccccscc" /></g><path
         inkscape:label="#circle4400"
         inkscape:connector-curvature="0"
         id="ring"
         d="M 88.506113,265.74856 A 40.577442,40.577442 0 0 0 47.928702,306.32597 40.577442,40.577442 0 0 0 88.506113,346.90365 40.577442,40.577442 0 0 0 129.0838,306.32597 40.577442,40.577442 0 0 0 88.506113,265.74856 Z m 0,2.02535 A 38.375756,38.552254 0 0 1 126.88201,306.32597 38.375756,38.552254 0 0 1 88.506113,344.8783 38.375756,38.552254 0 0 1 50.130497,306.32597 38.375756,38.552254 0 0 1 88.506113,267.77391 Z"
         style="display:inline;fill:#53a6c2;fill-opacity:1;stroke:none;stroke-width:1.16711712;stroke-opacity:1" /></g><text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:5.5970993px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="3.6412585"
       y="379.60495"
       id="text4175"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan4177"
         x="3.6412585"
         y="379.60495" /></text>
<text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:10.07446671px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="48.558857"
       y="344.43402"
       id="text4177"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         x="48.558857"
         y="344.43402"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.29654121px;font-family:Dottie;-inkscape-font-specification:Dottie"
         id="tspan4185" /></text>
<text
       sodipodi:linespacing="125%"
       id="text4251"
       y="445.44971"
       x="3.6412585"
       style="font-style:normal;font-weight:normal;font-size:5.5970993px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       xml:space="preserve"><tspan
         y="445.44971"
         x="3.6412585"
         id="tspan4253"
         sodipodi:role="line" /></text>
<ellipse
       ry="37.22398"
       rx="39.077209"
       style="display:inline;fill:none;fill-opacity:1;stroke:none;stroke-width:0.99528235;stroke-opacity:1"
       id="ellipse4269"
       cx="188.95584"
       cy="309.14252" /><g
       id="g4403"
       transform="translate(-3.4700316,-242.58675)"><rect
         style="fill:none;fill-opacity:1;stroke:#dcdcdc;stroke-width:0.33938482;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
         id="rect4249"
         width="98.517426"
         height="51.003555"
         x="42.359982"
         y="431.91147" /><text
         xml:space="preserve"
         style="font-style:normal;font-weight:normal;font-size:12.70192051px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         x="46.723854"
         y="443.1936"
         id="text4255"
         sodipodi:linespacing="125%"><tspan
           sodipodi:role="line"
           id="tspan4257"
           x="46.723854"
           y="443.1936"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie">BIN LOADER</tspan><tspan
           id="tspan4425"
           sodipodi:role="line"
           x="46.723854"
           y="453.11697"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie">DEC-08-LBAA-PM</tspan><tspan
           sodipodi:role="line"
           x="46.723854"
           y="463.04034"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie"
           id="tspan4261">5/10/67  SA:7777</tspan><tspan
           sodipodi:role="line"
           x="46.723854"
           y="472.96371"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie"
           id="tspan4263" /></text>
<g
         id="g4393"
         transform="translate(-0.5513317,64.832146)"><g
           transform="matrix(0.03699838,0,0,-0.03699838,88.960048,419.11479)"
           id="g4395"
           style="display:inline;fill:#1e97ec;fill-opacity:1;stroke:none"
           inkscape:label="#g3477"><g
             clip-path="url(#clipPath18)"
             id="g4397"
             style="fill:#1e97ec;fill-opacity:1;stroke:none"><path
               inkscape:connector-curvature="0"
               id="path4399"
               style="fill:#1e97ec;fill-opacity:1;fill-rule:nonzero;stroke:none"
               d="m 101.3,107.13 c 0.02,-2.96 -3.43,-7.55 -7.63,-7.56 -4.48,0.02 -8.87,4.08 -8.87,12.2 0,8.24 4.38,12.48 8.73,12.5 4.34,-0.02 7.79,-4.67 7.77,-8.2 l 0,-8.94 z m 0.23,22.74 0,18.13 8.9,0 0,-56.4 -9.2,0 0,2.6 c -0.75,-0.76 -3.92,-4.07 -9.33,-4.07 -5.64,-0.01 -15.73,4.69 -15.73,21.64 0,14.37 7.54,21.8 16.5,21.8 3.32,0 6.58,-1.18 8.86,-3.7 z M 72,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m 7,0 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 61.18,-20.72 c 0,3.59 -4.04,7.3 -7.78,7.3 -3.22,0 -8.2,-2.05 -8.2,-11.24 0,-9.79 6.12,-11.22 8.15,-11.22 4.03,0 7.83,2.77 7.83,7.05 l 0,8.11 z M 171,172 l 42.5,0 0,-100 -42.5,0 0,100 z m 28.52,-43.91 c -0.93,1.33 -3.98,4.35 -8.34,4.35 -6.77,-0.01 -16.21,-3.92 -16.21,-20.89 0.01,-13.38 10.7,-17.87 15.09,-17.87 4.74,-0.01 8.26,2.75 9.22,3.79 l 0,-4.21 c 0,-3.11 -3.19,-6.15 -8.53,-6.15 -4.6,0.01 -5.08,2.96 -5.08,4.42 l -9.4,0 c 0,-4.67 3.01,-11.49 13.91,-11.5 9.76,0 12.98,3.73 14.37,5.13 1.17,1.18 2.92,4.42 2.92,7.01 l 0,39.83 -7.94,0 -0.01,-3.91 z M 220.5,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 56.03,9.75 -8.23,0 0,-14.54 -4.31,0 0,-6.75 4.31,0 0,-25.44 c 0,-4.85 2.49,-8.66 9.43,-8.65 3.25,0 3.12,-0.02 5.94,0.47 l 0,8.4 c -1.84,-0.32 -2.41,-0.45 -3.31,-0.42 -1.73,0.05 -3.83,0.66 -3.83,3.74 l 0,21.89 6.52,0 0,6.76 -6.52,0 0,14.54 z M 312.5,72 270,72 l 0,100 42.5,0 0,-100 z m 49.5,0 -42.5,0 0,100 42.5,0 0,-100 z m -42.5,0 z m 6.99,47.28 7.71,0 c 0,4.1 2.12,6.25 6.16,6.25 6.44,0.01 6.23,-2.86 6.22,-6.45 -3.98,-1.55 -3.61,-1.51 -9.68,-2.89 -7.71,-1.78 -12.43,-5.08 -12.44,-13.07 0.01,-7.81 5.57,-11.98 11.84,-11.97 6.28,-0.01 7.58,2.02 10.18,3.75 l 0,-3.12 9.84,0 c -2.32,3.74 -1.71,4.47 -1.73,7.28 0.01,2.2 0,23.74 0,23.74 0.01,4.27 -2.21,6.88 -3.66,7.75 -3.62,2.16 -8.09,2.42 -10.57,2.43 -8.81,-0.02 -13.88,-4.45 -13.87,-13.7 z m 20.09,-7.82 0,-5.45 c 0,-2.49 -3.03,-7.31 -8.98,-7.32 -2.9,-0.01 -4.52,2.14 -4.53,4.93 0.01,2.8 2.38,4.95 4.39,5.35 2.09,0.42 6.9,1.65 9.12,2.49 z M 369,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,56.9 9,0 0,-56.9 z" /></g></g><path
           id="path4401"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 106.30956,411.81751 0,4.57684 0.9383,-0.0104 c 0,0 2.97311,0.1668 2.98173,-2.40832 0.008,-2.34582 -2.96088,-2.18938 -2.96088,-2.18938 z m 0.88618,0.87575 0,2.80449 0.45873,0 c 0,0 1.68636,0.0835 1.69937,-1.54299 0.0104,-1.3032 -1.60555,-1.28235 -1.60555,-1.28235 z"
           inkscape:connector-curvature="0" /><path
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 110.35469,411.70282 0,4.62897 2.3979,0 0,-0.86532 -1.40746,0 0,-1.03213 1.35532,0 0,-0.87575 -1.3866,0 0,-0.94874 1.37618,0 0,-0.90703 z"
           id="path4403"
           inkscape:connector-curvature="0" /><path
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 116.52666,413.02688 0.96958,0 c 0,0 -0.46915,-1.49086 -2.28321,-1.49086 -1.81406,0 -2.38747,1.59511 -2.38747,2.50214 0,0.90703 0.86533,2.29363 2.523,2.29363 1.65767,0 2.1581,-1.36575 2.1581,-1.36575 l -0.98,0 c 0,0 -0.23979,0.47958 -1.16767,0.47958 -0.92789,0 -1.60555,-0.66724 -1.60555,-1.49086 0,-0.82363 0.63596,-1.44916 1.49086,-1.44916 0.8549,0 1.28236,0.52128 1.28236,0.52128 z"
           id="path4405"
           inkscape:connector-curvature="0" /><path
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 117.6839,411.70282 0,4.62897 1.11554,0 0,-3.64895 -0.80277,0 0,-0.98002 z"
           id="path4407"
           inkscape:connector-curvature="0" /><path
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 119.64391,416.33179 0,-3.65938 0.86533,0 0.0208,-0.96959 2.47087,0 z"
           id="path4409"
           inkscape:connector-curvature="0" /><path
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 120.79073,416.33179 0.90703,-1.27192 1.04255,0 0,1.27192 z"
           id="path4411"
           inkscape:connector-curvature="0" /><path
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 122.25032,414.2571 0.50043,-0.70894 0,0.69851 z"
           id="path4413"
           inkscape:connector-curvature="0"
           sodipodi:nodetypes="cccc" /><path
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 123.60565,411.70282 0,4.62897 0.35447,0 0,-4.62897 z"
           id="path4415"
           inkscape:connector-curvature="0" /><path
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 124.80459,412.56815 0,1.19894 0.4483,0.0104 c 0,0 0.66724,-0.0834 0.66724,-0.61511 0,-0.53171 -0.68809,-0.57341 -0.68809,-0.57341 z"
           id="path4417"
           inkscape:connector-curvature="0" /><path
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 124.84629,414.54901 0,1.78278 2.04342,0 0,-4.62897 -1.23022,0 c 0,0 1.04256,0.23979 1.04256,1.55342 0,1.31363 -1.27192,1.3032 -1.27192,1.3032 z"
           id="path4419"
           inkscape:connector-curvature="0" /><path
           style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           d="m 129.14164,411.70282 0,0.88618 -1.35533,0 0,1.11554 1.34491,0 0,0.7298 -1.37619,0 0,1.12596 1.38661,0 0,0.77149 6.60983,0 c 0,0 4.50387,-0.80276 4.50387,-5.30663 0,-4.50386 -4.98345,-5.24408 -4.98345,-5.24408 l -90.87296,-0.43787 c 0,0 -0.04525,2.62725 -0.04525,4.68109 1.845329,0 90.29268,0.31277 90.29268,0.31277 0,0 0.80277,-0.0417 0.80277,0.71937 0,0.76107 -0.72979,0.64638 -0.72979,0.64638 z"
           id="path4421"
           inkscape:connector-curvature="0"
           sodipodi:nodetypes="cccccccccccsccccscc" /></g></g><g
       id="g4381"
       transform="translate(-3.4700316,-242.58675)"><rect
         y="366.06671"
         x="42.359982"
         height="51.003555"
         width="98.517426"
         id="rect4187"
         style="fill:none;fill-opacity:1;stroke:#dcdcdc;stroke-width:0.33938482;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><g
         transform="translate(-0.5513317,-0.5513317)"
         id="g4377"><g
           inkscape:label="#g3477"
           style="display:inline;fill:#1e97ec;fill-opacity:1;stroke:none"
           id="g4271"
           transform="matrix(0.03699838,0,0,-0.03699838,88.960048,419.11479)"><g
             style="fill:#1e97ec;fill-opacity:1;stroke:none"
             id="g4273"
             clip-path="url(#clipPath18)"><path
               d="m 101.3,107.13 c 0.02,-2.96 -3.43,-7.55 -7.63,-7.56 -4.48,0.02 -8.87,4.08 -8.87,12.2 0,8.24 4.38,12.48 8.73,12.5 4.34,-0.02 7.79,-4.67 7.77,-8.2 l 0,-8.94 z m 0.23,22.74 0,18.13 8.9,0 0,-56.4 -9.2,0 0,2.6 c -0.75,-0.76 -3.92,-4.07 -9.33,-4.07 -5.64,-0.01 -15.73,4.69 -15.73,21.64 0,14.37 7.54,21.8 16.5,21.8 3.32,0 6.58,-1.18 8.86,-3.7 z M 72,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m 7,0 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 61.18,-20.72 c 0,3.59 -4.04,7.3 -7.78,7.3 -3.22,0 -8.2,-2.05 -8.2,-11.24 0,-9.79 6.12,-11.22 8.15,-11.22 4.03,0 7.83,2.77 7.83,7.05 l 0,8.11 z M 171,172 l 42.5,0 0,-100 -42.5,0 0,100 z m 28.52,-43.91 c -0.93,1.33 -3.98,4.35 -8.34,4.35 -6.77,-0.01 -16.21,-3.92 -16.21,-20.89 0.01,-13.38 10.7,-17.87 15.09,-17.87 4.74,-0.01 8.26,2.75 9.22,3.79 l 0,-4.21 c 0,-3.11 -3.19,-6.15 -8.53,-6.15 -4.6,0.01 -5.08,2.96 -5.08,4.42 l -9.4,0 c 0,-4.67 3.01,-11.49 13.91,-11.5 9.76,0 12.98,3.73 14.37,5.13 1.17,1.18 2.92,4.42 2.92,7.01 l 0,39.83 -7.94,0 -0.01,-3.91 z M 220.5,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 56.03,9.75 -8.23,0 0,-14.54 -4.31,0 0,-6.75 4.31,0 0,-25.44 c 0,-4.85 2.49,-8.66 9.43,-8.65 3.25,0 3.12,-0.02 5.94,0.47 l 0,8.4 c -1.84,-0.32 -2.41,-0.45 -3.31,-0.42 -1.73,0.05 -3.83,0.66 -3.83,3.74 l 0,21.89 6.52,0 0,6.76 -6.52,0 0,14.54 z M 312.5,72 270,72 l 0,100 42.5,0 0,-100 z m 49.5,0 -42.5,0 0,100 42.5,0 0,-100 z m -42.5,0 z m 6.99,47.28 7.71,0 c 0,4.1 2.12,6.25 6.16,6.25 6.44,0.01 6.23,-2.86 6.22,-6.45 -3.98,-1.55 -3.61,-1.51 -9.68,-2.89 -7.71,-1.78 -12.43,-5.08 -12.44,-13.07 0.01,-7.81 5.57,-11.98 11.84,-11.97 6.28,-0.01 7.58,2.02 10.18,3.75 l 0,-3.12 9.84,0 c -2.32,3.74 -1.71,4.47 -1.73,7.28 0.01,2.2 0,23.74 0,23.74 0.01,4.27 -2.21,6.88 -3.66,7.75 -3.62,2.16 -8.09,2.42 -10.57,2.43 -8.81,-0.02 -13.88,-4.45 -13.87,-13.7 z m 20.09,-7.82 0,-5.45 c 0,-2.49 -3.03,-7.31 -8.98,-7.32 -2.9,-0.01 -4.52,2.14 -4.53,4.93 0.01,2.8 2.38,4.95 4.39,5.35 2.09,0.42 6.9,1.65 9.12,2.49 z M 369,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,56.9 9,0 0,-56.9 z"
               style="fill:#1e97ec;fill-opacity:1;fill-rule:nonzero;stroke:none"
               id="path4275"
               inkscape:connector-curvature="0" /></g></g><path
           inkscape:connector-curvature="0"
           d="m 106.30956,411.81751 0,4.57684 0.9383,-0.0104 c 0,0 2.97311,0.1668 2.98173,-2.40832 0.008,-2.34582 -2.96088,-2.18938 -2.96088,-2.18938 z m 0.88618,0.87575 0,2.80449 0.45873,0 c 0,0 1.68636,0.0835 1.69937,-1.54299 0.0104,-1.3032 -1.60555,-1.28235 -1.60555,-1.28235 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
           id="path4295" /><path
           inkscape:connector-curvature="0"
           id="path4297"
           d="m 110.35469,411.70282 0,4.62897 2.3979,0 0,-0.86532 -1.40746,0 0,-1.03213 1.35532,0 0,-0.87575 -1.3866,0 0,-0.94874 1.37618,0 0,-0.90703 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
           inkscape:connector-curvature="0"
           id="path4299"
           d="m 116.52666,413.02688 0.96958,0 c 0,0 -0.46915,-1.49086 -2.28321,-1.49086 -1.81406,0 -2.38747,1.59511 -2.38747,2.50214 0,0.90703 0.86533,2.29363 2.523,2.29363 1.65767,0 2.1581,-1.36575 2.1581,-1.36575 l -0.98,0 c 0,0 -0.23979,0.47958 -1.16767,0.47958 -0.92789,0 -1.60555,-0.66724 -1.60555,-1.49086 0,-0.82363 0.63596,-1.44916 1.49086,-1.44916 0.8549,0 1.28236,0.52128 1.28236,0.52128 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
           inkscape:connector-curvature="0"
           id="path4301"
           d="m 117.6839,411.70282 0,4.62897 1.11554,0 0,-3.64895 -0.80277,0 0,-0.98002 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
           inkscape:connector-curvature="0"
           id="path4303"
           d="m 119.64391,416.33179 0,-3.65938 0.86533,0 0.0208,-0.96959 2.47087,0 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
           inkscape:connector-curvature="0"
           id="path4305"
           d="m 120.79073,416.33179 0.90703,-1.27192 1.04255,0 0,1.27192 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
           sodipodi:nodetypes="cccc"
           inkscape:connector-curvature="0"
           id="path4307"
           d="m 122.25032,414.2571 0.50043,-0.70894 0,0.69851 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
           inkscape:connector-curvature="0"
           id="path4309"
           d="m 123.60565,411.70282 0,4.62897 0.35447,0 0,-4.62897 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
           inkscape:connector-curvature="0"
           id="path4311"
           d="m 124.80459,412.56815 0,1.19894 0.4483,0.0104 c 0,0 0.66724,-0.0834 0.66724,-0.61511 0,-0.53171 -0.68809,-0.57341 -0.68809,-0.57341 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
           inkscape:connector-curvature="0"
           id="path4313"
           d="m 124.84629,414.54901 0,1.78278 2.04342,0 0,-4.62897 -1.23022,0 c 0,0 1.04256,0.23979 1.04256,1.55342 0,1.31363 -1.27192,1.3032 -1.27192,1.3032 z"
           style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
           sodipodi:nodetypes="cccccccccccsccccscc"
           inkscape:connector-curvature="0"
           id="path4323"
           d="m 129.14164,411.70282 0,0.88618 -1.35533,0 0,1.11554 1.34491,0 0,0.7298 -1.37619,0 0,1.12596 1.38661,0 0,0.77149 6.60983,0 c 0,0 4.50387,-0.80276 4.50387,-5.30663 0,-4.50386 -4.98345,-5.24408 -4.98345,-5.24408 l -90.87296,-0.43787 c 0,0 -0.04525,2.62725 -0.04525,4.68109 1.845329,0 90.29268,0.31277 90.29268,0.31277 0,0 0.80277,-0.0417 0.80277,0.71937 0,0.76107 -0.72979,0.64638 -0.72979,0.64638 z"
           style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /></g><text
         sodipodi:linespacing="125%"
         id="text4427"
         y="378.7012"
         x="46.723854"
         style="font-style:normal;font-weight:normal;font-size:12.70192051px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         xml:space="preserve"><tspan
           id="tspan4455"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie"
           y="378.7012"
           x="46.723854"
           sodipodi:role="line">FOCAL-69</tspan><tspan
           id="tspan4459"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie"
           y="388.62457"
           x="46.723854"
           sodipodi:role="line">DEC-08-AJAB-PB</tspan><tspan
           id="tspan4465"
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie"
           y="398.54794"
           x="46.723854"
           sodipodi:role="line">4/29/68  SA:0200</tspan></text>
</g><rect
       style="fill:none;fill-opacity:1;stroke:#dcdcdc;stroke-width:0.33938482;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="rect4428"
       width="98.517426"
       height="51.003555"
       x="38.88995"
       y="254.30894" /><text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:12.70192051px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="43.253822"
       y="265.59109"
       id="text4430"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan4432"
         x="43.253822"
         y="265.59109"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie">ADD.PA</tspan><tspan
         id="tspan4434"
         sodipodi:role="line"
         x="43.253822"
         y="275.51447"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie">PIDP-8/I Example</tspan><tspan
         sodipodi:role="line"
         x="43.253822"
         y="285.43784"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie"
         id="tspan4436">11/27/16 SA:0200</tspan><tspan
         sodipodi:role="line"
         x="43.253822"
         y="295.36121"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie"
         id="tspan4438" /></text>
<g
       id="g4440"
       transform="translate(-4.0213633,-112.77037)"><g
         transform="matrix(0.03699838,0,0,-0.03699838,88.960048,419.11479)"
         id="g4442"
         style="display:inline;fill:#1e97ec;fill-opacity:1;stroke:none"
         inkscape:label="#g3477"><g
           clip-path="url(#clipPath18)"
           id="g4444"
           style="fill:#1e97ec;fill-opacity:1;stroke:none"><path
             inkscape:connector-curvature="0"
             id="path4446"
             style="fill:#1e97ec;fill-opacity:1;fill-rule:nonzero;stroke:none"
             d="m 101.3,107.13 c 0.02,-2.96 -3.43,-7.55 -7.63,-7.56 -4.48,0.02 -8.87,4.08 -8.87,12.2 0,8.24 4.38,12.48 8.73,12.5 4.34,-0.02 7.79,-4.67 7.77,-8.2 l 0,-8.94 z m 0.23,22.74 0,18.13 8.9,0 0,-56.4 -9.2,0 0,2.6 c -0.75,-0.76 -3.92,-4.07 -9.33,-4.07 -5.64,-0.01 -15.73,4.69 -15.73,21.64 0,14.37 7.54,21.8 16.5,21.8 3.32,0 6.58,-1.18 8.86,-3.7 z M 72,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m 7,0 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 61.18,-20.72 c 0,3.59 -4.04,7.3 -7.78,7.3 -3.22,0 -8.2,-2.05 -8.2,-11.24 0,-9.79 6.12,-11.22 8.15,-11.22 4.03,0 7.83,2.77 7.83,7.05 l 0,8.11 z M 171,172 l 42.5,0 0,-100 -42.5,0 0,100 z m 28.52,-43.91 c -0.93,1.33 -3.98,4.35 -8.34,4.35 -6.77,-0.01 -16.21,-3.92 -16.21,-20.89 0.01,-13.38 10.7,-17.87 15.09,-17.87 4.74,-0.01 8.26,2.75 9.22,3.79 l 0,-4.21 c 0,-3.11 -3.19,-6.15 -8.53,-6.15 -4.6,0.01 -5.08,2.96 -5.08,4.42 l -9.4,0 c 0,-4.67 3.01,-11.49 13.91,-11.5 9.76,0 12.98,3.73 14.37,5.13 1.17,1.18 2.92,4.42 2.92,7.01 l 0,39.83 -7.94,0 -0.01,-3.91 z M 220.5,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 56.03,9.75 -8.23,0 0,-14.54 -4.31,0 0,-6.75 4.31,0 0,-25.44 c 0,-4.85 2.49,-8.66 9.43,-8.65 3.25,0 3.12,-0.02 5.94,0.47 l 0,8.4 c -1.84,-0.32 -2.41,-0.45 -3.31,-0.42 -1.73,0.05 -3.83,0.66 -3.83,3.74 l 0,21.89 6.52,0 0,6.76 -6.52,0 0,14.54 z M 312.5,72 270,72 l 0,100 42.5,0 0,-100 z m 49.5,0 -42.5,0 0,100 42.5,0 0,-100 z m -42.5,0 z m 6.99,47.28 7.71,0 c 0,4.1 2.12,6.25 6.16,6.25 6.44,0.01 6.23,-2.86 6.22,-6.45 -3.98,-1.55 -3.61,-1.51 -9.68,-2.89 -7.71,-1.78 -12.43,-5.08 -12.44,-13.07 0.01,-7.81 5.57,-11.98 11.84,-11.97 6.28,-0.01 7.58,2.02 10.18,3.75 l 0,-3.12 9.84,0 c -2.32,3.74 -1.71,4.47 -1.73,7.28 0.01,2.2 0,23.74 0,23.74 0.01,4.27 -2.21,6.88 -3.66,7.75 -3.62,2.16 -8.09,2.42 -10.57,2.43 -8.81,-0.02 -13.88,-4.45 -13.87,-13.7 z m 20.09,-7.82 0,-5.45 c 0,-2.49 -3.03,-7.31 -8.98,-7.32 -2.9,-0.01 -4.52,2.14 -4.53,4.93 0.01,2.8 2.38,4.95 4.39,5.35 2.09,0.42 6.9,1.65 9.12,2.49 z M 369,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,56.9 9,0 0,-56.9 z" /></g></g><path
         id="path4448"
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 106.30956,411.81751 0,4.57684 0.9383,-0.0104 c 0,0 2.97311,0.1668 2.98173,-2.40832 0.008,-2.34582 -2.96088,-2.18938 -2.96088,-2.18938 z m 0.88618,0.87575 0,2.80449 0.45873,0 c 0,0 1.68636,0.0835 1.69937,-1.54299 0.0104,-1.3032 -1.60555,-1.28235 -1.60555,-1.28235 z"
         inkscape:connector-curvature="0" /><path
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 110.35469,411.70282 0,4.62897 2.3979,0 0,-0.86532 -1.40746,0 0,-1.03213 1.35532,0 0,-0.87575 -1.3866,0 0,-0.94874 1.37618,0 0,-0.90703 z"
         id="path4450"
         inkscape:connector-curvature="0" /><path
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 116.52666,413.02688 0.96958,0 c 0,0 -0.46915,-1.49086 -2.28321,-1.49086 -1.81406,0 -2.38747,1.59511 -2.38747,2.50214 0,0.90703 0.86533,2.29363 2.523,2.29363 1.65767,0 2.1581,-1.36575 2.1581,-1.36575 l -0.98,0 c 0,0 -0.23979,0.47958 -1.16767,0.47958 -0.92789,0 -1.60555,-0.66724 -1.60555,-1.49086 0,-0.82363 0.63596,-1.44916 1.49086,-1.44916 0.8549,0 1.28236,0.52128 1.28236,0.52128 z"
         id="path4452"
         inkscape:connector-curvature="0" /><path
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 117.6839,411.70282 0,4.62897 1.11554,0 0,-3.64895 -0.80277,0 0,-0.98002 z"
         id="path4454"
         inkscape:connector-curvature="0" /><path
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 119.64391,416.33179 0,-3.65938 0.86533,0 0.0208,-0.96959 2.47087,0 z"
         id="path4456"
         inkscape:connector-curvature="0" /><path
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 120.79073,416.33179 0.90703,-1.27192 1.04255,0 0,1.27192 z"
         id="path4458"
         inkscape:connector-curvature="0" /><path
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 122.25032,414.2571 0.50043,-0.70894 0,0.69851 z"
         id="path4460"
         inkscape:connector-curvature="0"
         sodipodi:nodetypes="cccc" /><path
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 123.60565,411.70282 0,4.62897 0.35447,0 0,-4.62897 z"
         id="path4462"
         inkscape:connector-curvature="0" /><path
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 124.80459,412.56815 0,1.19894 0.4483,0.0104 c 0,0 0.66724,-0.0834 0.66724,-0.61511 0,-0.53171 -0.68809,-0.57341 -0.68809,-0.57341 z"
         id="path4464"
         inkscape:connector-curvature="0" /><path
         style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 124.84629,414.54901 0,1.78278 2.04342,0 0,-4.62897 -1.23022,0 c 0,0 1.04256,0.23979 1.04256,1.55342 0,1.31363 -1.27192,1.3032 -1.27192,1.3032 z"
         id="path4466"
         inkscape:connector-curvature="0" /><path
         style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         d="m 129.14164,411.70282 0,0.88618 -1.35533,0 0,1.11554 1.34491,0 0,0.7298 -1.37619,0 0,1.12596 1.38661,0 0,0.77149 6.60983,0 c 0,0 4.50387,-0.80276 4.50387,-5.30663 0,-4.50386 -4.98345,-5.24408 -4.98345,-5.24408 l -90.87296,-0.43787 c 0,0 -0.04525,2.62725 -0.04525,4.68109 1.845329,0 90.29268,0.31277 90.29268,0.31277 0,0 0.80277,-0.0417 0.80277,0.71937 0,0.76107 -0.72979,0.64638 -0.72979,0.64638 z"
         id="path4468"
         inkscape:connector-curvature="0"
         sodipodi:nodetypes="cccccccccccsccccscc" /></g></g></svg>
Added media/copytoUSBsticks/binloader.pt.

cannot compute difference between binary files

Added media/copytoUSBsticks/focal69.pt.

cannot compute difference between binary files

Added media/copytoUSBsticks/readme.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The PiDP is typically used with a USB hub as its 'PiDP Universal Storage Device'.

Image files (disk images, paper tape images, DECtape images) are then stored on a USB stick,
and when inserted to the USB hub the first image file can be mounted into the emulated device.

The image files in this directory are typical candidates to put on USB sticks.

Mounting works as follows:

1. Select the device you want to mount on by setting the Data Field switches

   Switch Settings                                                   File Extension
   --------------------------------------------------------------------------------
        000 - mount USB paper tape on the high-speed paper tape reader   .pt
        001 - mount USB paper tape on the paper tape punch               .pt
        010 - mount DECtape on DT0 (TU55)                                .dt
        011 - mount DECtape on DT1 (TU55)                                .dt
        100 - mount 8" floppy disk on RX0 (RX01/02)                      .rx
        101 - mount 8" floppy disk on RX1 (RX01/02)                      .rx
        110 - mount 10MB removable disk cartridge on RL0 (RL8A)          .rl
        111 - mount 10MB removable disk cartridge on RL1 (RL8A)          .rl

2. Toggle Sing_Step and Sing_Inst switches together

3. The PiDP will scan all inserted USB sticks and mount the first unmounted image file for that device.
   Scanning requires the image file to have the extension as per the above table.
   This is equivalent to using the attach command from the simh command line.

Notes:

- Multiple image files can reside on one USB stick, as long as they do not have the same extension 
  (and your USB stick is large enough).

- You can put any other files on the sticks too, the PiDP will just ignore them.

- You can, of course, also just use the simh attach command to mount any image files on the SD card,
  and ignore the "PiDP Universal USB Storage Device" altogether.
Added media/etos/etos.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
At main console:
----------------
To run ETOS: R ETOS to start the operating system. Hit return at the option prompt.

To login enter LOGIN and then hit return which should then give the login prompt.  No prompt will be displayed for entering the login command. At the prompt enter account number such as 0,3 and hit return. Then enter the password at the password prompt. On the distribution ETOS pack the following users exist:

At terminal (telnet localhost 4000):
-----------

LOGIN;0,4 USER1 <CTRL-M>


Users:
------
Account Password
0,4     USER1
0,5     USER2

Shutdown:
---------
You can To shutdown enter
. ^VS      (^V is control-V)
!PRIV 4040
!SHUTUP 

See:
----
http://www.pdp8.net/os/etos/ (introduction)
http://highgate.comm.sfu.ca/pdp8/index.html (manuals, search page for ETOS)
ftp://ftp.pdp8online.com/images/etos/ (disk images)
Added media/etos/etosv5b-demo.rk05.
Added media/os8/LICENSE.md.






































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Digital License Agreement

This document is your Proof of License and the legal agreement governing
your use of the OS/8 software.


## 1 DEFINITION

SOFTWARE TECHNOLOGY shall mean the sources and binaries to the OS/8, an
operating system that runs on PDP-8 computers.

DIGITAL’S INTELLECTUAL PROPERTY RIGHTS shall mean DIGITAL’s patent,
copyright and trade secret rights in its SOFTWARE TECHNOLOGY.


## 2 LICENSE GRANT

Digital grants to Customer a worldwide, non-exclusive, royalty-free
license under DIGITAL’s INTELLECTUAL PROPERTY RIGHTS to reproduce,
modify, use and distribute the SOFTWARE TECHNOLOGY solely for
non-commercial uses.


## 3 TECHNOLOGY TRANSFER AND ACCEPTANCE

3.1 CUSTOMER acknowledges that it accepts the SOFTWARE TECHNOLOGY "AS
IS".

3.2 DIGITAL is under no obligation to supply error corrections or
updates to the SOFTWARE TECHNOLOGY as they become available, or to
provide training, support or consulting for the SOFTWARE TECHNOLOGY.


## 4 WARRANTY DISCLAIMER/LIMITATION OF LIABILITY

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO ANY SOFTWARE TECHNOLOGY
LICENSED TO CUSTOMER HEREUNDER, INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL DIGITAL BE LIABLE FOR
ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
CONTRACT, NEGLIGENCE, INTELLECTUAL PROPERTY INFRINGEMENT OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF ANY SOFTWARE TECHNOLOGY LICENSE HEREUNDER.


## 5 INDEMNITY

CUSTOMER will hold DIGITAL harmless against all liabilities, demands,
damages, expenses, or losses arising out of use by CUSTOMER of SOFTWARE
TECHNOLOGY or information furnished under this Agreement.


## 6 TERM AND TERMINATION

6.1 This Agreement shall be effective until otherwise terminated.
Either party may terminate this Agreement at any time upon 30 days
written notice.

6.2 If CUSTOMER shall fail to perform or observe any of the terms and
conditions to be performed or observed by it under this Agreement,
DIGITAL may in its sole discretion thereafter elect to terminate this
Agreement, and this Agreement and all the obligations owed and rights
granted herein to CUSTOMER shall immediately terminate.

6.3 The parties agree that the termination of this Agreement shall not
release either party from any other liability which shall have accrued
to the other party at the time such termination becomes effective, nor
affect in any manner the survival of any right, duty or obligation of
either party.

6.4 In the event of any termination of this Agreement for any reason,
CUSTOMER shall delete all original and all whole or partial copies and
derivatives of the SOFTWARE TECHNOLOGY provided to CUSTOMER under this
Agreement.  CUSTOMER further shall cease to use and distribute the
SOFTWARE TECHNOLOGY in all forms immediately upon the date of
termination.


## 7 GENERAL TERMS

7.1 This Agreement shall be governed by the laws of the Commonwealth of
Massachusetts.

7.2 This Agreement imposes personal obligations on CUSTOMER.  CUSTOMER
shall not assign any rights under this Agreement not specifically
transferable by its terms without the written consent of DIGITAL.

7.3 The SOFTWARE TECHNOLOGY obtained under this Agreement may be subject
to US and other government export control regulations.  CUSTOMER assures
that it will comply with these regulations whenever it exports or
re-exports a controlled product or technical data obtained from DIGITAL
or any product produced directly from the SOFTWARE TECHNOLOGY.

7.4 The waiver of a breach hereunder may be effected only by a writing
signed by the waiving party and shall not constitute a waiver of any
other breach.

7.5 CUSTOMER acknowledges that he has read this Agreement, understands
it and agrees to be bound by its term and further agrees that it is the
complete and exclusive statement of the Agreement between the parties
which supersedes all communications and understanding between the
parties relating to the subject matter of this Agreement.
Added media/os8/os8.rk05.

cannot compute difference between binary files

Added media/os8/os8.tu56.

cannot compute difference between binary files

Added media/spacewar/spacewar.bin.

cannot compute difference between binary files

Added media/tss8/tss8_init.bin.

cannot compute difference between binary files

Added media/tss8/tss8_rf.dsk.

cannot compute difference between binary files

Added 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 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 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 pics/wy/back.jpg.

cannot compute difference between binary files

Added pics/wy/front.jpg.

cannot compute difference between binary files

Added pics/wy/power-switch.png.

cannot compute difference between binary files

Added pics/wy/serial-db9.jpg.

cannot compute difference between binary files

Added pics/wy/serial-kk.jpg.

cannot compute difference between binary files

Added pics/wy/system.jpg.

cannot compute difference between binary files

Added schematics/power-switch.gsch.


























































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
v 20130925 2
C 39600 60100 1 0 0 5V-plus-1.sym
{
T 39600 60100 5 10 1 1 0 0 1
pinnumber=1
}
C 39700 58200 1 0 0 gnd-1.sym
{
T 39600 58400 5 10 1 1 0 0 1
pinnumber=7
}
C 40100 58200 1 0 0 gnd-1.sym
{
T 40000 58400 5 10 1 1 0 0 1
pinnumber=8
}
C 40000 60100 1 0 0 5V-plus-1.sym
{
T 40000 60100 5 10 1 1 0 0 1
pinnumber=2
}
C 38500 60600 1 270 0 switch-spst-1.sym
{
T 39200 60200 5 10 0 0 270 0 1
device=SPST
T 38800 60400 5 10 1 1 270 0 1
refdes=Switch
}
N 40200 59300 40200 60100 4
N 39800 58500 39800 58700 4
N 40200 58500 40200 59000 4
C 37900 60300 1 0 0 BNC-1.sym
{
T 38250 60950 5 10 0 0 0 0 1
device=BNC
T 37900 61000 5 10 1 1 0 0 1
refdes=DC Jack
}
B 39400 57800 1200 3000 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1
{
T 40700 60100 5 10 1 1 270 0 1
name=Expansion Port Pins
}
N 38000 60300 38000 58700 4
N 38000 58700 39800 58700 4
N 38000 59000 40200 59000 4
N 39800 60100 39800 59600 4
N 38500 59600 39800 59600 4
N 38500 59300 38500 59800 4
N 40200 59300 38500 59300 4
N 38400 60800 38500 60800 4
N 38500 60800 38500 60600 4
T 37800 56300 9 10 1 0 0 0 5
Copyright © 2016 by Warren Young

This file is licensed under the terms of
the SIMH license, a copy of which is in
../SIMH-LICENSE.md.
Added src/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
47
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
########################################################################
# 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/ 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 the PiDP-8/I project's C source code 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/PDP8/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
47
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
########################################################################
# 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/PDP8 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 the PiDP-8/I project's C source code 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/PDP8/pdp8_clk.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_clk.c: PDP-8 real-time clock simulator

   Copyright (c) 1993-2012, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   clk          real time clock

   18-Apr-12    RMS     Added clock coscheduling
   18-Jun-07    RMS     Added UNIT_IDLE flag
   01-Mar-03    RMS     Aded SET/SHOW CLK FREQ support
   04-Oct-02    RMS     Added DIB, device number support
   30-Dec-01    RMS     Removed for generalized timers
   05-Sep-01    RMS     Added terminal multiplexor support
   17-Jul-01    RMS     Moved function prototype
   05-Mar-01    RMS     Added clock calibration support

   Note: includes the IOT's for both the PDP-8/E and PDP-8/A clocks
*/

#include "pdp8_defs.h"

extern int32 int_req, int_enable, dev_done, stop_inst;

int32 clk_tps = 60;                                     /* ticks/second */
int32 tmxr_poll = 16000;                                /* term mux poll */

int32 clk (int32 IR, int32 AC);
t_stat clk_svc (UNIT *uptr);
t_stat clk_reset (DEVICE *dptr);
t_stat clk_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc);

/* CLK data structures

   clk_dev      CLK device descriptor
   clk_unit     CLK unit descriptor
   clk_reg      CLK register list
*/

DIB clk_dib = { DEV_CLK, 1, { &clk } };

UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), 16000 };

REG clk_reg[] = {
    { FLDATAD (DONE, dev_done, INT_V_CLK, "device done flag") },
    { FLDATAD (ENABLE, int_enable, INT_V_CLK, "interrupt enable flag") },
    { FLDATAD (INT, int_req, INT_V_CLK, "interrupt pending flag") },
    { DRDATAD (TIME, clk_unit.wait, 24, "clock interval"), REG_NZ + PV_LEFT },
    { DRDATA (TPS, clk_tps, 8), PV_LEFT + REG_HRO },
    { NULL }
    };

MTAB clk_mod[] = {
    { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ",
      &clk_set_freq, NULL, NULL },
    { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ",
      &clk_set_freq, NULL, NULL },
    { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL,
      NULL, &clk_show_freq, NULL },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev },
    { 0 }
    };

DEVICE clk_dev = {
    "CLK", &clk_unit, clk_reg, clk_mod,
    1, 0, 0, 0, 0, 0,
    NULL, NULL, &clk_reset,
    NULL, NULL, NULL,
    &clk_dib, 0
    };

/* IOT routine

   IOT's 6131-6133 are the PDP-8/E clock
   IOT's 6135-6137 are the PDP-8/A clock
*/

int32 clk (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 1:                                             /* CLEI */
        int_enable = int_enable | INT_CLK;              /* enable clk ints */
        int_req = INT_UPDATE;                           /* update interrupts */
        return AC;

    case 2:                                             /* CLDI */
        int_enable = int_enable & ~INT_CLK;             /* disable clk ints */
        int_req = int_req & ~INT_CLK;                   /* update interrupts */
        return AC;

    case 3:                                             /* CLSC */
        if (dev_done & INT_CLK) {                       /* flag set? */
            dev_done = dev_done & ~INT_CLK;             /* clear flag */
            int_req = int_req & ~INT_CLK;               /* clear int req */
            return IOT_SKP + AC;
            }
        return AC;

    case 5:                                             /* CLLE */
        if (AC & 1)                                     /* test AC<11> */
            int_enable = int_enable | INT_CLK;
        else int_enable = int_enable & ~INT_CLK;
        int_req = INT_UPDATE;                           /* update interrupts */
        return AC;

    case 6:                                             /* CLCL */
        dev_done = dev_done & ~INT_CLK;                 /* clear flag */
        int_req = int_req & ~INT_CLK;                   /* clear int req */
        return AC;

    case 7:                                             /* CLSK */
        return (dev_done & INT_CLK)? IOT_SKP + AC: AC;

    default:
        return (stop_inst << IOT_V_REASON) + AC;
        }                                               /* end switch */
}

/* Unit service */

t_stat clk_svc (UNIT *uptr)
{
dev_done = dev_done | INT_CLK;                          /* set done */
int_req = INT_UPDATE;                                   /* update interrupts */
tmxr_poll = sim_rtcn_calb (clk_tps, TMR_CLK);           /* calibrate clock */
sim_activate_after (uptr, 1000000/clk_tps);             /* reactivate unit */
return SCPE_OK;
}

/* Reset routine */

t_stat clk_reset (DEVICE *dptr)
{
dev_done = dev_done & ~INT_CLK;                         /* clear done, int */
int_req = int_req & ~INT_CLK;
int_enable = int_enable & ~INT_CLK;                     /* clear enable */
if (!sim_is_running) {                                  /* RESET (not CAF)? */
    tmxr_poll = sim_rtcn_init_unit (&clk_unit, clk_unit.wait, TMR_CLK);/* init 100Hz timer */
    sim_activate_after (&clk_unit, 1000000/clk_tps);        /* activate 100Hz unit */
    }
return SCPE_OK;
}

/* Set frequency */

t_stat clk_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if (cptr)
    return SCPE_ARG;
if ((val != 50) && (val != 60))
    return SCPE_IERR;
clk_tps = val;
return SCPE_OK;
}

/* Show frequency */

t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fprintf (st, (clk_tps == 50)? "50Hz": "60Hz");
return SCPE_OK;
}
Added src/PDP8/pdp8_cpu.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_cpu.c: PDP-8 CPU simulator

   Copyright (c) 1993-2016, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

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

   Portions copyright (c) 2015-2017, Oscar Vermeulen, Ian Schofield, 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.

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

   cpu          central processor

   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
   30-Sep-06    RMS     Fixed SC value after DVI overflow (Don North)
   22-Sep-05    RMS     Fixed declarations (Sterling Garwood)
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   06-Nov-04    RMS     Added =n to SHOW HISTORY
   31-Dec-03    RMS     Fixed bug in set_cpu_hist
   13-Oct-03    RMS     Added instruction history
                        Added TSC8-75 support (Bernhard Baehr)
   12-Mar-03    RMS     Added logical name support
   04-Oct-02    RMS     Revamped device dispatching, added device number support
   06-Jan-02    RMS     Added device enable/disable routines
   30-Dec-01    RMS     Added old PC queue
   16-Dec-01    RMS     Fixed bugs in EAE
   07-Dec-01    RMS     Revised to use new breakpoint package
   30-Nov-01    RMS     Added RL8A, extended SET/SHOW support
   16-Sep-01    RMS     Fixed bug in reset routine, added KL8A support
   10-Aug-01    RMS     Removed register from declarations
   17-Jul-01    RMS     Moved function prototype
   07-Jun-01    RMS     Fixed bug in JMS to non-existent memory
   25-Apr-01    RMS     Added device enable/disable support
   18-Mar-01    RMS     Added DF32 support
   05-Mar-01    RMS     Added clock calibration support
   15-Feb-01    RMS     Added DECtape support
   14-Apr-99    RMS     Changed t_addr to unsigned

   The register state for the PDP-8 is:

   AC<0:11>             accumulator
   MQ<0:11>             multiplier-quotient
   L                    link flag
   PC<0:11>             program counter
   IF<0:2>              instruction field
   IB<0:2>              instruction buffer
   DF<0:2>              data field
   UF                   user flag
   UB                   user buffer
   SF<0:6>              interrupt save field

   The PDP-8 has three instruction formats: memory reference, I/O transfer,
   and operate.  The memory reference format is:

     0  1  2  3  4  5  6  7  8  9 10 11
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |   op   |in|zr|    page offset     |        memory reference
   +--+--+--+--+--+--+--+--+--+--+--+--+

   <0:2>        mnemonic        action

    000         AND             AC = AC & M[MA]
    001         TAD             L'AC = AC + M[MA]
    010         DCA             M[MA] = AC, AC = 0
    011         ISZ             M[MA] = M[MA] + 1, skip if M[MA] == 0
    100         JMS             M[MA] = PC, PC = MA + 1
    101         JMP             PC = MA

   <3:4>        mode            action
    00  page zero               MA = IF'0'IR<5:11>
    01  current page            MA = IF'PC<0:4>'IR<5:11>
    10  indirect page zero      MA = xF'M[IF'0'IR<5:11>]
    11  indirect current page   MA = xF'M[IF'PC<0:4>'IR<5:11>]

   where x is D for AND, TAD, ISZ, DCA, and I for JMS, JMP.

   Memory reference instructions can access an address space of 32K words.
   The address space is divided into eight 4K word fields; each field is
   divided into thirty-two 128 word pages.  An instruction can directly
   address, via its 7b offset, locations 0-127 on page zero or on the current
   page.  All 32k words can be accessed via indirect addressing and the
   instruction and data field registers.  If an indirect address is in
   locations 0010-0017 of any field, the indirect address is incremented
   and rewritten to memory before use.

   The I/O transfer format is as follows:

     0  1  2  3  4  5  6  7  8  9 10 11
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |   op   |      device     | pulse  |        I/O transfer
   +--+--+--+--+--+--+--+--+--+--+--+--+

   The IO transfer instruction sends the the specified pulse to the
   specified I/O device.  The I/O device may take data from the AC,
   return data to the AC, initiate or cancel operations, or skip on
   status.

   The operate format is as follows:

   +--+--+--+--+--+--+--+--+--+--+--+--+
   | 1| 1| 1| 0|  |  |  |  |  |  |  |  |        operate group 1
   +--+--+--+--+--+--+--+--+--+--+--+--+
                |  |  |  |  |  |  |  |
                |  |  |  |  |  |  |  +--- increment AC  3
                |  |  |  |  |  |  +--- rotate 1 or 2    4
                |  |  |  |  |  +--- rotate left         4
                |  |  |  |  +--- rotate right           4
                |  |  |  +--- complement L              2
                |  |  +--- complement AC                2
                |  +--- clear L                         1
                +-- clear AC                            1

   +--+--+--+--+--+--+--+--+--+--+--+--+
   | 1| 1| 1| 1|  |  |  |  |  |  |  | 0|        operate group 2
   +--+--+--+--+--+--+--+--+--+--+--+--+
                |  |  |  |  |  |  |
                |  |  |  |  |  |  +--- halt             3
                |  |  |  |  |  +--- or switch register  3
                |  |  |  |  +--- reverse skip sense     1
                |  |  |  +--- skip on L != 0            1
                |  |  +--- skip on AC == 0              1
                |  +--- skip on AC < 0                  1
                +-- clear AC                            2

   +--+--+--+--+--+--+--+--+--+--+--+--+
   | 1| 1| 1| 1|  |  |  |  |  |  |  | 1|        operate group 3
   +--+--+--+--+--+--+--+--+--+--+--+--+
                |  |  |  | \______/
                |  |  |  |     |
                |  |  +--|-----+--- EAE command         3
                |  |     +--- AC -> MQ, 0 -> AC         2
                |  +--- MQ v AC --> AC                  2
                +-- clear AC                            1

  The operate instruction can be microprogrammed to perform operations
  on the AC, MQ, and link.

  This routine is the instruction decode routine for the PDP-8.
   It is called from the simulator control program to execute
   instructions in simulated memory, starting at the simulated PC.
   It runs until 'reason' is set non-zero.

   General notes:

   1. Reasons to stop.  The simulator can be stopped by:

        HALT instruction
        breakpoint encountered
        unimplemented instruction and stop_inst flag set
        I/O error in I/O simulator

   2. Interrupts.  Interrupts are maintained by three parallel variables:

        dev_done        device done flags
        int_enable      interrupt enable flags
        int_req         interrupt requests

      In addition, int_req contains the interrupt enable flag, the
      CIF not pending flag, and the ION not pending flag.  If all
      three of these flags are set, and at least one interrupt request
      is set, then an interrupt occurs.

   3. Non-existent memory.  On the PDP-8, reads to non-existent memory
      return zero, and writes are ignored.  In the simulator, the
      largest possible memory is instantiated and initialized to zero.
      Thus, only writes outside the current field (indirect writes) need
      be checked against actual memory size.

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

/* ---PiDP change------------------------------------------------------------------------------------------- */
#include "pidp8i.h"
/* ---PiDP end---------------------------------------------------------------------------------------------- */

#define PCQ_SIZE        64                              /* must be 2**n */
#define PCQ_MASK        (PCQ_SIZE - 1)
#define PCQ_ENTRY       pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = MA
#define UNIT_V_NOEAE    (UNIT_V_UF)                     /* EAE absent */
#define UNIT_NOEAE      (1 << UNIT_V_NOEAE)
#define UNIT_V_MSIZE    (UNIT_V_UF + 1)                 /* dummy mask */
#define UNIT_MSIZE      (1 << UNIT_V_MSIZE)
#define OP_KSF          06031                           /* for idle */

#define HIST_PC         0x40000000
#define HIST_MIN        64
#define HIST_MAX        65536

typedef struct {
    int32               pc;
    int32               ea;
    int16               ir;
    int16               opnd;
    int16               lac;
    int16               mq;
    } InstHistory;

uint16 M[MAXMEMSIZE] = { 0 };                           /* main memory */
int32 saved_LAC = 0;                                    /* saved L'AC */
int32 saved_MQ = 0;                                     /* saved MQ */
int32 saved_PC = 0;                                     /* saved IF'PC */
int32 saved_DF = 0;                                     /* saved Data Field */
int32 IB = 0;                                           /* Instruction Buffer */
int32 SF = 0;                                           /* Save Field */
int32 emode = 0;                                        /* EAE mode */
int32 gtf = 0;                                          /* EAE gtf flag */
int32 SC = 0;                                           /* EAE shift count */
int32 UB = 0;                                           /* User mode Buffer */
int32 UF = 0;                                           /* User mode Flag */
int32 OSR = 0;                                          /* Switch Register */
int32 tsc_ir = 0;                                       /* TSC8-75 IR */
int32 tsc_pc = 0;                                       /* TSC8-75 PC */
int32 tsc_cdf = 0;                                      /* TSC8-75 CDF flag */
int32 tsc_enb = 0;                                      /* TSC8-75 enabled */
int32 cpu_astop = 0;                                    /* address stop */
int16 pcq[PCQ_SIZE] = { 0 };                            /* PC queue */
int32 pcq_p = 0;                                        /* PC queue ptr */
REG *pcq_r = NULL;                                      /* PC queue reg ptr */
int32 dev_done = 0;                                     /* dev done flags */
int32 int_enable = INT_INIT_ENABLE;                     /* intr enables */
int32 int_req = 0;                                      /* intr requests */
int32 stop_inst = 0;                                    /* trap on ill inst */
int32 (*dev_tab[DEV_MAX])(int32 IR, int32 dat);         /* device dispatch */
int32 hst_p = 0;                                        /* history pointer */
int32 hst_lnt = 0;                                      /* history length */
InstHistory *hst = NULL;                                /* instruction history */

t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_reset (DEVICE *dptr);
t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_bool build_dev_tab (void);

/* CPU data structures

   cpu_dev      CPU device descriptor
   cpu_unit     CPU unit descriptor
   cpu_reg      CPU register list
   cpu_mod      CPU modifier list
*/

UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) };

REG cpu_reg[] = {
    { ORDATAD (PC, saved_PC, 15, "program counter") },
    { ORDATAD (AC, saved_LAC, 12, "accumulator") },
    { FLDATAD (L, saved_LAC, 12, "link") },
    { ORDATAD (MQ, saved_MQ, 12, "multiplier-quotient") },
    { ORDATAD (SR, OSR, 12, "front panel switches") },
    { GRDATAD (IF, saved_PC, 8, 3, 12, "instruction field") },
    { GRDATAD (DF, saved_DF, 8, 3, 12, "data field") },
    { GRDATAD (IB, IB, 8, 3, 12, "instruction field buffter") },
    { ORDATAD (SF, SF, 7, "save field") },
    { FLDATAD (UB, UB, 0, "user mode buffer") },
    { FLDATAD (UF, UF, 0, "user mode flag") },
    { ORDATAD (SC, SC, 5, "EAE shift counter") },
    { FLDATAD (GTF, gtf, 0, "EAE greater than flag") },
    { FLDATAD (EMODE, emode, 0, "EAE mode (0 = A, 1 = B)") },
    { FLDATAD (ION, int_req, INT_V_ION, "interrupt enable") },
    { FLDATAD (ION_DELAY, int_req, INT_V_NO_ION_PENDING, "interrupt enable delay for ION") },
    { FLDATAD (CIF_DELAY, int_req, INT_V_NO_CIF_PENDING, "interrupt enable delay for CIF") },
    { FLDATAD (PWR_INT, int_req, INT_V_PWR, "power fail interrupt") },
    { FLDATAD (UF_INT, int_req, INT_V_UF, "user mode violation interrupt") },
    { ORDATAD (INT, int_req, INT_V_ION+1, "interrupt pending flags"), REG_RO },
    { ORDATAD (DONE, dev_done, INT_V_DIRECT, "device done flags"), REG_RO },
    { ORDATAD (ENABLE, int_enable, INT_V_DIRECT, "device interrupt enable flags"), REG_RO },
    { BRDATAD (PCQ, pcq, 8, 15, PCQ_SIZE, "PC prior to last JMP, JMS, or interrupt;                                        most recent PC change first"), REG_RO+REG_CIRC },
    { ORDATA (PCQP, pcq_p, 6), REG_HRO },
    { FLDATAD (STOP_INST, stop_inst, 0, "stop on undefined instruction") },
    { ORDATAD (WRU, sim_int_char, 8, "interrupt character") },
    { NULL }
    };

MTAB cpu_mod[] = {
    { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL },
    { UNIT_NOEAE, 0, "EAE", "EAE", NULL },
    { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle },
    { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL },
    { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size },
    { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size },
    { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size },
    { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size },
    { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size },
    { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size },
    { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size },
    { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size },
    { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
      &cpu_set_hist, &cpu_show_hist },
    { 0 }
    };

DEVICE cpu_dev = {
    "CPU", &cpu_unit, cpu_reg, cpu_mod,
    1, 8, 15, 1, 8, 12,
    &cpu_ex, &cpu_dep, &cpu_reset,
    NULL, NULL, NULL,
    NULL, 0
    };


t_stat sim_instr (void)
{
int32 IR, MB, IF, DF, LAC, MQ;
uint32 PC, MA;
int32 device, pulse, temp, iot_data;
t_stat reason;

/* Restore register state */

if (build_dev_tab ())                                   /* build dev_tab */
    return SCPE_STOP;
PC = saved_PC & 007777;                                 /* load local copies */
IF = saved_PC & 070000;
DF = saved_DF & 070000;
LAC = saved_LAC & 017777;
MQ = saved_MQ & 07777;
int_req = INT_UPDATE;
reason = 0;


/* ---PiDP add--------------------------------------------------------------------------------------------- */
// Set some register values we care about which may not get values
// before we need them, and which weren't set above.
MA = MB = IR = 0;

// Light up LEDs for 1st time.  Only needed when STOP switch set at start.
set_pidp8i_leds(PC, MA, MB, IR, LAC, MQ, IF, DF, SC, int_req,
				pls_fetch);
/* ---PiDP end---------------------------------------------------------------------------------------------- */


/* Main instruction fetch/decode loop */

while (reason == 0) {                                   /* loop until halted */

/* ---PiDP add--------------------------------------------------------------------------------------------- */
    awfulHackFlag = 0; // no do script pending. Did I mention awful?
/* ---PiDP end---------------------------------------------------------------------------------------------- */

    if (sim_interval <= 0) {                            /* check clock queue */
        if ((reason = sim_process_event ()))
            break;
        }

/* ---PiDP add--------------------------------------------------------------------------------------------- */

    switch (handle_flow_control_switches(M, &PC, &MA, &MB, &LAC, &IF,
            &DF, &int_req)) {
        case pft_stop:
            // Don't choke off the SIMH event queue handler.
            sim_interval = sim_interval - 1;

            // Update LEDs even in STOP mode.
            //
            // Note M[MA] used in this call, not MB.  If we pass MB, the
            // simulator never processes Ctrl-E in STOP mode.  FIXME?
            set_pidp8i_leds(PC, MA, M[MA], IR, LAC, MQ, IF, DF, SC,
				int_req, pls_fetch);

            // Go no further in STOP mode.  In particular, fetch no more
            // instructions, and do not touch PC!
            continue;

        case pft_halt:
            // Clear all registers and halt simulator
            PC  = saved_PC  = 0;
            IF  = saved_PC  = 0;
            DF  = saved_DF  = 0;
            LAC = saved_LAC = 0;
            MQ  = saved_MQ  = 0;
            int_req = 0;
            reason = STOP_HALT;
            continue;

        case pft_normal:
            // execute normally
            break;
    }

/* ---PiDP end---------------------------------------------------------------------------------------------- */

    if (int_req > INT_PENDING) {                        /* interrupt? */
        int_req = int_req & ~INT_ION;                   /* interrupts off */
        SF = (UF << 6) | (IF >> 9) | (DF >> 12);        /* form save field */
        IF = IB = DF = UF = UB = 0;                     /* clear mem ext */
        PCQ_ENTRY;                                      /* save old PC */
        M[0] = PC;                                      /* save PC in 0 */
        PC = 1;                                         /* fetch next from 1 */
        }

    MA = IF | PC;                                       /* form PC */
    if (sim_brk_summ &&
        sim_brk_test (MA, (1u << SIM_BKPT_V_SPC) | SWMASK ('E'))) { /* breakpoint? */
        reason = STOP_IBKPT;                            /* stop simulation */
        break;
        }

    IR = M[MA];                                         /* fetch instruction */

    int_req = int_req | INT_NO_ION_PENDING;             /* clear ION delay */
    sim_interval = sim_interval - 1;

/* ---PiDP add--------------------------------------------------------------------------------------------- */

    // Update the front panel LEDs with the results of our instruction
    // fetch.  This is above the goto label below because while in
    // single instruction mode, there is no point updating the LEDs
    // until we fetch another instruction, as above.  Until we get
    // another CONT press and fetch another instruction, the LEDs are
    // already set correctly.
    set_pidp8i_leds(PC, MA, M[MA], IR, LAC, MQ, IF, DF, SC,
            int_req, pls_fetch);

/* ---PiDP end---------------------------------------------------------------------------------------------- */

    PC = (PC + 1) & 07777;                              /* increment PC */

/* Instruction decoding.

   The opcode (IR<0:2>), indirect flag (IR<3>), and page flag (IR<4>)
   are decoded together.  This produces 32 decode points, four per
   major opcode.  For IOT, the extra decode points are not useful;
   for OPR, only the group flag (IR<3>) is used.

   AND, TAD, ISZ, DCA calculate a full 15b effective address.
   JMS, JMP calculate a 12b field-relative effective address.

   Autoindex calculations always occur within the same field as the
   instruction fetch.  The field must exist; otherwise, the instruction
   fetched would be 0000, and indirect addressing could not occur.

   Note that MA contains IF'PC.
*/

    if (hst_lnt) {                                      /* history enabled? */
        int32 ea;

        hst_p = (hst_p + 1);                            /* next entry */
        if (hst_p >= hst_lnt)
            hst_p = 0;
        hst[hst_p].pc = MA | HIST_PC;                   /* save PC, IR, LAC, MQ */
        hst[hst_p].ir = IR;
        hst[hst_p].lac = LAC;
        hst[hst_p].mq = MQ;
        if (IR < 06000) {                               /* mem ref? */
            if (IR & 0200)
                ea = (MA & 077600) | (IR & 0177);
            else ea = IF | (IR & 0177);                 /* direct addr */
            if (IR & 0400) {                            /* indirect? */
                if (IR < 04000) {                       /* mem operand? */
                    if ((ea & 07770) != 00010)
                        ea = DF | M[ea];
                    else ea = DF | ((M[ea] + 1) & 07777);
                    }
                else {                                  /* no, jms/jmp */
                    if ((ea & 07770) != 00010)
                        ea = IB | M[ea];
                    else ea = IB | ((M[ea] + 1) & 07777);
                    }
                }
            hst[hst_p].ea = ea;                         /* save eff addr */
            hst[hst_p].opnd = M[ea];                    /* save operand */
            }
        }

switch ((IR >> 7) & 037) {                              /* decode IR<0:4> */

/* Opcode 0, AND */

    case 000:                                           /* AND, dir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        LAC = LAC & (M[MA] | 010000);
        break;

    case 001:                                           /* AND, dir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        LAC = LAC & (M[MA] | 010000);
        break;

    case 002:                                           /* AND, indir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        LAC = LAC & (M[MA] | 010000);
        break;

    case 003:                                           /* AND, indir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        LAC = LAC & (M[MA] | 010000);
        break;

/* Opcode 1, TAD */

    case 004:                                           /* TAD, dir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        LAC = (LAC + M[MA]) & 017777;
        break;

    case 005:                                           /* TAD, dir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        LAC = (LAC + M[MA]) & 017777;
        break;

    case 006:                                           /* TAD, indir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        LAC = (LAC + M[MA]) & 017777;
        break;

    case 007:                                           /* TAD, indir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        LAC = (LAC + M[MA]) & 017777;
        break;

/* Opcode 2, ISZ */

    case 010:                                           /* ISZ, dir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        M[MA] = MB = (M[MA] + 1) & 07777;               /* field must exist */
        if (MB == 0)
            PC = (PC + 1) & 07777;
        break;

    case 011:                                           /* ISZ, dir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        M[MA] = MB = (M[MA] + 1) & 07777;               /* field must exist */
        if (MB == 0)
            PC = (PC + 1) & 07777;
        break;

    case 012:                                           /* ISZ, indir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        MB = (M[MA] + 1) & 07777;
        if (MEM_ADDR_OK (MA))
            M[MA] = MB;
        if (MB == 0)
            PC = (PC + 1) & 07777;
        break;

    case 013:                                           /* ISZ, indir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        MB = (M[MA] + 1) & 07777;
        if (MEM_ADDR_OK (MA))
            M[MA] = MB;
        if (MB == 0)
            PC = (PC + 1) & 07777;
        break;

/* Opcode 3, DCA */

    case 014:                                           /* DCA, dir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        M[MA] = LAC & 07777;
        LAC = LAC & 010000;
        break;

    case 015:                                           /* DCA, dir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        M[MA] = LAC & 07777;
        LAC = LAC & 010000;
        break;

    case 016:                                           /* DCA, indir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        if (MEM_ADDR_OK (MA))
            M[MA] = LAC & 07777;
        LAC = LAC & 010000;
        break;

    case 017:                                           /* DCA, indir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        if (MEM_ADDR_OK (MA))
            M[MA] = LAC & 07777;
        LAC = LAC & 010000;
        break;

/* Opcode 4, JMS.  From Bernhard Baehr's description of the TSC8-75:

   (In user mode) the current JMS opcode is moved to the ERIOT register, the ECDF
   flag is cleared. The address of the JMS instruction is loaded into the ERTB
   register and the TSC8-75 I/O flag is raised. When the TSC8-75 is enabled, the
   target addess of the JMS is loaded into PC, but nothing else (loading of IF, UF,
   clearing the interrupt inhibit flag, storing of the return address in the first
   word of the subroutine) happens. When the TSC8-75 is disabled, the JMS is performed
   as usual. */

    case 020:                                           /* JMS, dir, zero */
        PCQ_ENTRY;
        MA = IR & 0177;                                 /* dir addr, page zero */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            }
        if (UF && tsc_enb) {                            /* user mode, TSC enab? */
            tsc_pc = (PC - 1) & 07777;                  /* save PC */
            int_req = int_req | INT_TSC;                /* request intr */
            }
        else {                                          /* normal */
            IF = IB;                                    /* change IF */
            UF = UB;                                    /* change UF */
            int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
            MA = IF | MA;
            if (MEM_ADDR_OK (MA))
                M[MA] = PC;
            }
        PC = (MA + 1) & 07777;
        break;

    case 021:                                           /* JMS, dir, curr */
        PCQ_ENTRY;
        MA = (MA & 007600) | (IR & 0177);               /* dir addr, curr page */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            }
        if (UF && tsc_enb) {                            /* user mode, TSC enab? */
            tsc_pc = (PC - 1) & 07777;                  /* save PC */
            int_req = int_req | INT_TSC;                /* request intr */
            }
        else {                                          /* normal */
            IF = IB;                                    /* change IF */
            UF = UB;                                    /* change UF */
            int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
            MA = IF | MA;
            if (MEM_ADDR_OK (MA))
                M[MA] = PC;
            }
        PC = (MA + 1) & 07777;
        break;

    case 022:                                           /* JMS, indir, zero */
        PCQ_ENTRY;
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = M[MA];
        else MA = (M[MA] = (M[MA] + 1) & 07777);        /* incr before use */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            }
        if (UF && tsc_enb) {                            /* user mode, TSC enab? */
            tsc_pc = (PC - 1) & 07777;                  /* save PC */
            int_req = int_req | INT_TSC;                /* request intr */
            }
        else {                                          /* normal */
            IF = IB;                                    /* change IF */
            UF = UB;                                    /* change UF */
            int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
            MA = IF | MA;
            if (MEM_ADDR_OK (MA))
                M[MA] = PC;
            }
        PC = (MA + 1) & 07777;
        break;

    case 023:                                           /* JMS, indir, curr */
        PCQ_ENTRY;
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = M[MA];
        else MA = (M[MA] = (M[MA] + 1) & 07777);        /* incr before use */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            }
        if (UF && tsc_enb) {                            /* user mode, TSC enab? */
            tsc_pc = (PC - 1) & 07777;                  /* save PC */
            int_req = int_req | INT_TSC;                /* request intr */
            }
        else {                                          /* normal */
            IF = IB;                                    /* change IF */
            UF = UB;                                    /* change UF */
            int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
            MA = IF | MA;
            if (MEM_ADDR_OK (MA))
                M[MA] = PC;
            }
        PC = (MA + 1) & 07777;
        break;

/* Opcode 5, JMP.  From Bernhard Baehr's description of the TSC8-75:

   (In user mode) the current JMP opcode is moved to the ERIOT register, the ECDF
   flag is cleared. The address of the JMP instruction is loaded into the ERTB
   register and the TSC8-75 I/O flag is raised. Then the JMP is performed as usual
   (including the setting of IF, UF and clearing the interrupt inhibit flag). */


    case 024:                                           /* JMP, dir, zero */
        PCQ_ENTRY;
        MA = IR & 0177;                                 /* dir addr, page zero */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            if (tsc_enb) {                              /* TSC8 enabled? */
                tsc_pc = (PC - 1) & 07777;              /* save PC */
                int_req = int_req | INT_TSC;            /* request intr */
                }
            }
        IF = IB;                                        /* change IF */
        UF = UB;                                        /* change UF */
        int_req = int_req | INT_NO_CIF_PENDING;         /* clr intr inhibit */
        PC = MA;
        break;

/* If JMP direct, also check for idle (KSF/JMP *-1) and infinite loop */

    case 025:                                           /* JMP, dir, curr */
        PCQ_ENTRY;
        MA = (MA & 007600) | (IR & 0177);               /* dir addr, curr page */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            if (tsc_enb) {                              /* TSC8 enabled? */
                tsc_pc = (PC - 1) & 07777;              /* save PC */
                int_req = int_req | INT_TSC;            /* request intr */
                }
            }
        if (sim_idle_enab &&                            /* idling enabled? */
            (IF == IB)) {                               /* to same bank? */
            if (MA == ((PC - 2) & 07777)) {             /* 1) JMP *-1? */
                if (!(int_req & (INT_ION|INT_TTI)) &&   /*    iof, TTI flag off? */
                    (M[IB|((PC - 2) & 07777)] == OP_KSF)) /*  next is KSF? */
                    sim_idle (TMR_CLK, FALSE);          /* we're idle */
                }                                       /* end JMP *-1 */
            else if (MA == ((PC - 1) & 07777)) {        /* 2) JMP *? */
                if (!(int_req & INT_ION))               /*    iof? */
                    reason = STOP_LOOP;                 /* then infinite loop */
                else if (!(int_req & INT_ALL))          /*    ion, not intr? */
                    sim_idle (TMR_CLK, FALSE);          /* we're idle */
                }                                       /* end JMP */
            }                                           /* end idle enabled */
        IF = IB;                                        /* change IF */
        UF = UB;                                        /* change UF */
        int_req = int_req | INT_NO_CIF_PENDING;         /* clr intr inhibit */
        PC = MA;
        break;

    case 026:                                           /* JMP, indir, zero */
        PCQ_ENTRY;
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = M[MA];
        else MA = (M[MA] = (M[MA] + 1) & 07777);        /* incr before use */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            if (tsc_enb) {                              /* TSC8 enabled? */
                tsc_pc = (PC - 1) & 07777;              /* save PC */
                int_req = int_req | INT_TSC;            /* request intr */
                }
            }
        IF = IB;                                        /* change IF */
        UF = UB;                                        /* change UF */
        int_req = int_req | INT_NO_CIF_PENDING;         /* clr intr inhibit */
        PC = MA;
        break;

    case 027:                                           /* JMP, indir, curr */
        PCQ_ENTRY;
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = M[MA];
        else MA = (M[MA] = (M[MA] + 1) & 07777);        /* incr before use */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            if (tsc_enb) {                              /* TSC8 enabled? */
                tsc_pc = (PC - 1) & 07777;              /* save PC */
                int_req = int_req | INT_TSC;            /* request intr */
                }
            }
        IF = IB;                                        /* change IF */
        UF = UB;                                        /* change UF */
        int_req = int_req | INT_NO_CIF_PENDING;         /* clr intr inhibit */
        PC = MA;
        break;

/* Opcode 7, OPR group 1 */

    case 034:case 035:                                  /* OPR, group 1 */
        switch ((IR >> 4) & 017) {                      /* decode IR<4:7> */
        case 0:                                         /* nop */
            break;
        case 1:                                         /* CML */
            LAC = LAC ^ 010000;
            break;
        case 2:                                         /* CMA */
            LAC = LAC ^ 07777;
            break;
        case 3:                                         /* CMA CML */
            LAC = LAC ^ 017777;
            break;
        case 4:                                         /* CLL */
            LAC = LAC & 07777;
            break;
        case 5:                                         /* CLL CML = STL */
            LAC = LAC | 010000;
            break;
        case 6:                                         /* CLL CMA */
            LAC = (LAC ^ 07777) & 07777;
            break;
        case 7:                                         /* CLL CMA CML */
            LAC = (LAC ^ 07777) | 010000;
            break;
        case 010:                                       /* CLA */
            LAC = LAC & 010000;
            break;
        case 011:                                       /* CLA CML */
            LAC = (LAC & 010000) ^ 010000;
            break;
        case 012:                                       /* CLA CMA = STA */
            LAC = LAC | 07777;
            break;
        case 013:                                       /* CLA CMA CML */
            LAC = (LAC | 07777) ^ 010000;
            break;
        case 014:                                       /* CLA CLL */
            LAC = 0;
            break;
        case 015:                                       /* CLA CLL CML */
            LAC = 010000;
            break;
        case 016:                                       /* CLA CLL CMA */
            LAC = 07777;
            break;
        case 017:                                       /* CLA CLL CMA CML */
            LAC = 017777;
            break;
            }                                           /* end switch opers */

        if (IR & 01)                                    /* IAC */
            LAC = (LAC + 1) & 017777;
        switch ((IR >> 1) & 07) {                       /* decode IR<8:10> */
        case 0:                                         /* nop */
            break;
        case 1:                                         /* BSW */
            LAC = (LAC & 010000) | ((LAC >> 6) & 077) | ((LAC & 077) << 6);
            break;
        case 2:                                         /* RAL */
            LAC = ((LAC << 1) | (LAC >> 12)) & 017777;
            break;
        case 3:                                         /* RTL */
            LAC = ((LAC << 2) | (LAC >> 11)) & 017777;
            break;
        case 4:                                         /* RAR */
            LAC = ((LAC >> 1) | (LAC << 12)) & 017777;
            break;
        case 5:                                         /* RTR */
            LAC = ((LAC >> 2) | (LAC << 11)) & 017777;
            break;
        case 6:                                         /* RAL RAR - undef */
            LAC = LAC & (IR | 010000);                  /* uses AND path */
            break;
        case 7:                                         /* RTL RTR - undef */
            LAC = (LAC & 010000) | (MA & 07600) | (IR & 0177);
            break;                                      /* uses address path */
            }                                           /* end switch shifts */
        break;                                          /* end group 1 */

/* OPR group 2.  From Bernhard Baehr's description of the TSC8-75:

   (In user mode) HLT (7402), OSR (7404) and microprogrammed combinations with
   HLT and OSR: Additional to raising a user mode interrupt, the current OPR
   opcode is moved to the ERIOT register and the ECDF flag is cleared. */

    case 036:case 037:                                  /* OPR, groups 2, 3 */
        if ((IR & 01) == 0) {                           /* group 2 */
            switch ((IR >> 3) & 017) {                  /* decode IR<6:8> */
            case 0:                                     /* nop */
                break;
            case 1:                                     /* SKP */
                PC = (PC + 1) & 07777;
                break;
            case 2:                                     /* SNL */
                if (LAC >= 010000)
                    PC = (PC + 1) & 07777;
                break;
            case 3:                                     /* SZL */
                if (LAC < 010000)
                    PC = (PC + 1) & 07777;
                break;
            case 4:                                     /* SZA */
                if ((LAC & 07777) == 0)
                    PC = (PC + 1) & 07777;
                break;
            case 5:                                     /* SNA */
                if ((LAC & 07777)
                    != 0) PC = (PC + 1) & 07777;
                break;
            case 6:                                     /* SZA | SNL */
                if ((LAC == 0) || (LAC >= 010000))
                    PC = (PC + 1) & 07777;
                break;
            case 7:                                     /* SNA & SZL */
                if ((LAC != 0) && (LAC < 010000))
                    PC = (PC + 1) & 07777;
                break;
            case 010:                                   /* SMA */
                if ((LAC & 04000) != 0)
                    PC = (PC + 1) & 07777;
                break;
            case 011:                                   /* SPA */
                if ((LAC & 04000) == 0)
                    PC = (PC + 1) & 07777;
                break;
            case 012:                                   /* SMA | SNL */
                if (LAC >= 04000)
                    PC = (PC + 1) & 07777;
                break;
            case 013:                                   /* SPA & SZL */
                if (LAC < 04000)
                    PC = (PC + 1) & 07777;
                break;
            case 014:                                   /* SMA | SZA */
                if (((LAC & 04000) != 0) || ((LAC & 07777) == 0))
                    PC = (PC + 1) & 07777;
                break;
            case 015:                                   /* SPA & SNA */
                if (((LAC & 04000) == 0) && ((LAC & 07777) != 0))
                    PC = (PC + 1) & 07777;
                break;
            case 016:                                   /* SMA | SZA | SNL */
                if ((LAC >= 04000) || (LAC == 0))
                    PC = (PC + 1) & 07777;
                break;
            case 017:                                   /* SPA & SNA & SZL */
                if ((LAC < 04000) && (LAC != 0))
                    PC = (PC + 1) & 07777;
                break;
                }                                       /* end switch skips */
            if (IR & 0200)                              /* CLA */
                LAC = LAC & 010000;
            if ((IR & 06) && UF) {                      /* user mode? */
                int_req = int_req | INT_UF;             /* request intr */
                tsc_ir = IR;                            /* save instruction */
                tsc_cdf = 0;                            /* clear flag */
                }
            else {
                if (IR & 04) {                          /* OSR */
//--- PiDP add--------------------------------------------------------------------------
                    OSR = get_switch_register();        /* FIXME: [fad3ad73ea] */
//--- PiDP end--------------------------------------------------------------------------
                    LAC = LAC | OSR;
                    }
                if (IR & 02) {                          /* HLT */
//--- PiDP change--------------------------------------------------------------------------
                    // reason = STOP_HALT;
                    set_stop_mode();
//--- end of PiDP change--------------------------------------------------------------------------
                    }
                }
            break;
            }                                           /* end if group 2 */

/* OPR group 3 standard

   MQA!MQL exchanges AC and MQ, as follows:

        temp = MQ;
        MQ = LAC & 07777;
        LAC = LAC & 010000 | temp;
*/

        temp = MQ;                                      /* group 3 */
        if (IR & 0200)                                  /* CLA */
            LAC = LAC & 010000;
        if (IR & 0020) {                                /* MQL */
            MQ = LAC & 07777;
            LAC = LAC & 010000;
            }
        if (IR & 0100)                                  /* MQA */
            LAC = LAC | temp;
        if ((IR & 0056) && (cpu_unit.flags & UNIT_NOEAE)) {
            reason = stop_inst;                         /* EAE not present */
            break;
            }

/* OPR group 3 EAE

   The EAE operates in two modes:

        Mode A, PDP-8/I compatible
        Mode B, extended capability

   Mode B provides eight additional subfunctions; in addition, some
   of the Mode A functions operate differently in Mode B.

   The mode switch instructions are decoded explicitly and cannot be
   microprogrammed with other EAE functions (SWAB performs an MQL as
   part of standard group 3 decoding).  If mode switching is decoded,
   all other EAE timing is suppressed.
*/

        if (IR == 07431) {                              /* SWAB */
            emode = 1;                                  /* set mode flag */
            break;
            }
        if (IR == 07447) {                              /* SWBA */
            emode = gtf = 0;                            /* clear mode, gtf */
            break;
            }

/* If not switching modes, the EAE operation is determined by the mode
   and IR<6,8:10>:

   <6:10>       mode A          mode B          comments

   0x000        NOP             NOP
   0x001        SCL             ACS
   0x010        MUY             MUY             if mode B, next = address
   0x011        DVI             DVI             if mode B, next = address
   0x100        NMI             NMI             if mode B, clear AC if
                                                 result = 4000'0000
   0x101        SHL             SHL             if mode A, extra shift
   0x110        ASR             ASR             if mode A, extra shift
   0x111        LSR             LSR             if mode A, extra shift
   1x000        SCA             SCA
   1x001        SCA + SCL       DAD
   1x010        SCA + MUY       DST
   1x011        SCA + DVI       SWBA            NOP if not detected earlier
   1x100        SCA + NMI       DPSZ            
   1x101        SCA + SHL       DPIC            must be combined with MQA!MQL
   1x110        SCA + ASR       DCM             must be combined with MQA!MQL
   1x111        SCA + LSR       SAM

   EAE instructions which fetch memory operands use the CPU's DEFER
   state to read the first word; if the address operand is in locations
   x0010 - x0017, it is autoincremented.
*/

        if (emode == 0)                                 /* mode A? clr gtf */
            gtf = 0;
        switch ((IR >> 1) & 027) {                      /* decode IR<6,8:10> */

        case 020:                                       /* mode A, B: SCA */
            LAC = LAC | SC;
            break;
        case 000:                                       /* mode A, B: NOP */
            break;

        case 021:                                       /* mode B: DAD */
            if (emode) {
                MA = IF | PC;
                if ((MA & 07770) != 00010)              /* indirect; autoinc? */
                    MA = DF | M[MA];
                else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
                MQ = MQ + M[MA];
                MA = DF | ((MA + 1) & 07777);
                LAC = (LAC & 07777) + M[MA] + (MQ >> 12);
                MQ = MQ & 07777;
                PC = (PC + 1) & 07777;
                break;
                }
            LAC = LAC | SC;                             /* mode A: SCA then */
        case 001:                                       /* mode B: ACS */
            if (emode) {
                SC = LAC & 037;
                LAC = LAC & 010000;
                }
            else {                                      /* mode A: SCL */
                SC = (~M[IF | PC]) & 037;
                PC = (PC + 1) & 07777;
                }
            break;

        case 022:                                       /* mode B: DST */
            if (emode) {
                MA = IF | PC;
                if ((MA & 07770) != 00010)              /* indirect; autoinc? */
                    MA = DF | M[MA];
                else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
                if (MEM_ADDR_OK (MA))
                    M[MA] = MQ & 07777;
                MA = DF | ((MA + 1) & 07777);
                if (MEM_ADDR_OK (MA))
                    M[MA] = LAC & 07777;
                PC = (PC + 1) & 07777;
                break;
                }
            LAC = LAC | SC;                             /* mode A: SCA then */
        case 002:                                       /* MUY */
            MA = IF | PC;
            if (emode) {                                /* mode B: defer */
                if ((MA & 07770) != 00010)              /* indirect; autoinc? */
                    MA = DF | M[MA];
                else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
                }
            temp = (MQ * M[MA]) + (LAC & 07777);
            LAC = (temp >> 12) & 07777;
            MQ = temp & 07777;
            PC = (PC + 1) & 07777;
            SC = 014;                                   /* 12 shifts */
            break;

        case 023:                                       /* mode B: SWBA */
            if (emode)
                break;
            LAC = LAC | SC;                             /* mode A: SCA then */
        case 003:                                       /* DVI */
            MA = IF | PC;
            if (emode) {                                /* mode B: defer */
                if ((MA & 07770) != 00010)              /* indirect; autoinc? */
                    MA = DF | M[MA];
                else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
                }
            if ((LAC & 07777) >= M[MA]) {               /* overflow? */
                LAC = LAC | 010000;                     /* set link */
                MQ = ((MQ << 1) + 1) & 07777;           /* rotate MQ */
                SC = 0;                                 /* no shifts */
                }
            else {
                temp = ((LAC & 07777) << 12) | MQ;
                MQ = temp / M[MA];
                LAC = temp % M[MA];
                SC = 015;                               /* 13 shifts */
                }
            PC = (PC + 1) & 07777;
            break;

        case 024:                                       /* mode B: DPSZ */
            if (emode) {
                if (((LAC | MQ) & 07777) == 0)
                    PC = (PC + 1) & 07777;
                break;
                }
            LAC = LAC | SC;                             /* mode A: SCA then */
        case 004:                                       /* NMI */
            temp = (LAC << 12) | MQ;                    /* preserve link */
            for (SC = 0; ((temp & 017777777) != 0) &&
                (temp & 040000000) == ((temp << 1) & 040000000); SC++)
                temp = temp << 1;
            LAC = (temp >> 12) & 017777;
            MQ = temp & 07777;
            if (emode && ((LAC & 07777) == 04000) && (MQ == 0))
                LAC = LAC & 010000;                     /* clr if 4000'0000 */
            break;

        case 025:                                       /* mode B: DPIC */
            if (emode) {
                temp = (LAC + 1) & 07777;               /* SWP already done! */
                LAC = MQ + (temp == 0);
                MQ = temp;
                break;
                }
            LAC = LAC | SC;                             /* mode A: SCA then */
        case 5:                                         /* SHL */
            SC = (M[IF | PC] & 037) + (emode ^ 1);      /* shift+1 if mode A */
            if (SC > 25)                                /* >25? result = 0 */
                temp = 0;
            else temp = ((LAC << 12) | MQ) << SC;       /* <=25? shift LAC:MQ */
            LAC = (temp >> 12) & 017777;
            MQ = temp & 07777;
            PC = (PC + 1) & 07777;
            SC = emode? 037: 0;                         /* SC = 0 if mode A */
            break;

        case 026:                                       /* mode B: DCM */
            if (emode) {
                temp = (-LAC) & 07777;                  /* SWP already done! */
                LAC = (MQ ^ 07777) + (temp == 0);
                MQ = temp;
                break;
                }
            LAC = LAC | SC;                             /* mode A: SCA then */
        case 6:                                         /* ASR */
            SC = (M[IF | PC] & 037) + (emode ^ 1);      /* shift+1 if mode A */
            temp = ((LAC & 07777) << 12) | MQ;          /* sext from AC0 */
            if (LAC & 04000)
                temp = temp | ~037777777;
            if (emode && (SC != 0))
                gtf = (temp >> (SC - 1)) & 1;
            if (SC > 25)
                temp = (LAC & 04000)? -1: 0;
            else temp = temp >> SC;
            LAC = (temp >> 12) & 017777;
            MQ = temp & 07777;
            PC = (PC + 1) & 07777;
            SC = emode? 037: 0;                         /* SC = 0 if mode A */
            break;

        case 027:                                       /* mode B: SAM */
            if (emode) {
                temp = LAC & 07777;
                LAC = MQ + (temp ^ 07777) + 1;          /* L'AC = MQ - AC */
                gtf = (temp <= MQ) ^ ((temp ^ MQ) >> 11);
                break;
                }
            LAC = LAC | SC;                             /* mode A: SCA then */
        case 7:                                         /* LSR */
            SC = (M[IF | PC] & 037) + (emode ^ 1);      /* shift+1 if mode A */
            temp = ((LAC & 07777) << 12) | MQ;          /* clear link */
            if (emode && (SC != 0))
                gtf = (temp >> (SC - 1)) & 1;
            if (SC > 24)                                /* >24? result = 0 */
                temp = 0;
            else temp = temp >> SC;                     /* <=24? shift AC:MQ */
            LAC = (temp >> 12) & 07777;
            MQ = temp & 07777;
            PC = (PC + 1) & 07777;
            SC = emode? 037: 0;                         /* SC = 0 if mode A */
            break;
            }                                           /* end switch */
        break;                                          /* end case 7 */

/* Opcode 6, IOT.  From Bernhard Baehr's description of the TSC8-75:

   (In user mode) Additional to raising a user mode interrupt, the current IOT
   opcode is moved to the ERIOT register. When the IOT is a CDF instruction (62x1),
   the ECDF flag is set, otherwise it is cleared. */

    case 030:case 031:case 032:case 033:                /* IOT */
        if (UF) {                                       /* privileged? */
            int_req = int_req | INT_UF;                 /* request intr */
            tsc_ir = IR;                                /* save instruction */
            if ((IR & 07707) == 06201)                  /* set/clear flag */
                tsc_cdf = 1;
            else tsc_cdf = 0;
            break;
            }
        device = (IR >> 3) & 077;                       /* device = IR<3:8> */

/* --------------------------------------------------------------------------------------------------------- */
// the IOT ION, IOF do not light pause, anything else does:
/* --------------------------------------------------------------------------------------------------------- */

        pulse = IR & 07;                                /* pulse = IR<9:11> */
        iot_data = LAC & 07777;                         /* AC unchanged */
        switch (device) {                               /* decode IR<3:8> */

        case 000:                                       /* CPU control */
            switch (pulse) {                            /* decode IR<9:11> */

            case 0:                                     /* SKON */
                if (int_req & INT_ION)
                    PC = (PC + 1) & 07777;
                int_req = int_req & ~INT_ION;
                break;

            case 1:                                     /* ION */
                int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING;
                break;

            case 2:                                     /* IOF */
                int_req = int_req & ~INT_ION;
                break;

            case 3:                                     /* SRQ */
                if (int_req & INT_ALL)
                    PC = (PC + 1) & 07777;
                break;

            case 4:                                     /* GTF */
                LAC = (LAC & 010000) |
                      ((LAC & 010000) >> 1) | (gtf << 10) |
                      (((int_req & INT_ALL) != 0) << 9) |
                      (((int_req & INT_ION) != 0) << 7) | SF;
                break;

            case 5:                                     /* RTF */
                gtf = ((LAC & 02000) >> 10);
                UB = (LAC & 0100) >> 6;
                IB = (LAC & 0070) << 9;
                DF = (LAC & 0007) << 12;
                LAC = ((LAC & 04000) << 1) | iot_data;
                int_req = (int_req | INT_ION) & ~INT_NO_CIF_PENDING;
                break;

            case 6:                                     /* SGT */
                if (gtf)
                    PC = (PC + 1) & 07777;
                break;

            case 7:                                     /* CAF */
                gtf = 0;
                emode = 0;
                int_req = int_req & INT_NO_CIF_PENDING;
                dev_done = 0;
                int_enable = INT_INIT_ENABLE;
                LAC = 0;
                reset_all (1);                          /* reset all dev */
                break;
                }                                       /* end switch pulse */
            break;                                      /* end case 0 */

        case 020:case 021:case 022:case 023:
        case 024:case 025:case 026:case 027:            /* memory extension */

/* --------------------------------------------------------------------------------------------------------- */
// Memory extension does not trigger IOP pauses --> do not light pause
/* --------------------------------------------------------------------------------------------------------- */

            switch (pulse) {                            /* decode IR<9:11> */

            case 1:                                     /* CDF */
                DF = (IR & 0070) << 9;
                break;

            case 2:                                     /* CIF */
                IB = (IR & 0070) << 9;
                int_req = int_req & ~INT_NO_CIF_PENDING;
                break;

            case 3:                                     /* CDF CIF */
                DF = IB = (IR & 0070) << 9;
                int_req = int_req & ~INT_NO_CIF_PENDING;
                break;

            case 4:
                switch (device & 07) {                  /* decode IR<6:8> */

                case 0:                                 /* CINT */
                    int_req = int_req & ~INT_UF;
                    break;

                case 1:                                 /* RDF */
                    LAC = LAC | (DF >> 9);
                        break;

                case 2:                                 /* RIF */
                    LAC = LAC | (IF >> 9);
                    break;

                case 3:                                 /* RIB */
                    LAC = LAC | SF;
                    break;

                case 4:                                 /* RMF */
                    UB = (SF & 0100) >> 6;
                    IB = (SF & 0070) << 9;
                    DF = (SF & 0007) << 12;
                    int_req = int_req & ~INT_NO_CIF_PENDING;
                    break;

                case 5:                                 /* SINT */
                    if (int_req & INT_UF)
                        PC = (PC + 1) & 07777;
                    break;

                case 6:                                 /* CUF */
                    UB = 0;
                    int_req = int_req & ~INT_NO_CIF_PENDING;
                    break;

                case 7:                                 /* SUF */
                    UB = 1;
                    int_req = int_req & ~INT_NO_CIF_PENDING;
                    break;
                    }                                   /* end switch device */
                break;

            default:
                reason = stop_inst;
                break;
                }                                       /* end switch pulse */
            break;                                      /* end case 20-27 */

        case 010:                                       /* power fail */
            switch (pulse) {                            /* decode IR<9:11> */

            case 1:                                     /* SBE */
                break;

            case 2:                                     /* SPL */
                if (int_req & INT_PWR)
                    PC = (PC + 1) & 07777;
                break;

            case 3:                                     /* CAL */
                int_req = int_req & ~INT_PWR;
                break;

            default:
                reason = stop_inst;
                break;
                }                                       /* end switch pulse */
            break;                                      /* end case 10 */

        default:                                        /* I/O device */
            if (dev_tab[device]) {                      /* dev present? */
/* ---PiDP add--------------------------------------------------------------------------------------------- */
                // Any other device will trigger IOP, so light pause
                set_pidp8i_leds(PC, MA, MB, IR, LAC, MQ, IF, DF, SC,
                        int_req, pls_pause);
/* ---PiDP end---------------------------------------------------------------------------------------------- */
                iot_data = dev_tab[device] (IR, iot_data);
                LAC = (LAC & 010000) | (iot_data & 07777);
                if (iot_data & IOT_SKP)
                    PC = (PC + 1) & 07777;
                if (iot_data >= IOT_REASON)
                    reason = iot_data >> IOT_V_REASON;
                }
            else reason = stop_inst;                    /* stop on flag */
            break;
            }                                           /* end switch device */
        break;                                          /* end case IOT */
        }                                               /* end switch opcode */

/* ---PiDP add--------------------------------------------------------------------------------------------- */
    if (IR < 05000)
        set_pidp8i_leds(PC, MA, MB, IR, LAC, MQ, IF, DF, SC,
                int_req, pls_execute);
/* ---PiDP end---------------------------------------------------------------------------------------------- */

    }                                                   /* end while */

/* Simulation halted */

saved_PC = IF | (PC & 07777);                           /* save copies */
saved_DF = DF & 070000;
saved_LAC = LAC & 017777;
saved_MQ = MQ & 07777;
pcq_r->qptr = pcq_p;                                    /* update pc q ptr */
return reason;
}                                                       /* end sim_instr */

/* Reset routine */

t_stat cpu_reset (DEVICE *dptr)
{
int_req = (int_req & ~INT_ION) | INT_NO_CIF_PENDING;
saved_DF = IB = saved_PC & 070000;
UF = UB = gtf = emode = 0;
pcq_r = find_reg ("PCQ", NULL, dptr);
if (pcq_r)
    pcq_r->qptr = 0;
else return SCPE_IERR;
sim_brk_types = SWMASK ('E') | SWMASK('I');
sim_brk_dflt = SWMASK ('E');
return SCPE_OK;
}

/* Set PC for boot (PC<14:12> will typically be 0) */

void cpu_set_bootpc (int32 pc)
{
saved_PC = pc;                                          /* set PC, IF */
saved_DF = IB = pc & 070000;                            /* set IB, DF */
return;
}

/* Memory examine */

t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE)
    return SCPE_NXM;
if (vptr != NULL)
    *vptr = M[addr] & 07777;
return SCPE_OK;
}

/* Memory deposit */

t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE)
    return SCPE_NXM;
M[addr] = val & 07777;
return SCPE_OK;
}

/* Memory size change */

t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 mc = 0;
uint32 i;

if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0))
    return SCPE_ARG;
for (i = val; i < MEMSIZE; i++)
    mc = mc | M[i];
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
    return SCPE_OK;
MEMSIZE = val;
for (i = MEMSIZE; i < MAXMEMSIZE; i++)
    M[i] = 0;
return SCPE_OK;
}

/* Change device number for a device */

t_stat set_dev (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr;
DIB *dibp;
uint32 newdev;
t_stat r;

if (cptr == NULL)
    return SCPE_ARG;
if (uptr == NULL)
    return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL)
    return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp == NULL)
    return SCPE_IERR;
newdev = get_uint (cptr, 8, DEV_MAX - 1, &r);           /* get new */
if ((r != SCPE_OK) || (newdev == dibp->dev))
    return r;
dibp->dev = newdev;                                     /* store */
return SCPE_OK;
}

/* Show device number for a device */

t_stat show_dev (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
DEVICE *dptr;
DIB *dibp;

if (uptr == NULL)
    return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL)
    return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp == NULL)
    return SCPE_IERR;
fprintf (st, "devno=%02o", dibp->dev);
if (dibp->num > 1)
    fprintf (st, "-%2o", dibp->dev + dibp->num - 1);
return SCPE_OK;
}

/* CPU device handler - should never get here! */

int32 bad_dev (int32 IR, int32 AC)
{
return (SCPE_IERR << IOT_V_REASON) | AC;                /* broken! */
}

/* Build device dispatch table */

t_bool build_dev_tab (void)
{
DEVICE *dptr;
DIB *dibp;
uint32 i, j;
static const uint8 std_dev[] = {
    000, 010, 020, 021, 022, 023, 024, 025, 026, 027
    };

for (i = 0; i < DEV_MAX; i++)                           /* clr table */
    dev_tab[i] = NULL;
for (i = 0; i < ((uint32) sizeof (std_dev)); i++)       /* std entries */
    dev_tab[std_dev[i]] = &bad_dev;
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {     /* add devices */
    dibp = (DIB *) dptr->ctxt;                          /* get DIB */
    if (dibp && !(dptr->flags & DEV_DIS)) {             /* enabled? */
        if (dibp->dsp_tbl) {                            /* dispatch table? */
            DIB_DSP *dspp = dibp->dsp_tbl;              /* set ptr */
            for (j = 0; j < dibp->num; j++, dspp++) {   /* loop thru tbl */
                if (dspp->dsp) {                        /* any dispatch? */
                    if (dev_tab[dspp->dev]) {           /* already filled? */
                        sim_printf ("%s device number conflict at %02o\n",
                            sim_dname (dptr), dibp->dev + j);
                        return TRUE;
                        }
                    dev_tab[dspp->dev] = dspp->dsp;     /* fill */
                    }                                   /* end if dsp */
                }                                       /* end for j */
            }                                           /* end if dsp_tbl */
        else {                                          /* inline dispatches */
            for (j = 0; j < dibp->num; j++) {           /* loop thru disp */
                if (dibp->dsp[j]) {                     /* any dispatch? */
                    if (dev_tab[dibp->dev + j]) {       /* already filled? */
                        sim_printf ("%s device number conflict at %02o\n",
                            sim_dname (dptr), dibp->dev + j);
                        return TRUE;
                        }
                    dev_tab[dibp->dev + j] = dibp->dsp[j]; /* fill */
                    }                                   /* end if dsp */
                }                                       /* end for j */
            }                                           /* end else */
        }                                               /* end if enb */
    }                                                   /* end for i */
return FALSE;
}

/* Set history */

t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 i, lnt;
t_stat r;

if (cptr == NULL) {
    for (i = 0; i < hst_lnt; i++)
        hst[i].pc = 0;
    hst_p = 0;
    return SCPE_OK;
    }
lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN)))
    return SCPE_ARG;
hst_p = 0;
if (hst_lnt) {
    free (hst);
    hst_lnt = 0;
    hst = NULL;
    }
if (lnt) {
    hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
    if (hst == NULL)
        return SCPE_MEM;
    hst_lnt = lnt;
    }
return SCPE_OK;
}

/* 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))
        return SCPE_ARG;
    }
else lnt = hst_lnt;
di = hst_p - lnt;                                       /* work forward */
if (di < 0)
    di = di + hst_lnt;
fprintf (st, "PC     L AC    MQ    ea     IR\n\n");
for (k = 0; k < lnt; k++) {                             /* print specified */
    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)
            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;
}
Added src/PDP8/pdp8_ct.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_ct.c: PDP-8 cassette tape simulator

   Copyright (c) 2006-2013, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   ct           TA8E/TU60 cassette tape

   17-Sep-07    RMS     Changed to use central set_bootpc routine
   13-Aug-07    RMS     Fixed handling of BEOT
   06-Aug-07    RMS     Foward op at BOT skips initial file gap
   30-May-07    RMS     Fixed typo (Norm Lastovica)

   Magnetic tapes are represented as a series of variable records
   of the form:

        32b byte count
        byte 0
        byte 1
        :
        byte n-2
        byte n-1
        32b byte count

   If the byte count is odd, the record is padded with an extra byte
   of junk.  File marks are represented by a byte count of 0.

   Cassette format differs in one very significant way: it has file gaps
   rather than file marks.  If the controller spaces or reads into a file
   gap and then reverses direction, the file gap is not seen again.  This
   is in contrast to magnetic tapes, where the file mark is a character
   sequence and is seen again if direction is reversed.  In addition,
   cassettes have an initial file gap which is automatically skipped on
   forward operations from beginning of tape.

   Note that the read and write sequences for the cassette are asymmetric:

   Read:    KLSA            /SELECT READ
            KGOA            /INIT READ, CLEAR DF
            <data flag sets, char in buf>
            KGOA            /READ 1ST CHAR, CLEAR DF
            DCA CHAR
            :
            <data flag sets, char in buf>
            KGOA            /READ LAST CHAR, CLEAR DF
            DCA CHAR
            <data flag sets, CRC1 in buf>
            KLSA            /SELECT CRC MODE
            KGOA            /READ 1ST CRC
            <data flag sets, CRC2 in buf>
            KGOA            /READ 2ND CRC
            <ready flag/CRC error flag sets>

   Write:   KLSA            /SELECT WRITE
            TAD CHAR        /1ST CHAR
            KGOA            /INIT WRITE, CHAR TO BUF, CLEAR DF
            <data flag sets, char to tape>
            :
            TAD CHAR        /LAST CHAR
            KGOA            /CHAR TO BUF, CLEAR DF
            <data flag sets, char to tape>
            KLSA            /SELECT CRC MODE
            KGOA            /WRITE CRC, CLEAR DF
            <ready flag sets, CRC on tape>
*/

#include "pdp8_defs.h"
#include "sim_tape.h"

#define CT_NUMDR        2                               /* #drives */
#define FNC             u3                              /* unit function */
#define UST             u4                              /* unit status */
#define CT_MAXFR        (CT_SIZE)                       /* max record lnt */
#define CT_SIZE         93000                           /* chars/tape */

/* Status Register A */

#define SRA_ENAB        0200                            /* enable */
#define SRA_V_UNIT      6                               /* unit */
#define SRA_M_UNIT      (CT_NUMDR - 1)
#define SRA_V_FNC       3                               /* function */
#define SRA_M_FNC       07
#define  SRA_READ        00
#define  SRA_REW         01
#define  SRA_WRITE       02
#define  SRA_SRF         03
#define  SRA_WFG         04
#define  SRA_SRB         05
#define  SRA_CRC         06
#define  SRA_SFF         07
#define SRA_2ND         010
#define SRA_IE          0001                            /* int enable */
#define GET_UNIT(x)     (((x) >> SRA_V_UNIT) & SRA_M_UNIT)
#define GET_FNC(x)      (((x) >> SRA_V_FNC) & SRA_M_FNC)

/* Function code flags */

#define OP_WRI          01                              /* op is a write */
#define OP_REV          02                              /* op is rev motion */
#define OP_FWD          04                              /* op is fwd motion */

/* Unit status flags */

#define UST_REV         (OP_REV)                        /* last op was rev */
#define UST_GAP         01                              /* last op hit gap */

/* Status Register B, ^ = computed on the fly */

#define SRB_WLE         0400                            /* "write lock err" */
#define SRB_CRC         0200                            /* CRC error */
#define SRB_TIM         0100                            /* timing error */
#define SRB_BEOT        0040                            /* ^BOT/EOT */
#define SRB_EOF         0020                            /* end of file */
#define SRB_EMP         0010                            /* ^drive empty */
#define SRB_REW         0004                            /* rewinding */
#define SRB_WLK         0002                            /* ^write locked */
#define SRB_RDY         0001                            /* ^ready */
#define SRB_ALLERR      (SRB_WLE|SRB_CRC|SRB_TIM|SRB_BEOT|SRB_EOF|SRB_EMP)
#define SRB_XFRERR      (SRB_WLE|SRB_CRC|SRB_TIM|SRB_EOF)

extern int32 int_req, stop_inst;
extern UNIT cpu_unit;

uint32 ct_sra = 0;                                      /* status reg A */
uint32 ct_srb = 0;                                      /* status reg B */
uint32 ct_db = 0;                                       /* data buffer */
uint32 ct_df = 0;                                       /* data flag */
uint32 ct_write = 0;                                    /* TU60 write flag */
uint32 ct_bptr = 0;                                     /* buf ptr */
uint32 ct_blnt = 0;                                     /* buf length */
int32 ct_stime = 1000;                                  /* start time */
int32 ct_ctime = 100;                                   /* char latency */
uint32 ct_stopioe = 1;                                  /* stop on error */
uint8 *ct_xb = NULL;                                    /* transfer buffer */
static uint8 ct_fnc_tab[SRA_M_FNC + 1] = {
    OP_FWD,        0     , OP_WRI|OP_FWD, OP_REV,
    OP_WRI|OP_FWD, OP_REV, 0,             OP_FWD
    };

int32 ct70 (int32 IR, int32 AC);
t_stat ct_svc (UNIT *uptr);
t_stat ct_reset (DEVICE *dptr);
t_stat ct_attach (UNIT *uptr, CONST char *cptr);
t_stat ct_detach (UNIT *uptr);
t_stat ct_boot (int32 unitno, DEVICE *dptr);
uint32 ct_updsta (UNIT *uptr);
int32 ct_go_start (int32 AC);
int32 ct_go_cont (UNIT *uptr, int32 AC);
t_stat ct_map_err (UNIT *uptr, t_stat st);
UNIT *ct_busy (void);
void ct_set_df (t_bool timchk);
t_bool ct_read_char (void);
uint32 ct_crc (uint8 *buf, uint32 cnt);

/* CT data structures

   ct_dev       CT device descriptor
   ct_unit      CT unit list
   ct_reg       CT register list
   ct_mod       CT modifier list
*/

DIB ct_dib = { DEV_CT, 1, { &ct70 } };

UNIT ct_unit[] = {
    { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) },
    { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) },
    };

REG ct_reg[] = {
    { ORDATAD (CTSRA, ct_sra, 8, "status register A") },
    { ORDATAD (CTSRB, ct_srb, 8, "status register B") },
    { ORDATAD (CTDB, ct_db, 8, "data buffer") },
    { FLDATAD (CTDF, ct_df, 0, "data flag") },
    { FLDATAD (RDY, ct_srb, 0, "ready flag") },
    { FLDATAD (WLE, ct_srb, 8, "write lock error") },
    { FLDATAD (WRITE, ct_write, 0, "TA60 write operation flag") },
    { FLDATAD (INT, int_req, INT_V_CT, "interrupt request") },
    { DRDATAD (BPTR, ct_bptr, 17, "buffer pointer") },
    { DRDATAD (BLNT, ct_blnt, 17, "buffer length") },
    { DRDATAD (STIME, ct_stime, 24, "operation start time"), PV_LEFT + REG_NZ },
    { DRDATAD (CTIME, ct_ctime, 24, "character latency"), PV_LEFT + REG_NZ },
    { FLDATAD (STOP_IOE, ct_stopioe, 0, "stop on I/O errors flag") },
    { URDATA (UFNC, ct_unit[0].FNC, 8, 4, 0, CT_NUMDR, REG_HRO) },
    { URDATA (UST, ct_unit[0].UST, 8, 2, 0, CT_NUMDR, REG_HRO) },
    { URDATAD (POS, ct_unit[0].pos, 10, T_ADDR_W, 0,
              CT_NUMDR, PV_LEFT | REG_RO, "position, units 0-1") },
    { FLDATA (DEVNUM, ct_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB ct_mod[] = {
    { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },
    { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, 
//    { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
//      &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
    { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL,
      NULL, &sim_tape_show_capac, NULL },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { 0 }
    };

DEVICE ct_dev = {
    "CT", ct_unit, ct_reg, ct_mod,
    CT_NUMDR, 10, 31, 1, 8, 8,
    NULL, NULL, &ct_reset,
    &ct_boot, &ct_attach, &ct_detach,
    &ct_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_TAPE
    };

/* IOT routines */

int32 ct70 (int32 IR, int32 AC)
{
int32 srb;
UNIT *uptr;

srb = ct_updsta (NULL);                                 /* update status */
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 0:                                             /* KCLR */
        ct_reset (&ct_dev);                             /* reset the world */
        break;

    case 1:                                             /* KSDR */
        if (ct_df)
            AC |= IOT_SKP;
        break;

    case 2:                                             /* KSEN */
        if (srb & SRB_ALLERR)
            AC |= IOT_SKP;
        break;

    case 3:                                             /* KSBF */
        if ((srb & SRB_RDY) && !(srb & SRB_EMP))
            AC |= IOT_SKP;
        break;

    case 4:                                             /* KLSA */
        ct_sra = AC & 0377;
        ct_updsta (NULL);
        return ct_sra ^ 0377;

    case 5:                                             /* KSAF */
        if (ct_df || (srb & (SRB_ALLERR|SRB_RDY)))
            AC |= IOT_SKP;
        break;

    case 6:                                             /* KGOA */
        ct_df = 0;                                      /* clear data flag */
        if ((uptr = ct_busy ()))                        /* op in progress? */
            AC = ct_go_cont (uptr, AC);                 /* yes */
        else AC = ct_go_start (AC);                     /* no, start */
        ct_updsta (NULL);
        break;

    case 7:                                             /* KSRB */
        return srb & 0377;
        }                                               /* end switch */

return AC;
}

/* Start a new operation - cassette is not busy */

int32 ct_go_start (int32 AC)
{
UNIT *uptr = ct_dev.units + GET_UNIT (ct_sra);
uint32 fnc = GET_FNC (ct_sra);
uint32 flg = ct_fnc_tab[fnc];
uint32 old_ust = uptr->UST;

if (DEBUG_PRS (ct_dev)) fprintf (sim_deb,
    ">>CT start: op=%o, old_sta = %o, pos=%d\n",
    fnc, uptr->UST, uptr->pos);
if ((ct_sra & SRA_ENAB) && (uptr->flags & UNIT_ATT)) {  /* enabled, att? */
    ct_srb &= ~(SRB_XFRERR|SRB_REW);                    /* clear err, rew */
    if (flg & OP_WRI) {                                 /* write-type op? */
        if (sim_tape_wrp (uptr)) {                      /* locked? */
            ct_srb |= SRB_WLE;                          /* set flag, abort */
            return AC;
            }
        ct_write = 1;                                   /* set TU60 wr flag */
        ct_db = AC & 0377;
        }
    else {
        ct_write = 0;
        ct_db = 0;
        }
    ct_srb &= ~SRB_BEOT;                                /* tape in motion */
    if (fnc == SRA_REW)                                 /* rew? set flag */
        ct_srb |= SRB_REW;
    if ((fnc != SRA_REW) && !(flg & OP_WRI)) {          /* read cmd? */
        t_mtrlnt t;
        t_stat st;
        uptr->UST = flg & UST_REV;                      /* save direction */
        if (sim_tape_bot (uptr) && (flg & OP_FWD)) {    /* spc/read fwd bot? */
            st = sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); /* skip file gap */
            if (st != MTSE_TMK)                         /* not there? */
                sim_tape_rewind (uptr);                 /* restore tap pos */
            else old_ust = 0;                           /* defang next */
            }
        if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* rev in gap? */
            if (DEBUG_PRS (ct_dev)) fprintf (sim_deb,
                ">>CT skip gap: op=%o, old_sta = %o, pos=%d\n",
                fnc, uptr->UST, uptr->pos);
            if (uptr->UST)                              /* skip file gap */
                sim_tape_rdrecr (uptr, ct_xb, &t, CT_MAXFR);
            else sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR);
            }
        }
    else uptr->UST = 0;
    ct_bptr = 0;                                        /* init buffer */
    ct_blnt = 0;
    uptr->FNC = fnc;                                    /* save function */
    sim_activate (uptr, ct_stime);                      /* schedule op */
    }
if ((fnc == SRA_READ) || (fnc == SRA_CRC))              /* read or CRC? */
    return 0;                                           /* get "char" */
return AC;
}

/* Continue an in-progress operation - cassette is in motion */

int32 ct_go_cont (UNIT *uptr, int32 AC)
{
int32 fnc = GET_FNC (ct_sra);

switch (fnc) {                                          /* case on function */

    case SRA_READ:                                      /* read */
        return ct_db;                                   /* return data */

    case SRA_WRITE:                                     /* write */
        ct_db = AC & 0377;                              /* save data */
        break;

    case SRA_CRC:                                       /* CRC */
        if ((uptr->FNC & SRA_M_FNC) != SRA_CRC)         /* if not CRC */
            uptr->FNC = SRA_CRC;                        /* start CRC seq */
        if (!ct_write)                                  /* read? AC <- buf */
            return ct_db;
        break;

    default:
        break;
    }

return AC;
}

/* Unit service */

t_stat ct_svc (UNIT *uptr)
{
uint32 i, crc;
uint32 flgs = ct_fnc_tab[uptr->FNC & SRA_M_FNC];
t_mtrlnt tbc;
t_stat st, r;

if ((uptr->flags & UNIT_ATT) == 0) {                    /* not attached? */
    ct_updsta (uptr);                                   /* update status */
    return (ct_stopioe? SCPE_UNATT: SCPE_OK);
    }
if (((flgs & OP_REV) && sim_tape_bot (uptr)) ||         /* rev at BOT or */
    ((flgs & OP_FWD) && sim_tape_eot (uptr))) {         /* fwd at EOT? */
    ct_srb |= SRB_BEOT;                                 /* error */
    ct_updsta (uptr);                                   /* op done */
    return SCPE_OK;
    }

r = SCPE_OK;
switch (uptr->FNC) {                                    /* case on function */

    case SRA_READ:                                      /* read start */
        st = sim_tape_rdrecf (uptr, ct_xb, &ct_blnt, CT_MAXFR); /* get rec */
        if (st == MTSE_RECE)                            /* rec in err? */
            ct_srb |= SRB_CRC;
        else if (st != MTSE_OK) {                       /* other error? */
            r = ct_map_err (uptr, st);                  /* map error */
            break;
            }
        crc = ct_crc (ct_xb, ct_blnt);                  /* calculate CRC */
        ct_xb[ct_blnt++] = (crc >> 8) & 0377;           /* append to buffer */
        ct_xb[ct_blnt++] = crc & 0377;
        uptr->FNC |= SRA_2ND;                           /* next state */
        sim_activate (uptr, ct_ctime);                  /* sched next char */
        return SCPE_OK;

    case SRA_READ|SRA_2ND:                              /* read char */
        if (!ct_read_char ())                           /* read, overrun? */
            break;
        ct_set_df (TRUE);                               /* set data flag */
        sim_activate (uptr, ct_ctime);                  /* sched next char */
        return SCPE_OK;

    case SRA_WRITE:                                     /* write start */
        for (i = 0; i < CT_MAXFR; i++)                  /* clear buffer */
            ct_xb[i] = 0;
        uptr->FNC |= SRA_2ND;                           /* next state */
        sim_activate (uptr, ct_ctime);                  /* sched next char */
        return SCPE_OK;

    case SRA_WRITE|SRA_2ND:                             /* write char */
        if ((ct_bptr < CT_MAXFR) &&                     /* room in buf? */
            ((uptr->pos + ct_bptr) < uptr->capac))      /* room on tape? */
            ct_xb[ct_bptr++] = ct_db;                   /* store char */
        ct_set_df (TRUE);                               /* set data flag */
        sim_activate (uptr, ct_ctime);                  /* sched next char */
        return SCPE_OK;

    case SRA_CRC:                                       /* CRC */
        if (ct_write) {                                 /* write? */
           if ((st = sim_tape_wrrecf (uptr, ct_xb, ct_bptr)))/* write, err? */
               r = ct_map_err (uptr, st);               /* map error */
           break;                                       /* write done */
           }
        ct_read_char ();                                /* get second CRC */
        ct_set_df (FALSE);                              /* set df */
        uptr->FNC |= SRA_2ND;                           /* next state */
        sim_activate (uptr, ct_ctime);
        return SCPE_OK;

    case SRA_CRC|SRA_2ND:                               /* second read CRC */
        if (ct_bptr != ct_blnt) {                       /* partial read? */
            crc = ct_crc (ct_xb, ct_bptr);              /* actual CRC */
            if (crc != 0)                               /* must be zero */
                ct_srb |= SRB_CRC;
            }
         break;                                         /* read done */

    case SRA_WFG:                                       /* write file gap */
        if ((st = sim_tape_wrtmk (uptr)))               /* write tmk, err? */
            r = ct_map_err (uptr, st);                  /* map error */
        break;

    case SRA_REW:                                       /* rewind */
        sim_tape_rewind (uptr);
        ct_srb |= SRB_BEOT;                             /* set BOT */
        break;

    case SRA_SRB:                                       /* space rev blk */
        if ((st = sim_tape_sprecr (uptr, &tbc)))        /* space rev, err? */
            r = ct_map_err (uptr, st);                  /* map error */
         break;

    case SRA_SRF:                                       /* space rev file */
        while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;
        r = ct_map_err (uptr, st);                      /* map error */
        break;

    case SRA_SFF:                                       /* space fwd file */
        while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ;
        r = ct_map_err (uptr, st);                      /* map error */
        break;

    default:                                            /* never get here! */
        return SCPE_IERR;        
        }                                               /* end case */

ct_updsta (uptr);                                       /* update status */
if (DEBUG_PRS (ct_dev)) fprintf (sim_deb,
    ">>CT done: op=%o, statusA = %o, statusB = %o, pos=%d\n",
    uptr->FNC, ct_sra, ct_srb, uptr->pos);
return r;
}

/* Update controller status */

uint32 ct_updsta (UNIT *uptr)
{
int32 srb;

if (uptr == NULL) {                                     /* unit specified? */
    uptr = ct_busy ();                                  /* use busy unit */
    if ((uptr == NULL) && (ct_sra & SRA_ENAB))          /* none busy? */
        uptr = ct_dev.units + GET_UNIT (ct_sra);        /* use sel unit */
    }
else if (ct_srb & SRB_EOF)                              /* save gap */
    uptr->UST |= UST_GAP;
if (uptr) {                                             /* any unit? */
    ct_srb &= ~(SRB_WLK|SRB_EMP|SRB_RDY);               /* clear dyn flags */
    if ((uptr->flags & UNIT_ATT) == 0)                  /* unattached? */
        ct_srb = (ct_srb | SRB_EMP|SRB_WLK) & ~SRB_REW; /* empty, locked */
    if (!sim_is_active (uptr)) {                        /* not busy? */
        ct_srb = (ct_srb | SRB_RDY) & ~SRB_REW;         /* ready, ~rew */
        }
    if (sim_tape_wrp (uptr) || (ct_srb & SRB_REW))      /* locked or rew? */
        ct_srb |= SRB_WLK;                              /* set locked */
    }
if (ct_sra & SRA_ENAB)                                  /* can TA see TU60? */
    srb = ct_srb;
else srb = 0;                                           /* no */
if ((ct_sra & SRA_IE) &&                                /* int enabled? */
    (ct_df || (srb & (SRB_ALLERR|SRB_RDY))))            /* any flag? */
    int_req |= INT_CT;                                  /* set int req */
else int_req &= ~INT_CT;                                /* no, clr int req */
return srb;
}

/* Set data flag */

void ct_set_df (t_bool timchk)
{
if (ct_df && timchk)                                    /* flag still set? */
    ct_srb |= SRB_TIM;
ct_df = 1;                                              /* set data flag */
if (ct_sra & SRA_IE)                                    /* if ie, int req */
    int_req |= INT_CT;
return;
}

/* Read character */

t_bool ct_read_char (void)
{
if (ct_bptr < ct_blnt) {                                /* more chars? */
    ct_db = ct_xb[ct_bptr++];
    return TRUE;
    }
ct_db = 0;
ct_srb |= SRB_CRC;                                      /* overrun */
return FALSE;
}

/* Test if controller busy */

UNIT *ct_busy (void)
{
uint32 u;
UNIT *uptr;

for (u = 0; u < CT_NUMDR; u++) {                        /* loop thru units */
    uptr = ct_dev.units + u;
    if (sim_is_active (uptr))
        return uptr;
    }
return NULL;
}

/* Calculate CRC on buffer */

uint32 ct_crc (uint8 *buf, uint32 cnt)
{
uint32 crc, i, j;

crc = 0;
for (i = 0; i < cnt; i++) {
    crc = crc ^ (((uint32) buf[i]) << 8);
    for (j = 0; j < 8; j++) {
        if (crc & 1)
            crc = (crc >> 1) ^ 0xA001;
        else crc = crc >> 1;
        }
    }
return crc;
}

/* Map error status */

t_stat ct_map_err (UNIT *uptr, t_stat st)
{
switch (st) {

    case MTSE_FMT:                                      /* illegal fmt */
    case MTSE_UNATT:                                    /* unattached */
        ct_srb |= SRB_CRC;
    case MTSE_OK:                                       /* no error */
        return SCPE_IERR;                               /* never get here! */

    case MTSE_TMK:                                      /* end of file */
        ct_srb |= SRB_EOF;
        break;

    case MTSE_IOERR:                                    /* IO error */
        ct_srb |= SRB_CRC;                              /* set crc err */
        if (ct_stopioe)
            return SCPE_IOERR;
        break;

    case MTSE_INVRL:                                    /* invalid rec lnt */
        ct_srb |= SRB_CRC;                              /* set crc err */
        return SCPE_MTRLNT;

    case MTSE_RECE:                                     /* record in error */
    case MTSE_EOM:                                      /* end of medium */
        ct_srb |= SRB_CRC;                              /* set crc err */
        break;

    case MTSE_BOT:                                      /* reverse into BOT */
        ct_srb |= SRB_BEOT;                             /* set BOT */
        break;

    case MTSE_WRP:                                      /* write protect */
        ct_srb |= SRB_WLE;                              /* set wlk err */
        break;
        }

return SCPE_OK;
}

/* Reset routine */

t_stat ct_reset (DEVICE *dptr)
{
uint32 u;
UNIT *uptr;

ct_sra = 0;
ct_srb = 0;
ct_df = 0;
ct_db = 0;
ct_write = 0;
ct_bptr = 0;
ct_blnt = 0;
int_req = int_req & ~INT_CT;                            /* clear interrupt */
for (u = 0; u < CT_NUMDR; u++) {                        /* loop thru units */
    uptr = ct_dev.units + u;
    sim_cancel (uptr);                                  /* cancel activity */
    sim_tape_reset (uptr);                              /* reset tape */
    }
if (ct_xb == NULL)
    ct_xb = (uint8 *) calloc (CT_MAXFR + 2, sizeof (uint8));
if (ct_xb == NULL)
    return SCPE_MEM;
return SCPE_OK;
}

/* Attach routine */

t_stat ct_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;

r = sim_tape_attach (uptr, cptr);
if (r != SCPE_OK)
    return r;
ct_updsta (NULL);
uptr->UST = 0;
return r;
}

/* Detach routine */

t_stat ct_detach (UNIT* uptr)
{
t_stat r;

if (!(uptr->flags & UNIT_ATT))                          /* check attached */
    return SCPE_OK;
r = sim_tape_detach (uptr);
ct_updsta (NULL);
uptr->UST = 0;
return r;
}

/* Bootstrap routine */

#define BOOT_START 04000
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))

static const uint16 boot_rom[] = {
    01237,          /* BOOT,    TAD M50     /change CRC to REW */
    01206,          /* CRCCHK,  TAD L260    /crc op */
    06704,          /*          KLSA        /load op */
    06706,          /*          KGOA        /start */
    06703,          /*          KSBF        /ready? */
    05204,          /* RDCOD,   JMP .-1     /loop */
    07264,          /* L260,    CML STA RAL /L = 1, AC = halt */
    06702,          /*          KSEN        /error? */
    07610,          /*          SKP CLA     /halt on any error */
    03211,          /*          DCA .       /except REW or FFG */
    03636,          /*          DCA I PTR   /TAD I PTR mustn't change L */
    01205,          /*          TAD RDCOD   /read op */
    06704,          /*          KLSA        /load op */
    06706,          /*          KGOA        /start */
    06701,          /* LOOP,    KSDF        /data ready? */
    05216,          /*          JMP .-1     /loop */
    07002,          /*          BSW         /to upper 6b */
    07430,          /*          SZL         /second byte? */
    01636,          /*          TAD I PTR   /yes */
    07022,          /*          CML BSW     /swap back */
    03636,          /*          DCA I PTR   /store in mem */
    07420,          /*          SNL         /done with both bytes? */
    02236,          /*          ISZ PTR     /yes, bump mem ptr */
    02235,          /*          ISZ KNT     /done with record? */
    05215,          /*          JMP LOOP    /next byte */
    07346,          /*          STA CLL RTL */
    07002,          /*          BSW         /AC = 7757 */
    03235,          /*          STA KNT     /now read 200 byte record */
    05201,          /*          JMP CRCCHK  /go check CRC */
    07737,          /* KNT,     7737        /1's compl of byte count */
    03557,          /* PTR,     3557        /load point */
    07730,          /* M50,     7730        /CLA SPA SZL */
    };

t_stat ct_boot (int32 unitno, DEVICE *dptr)
{
size_t i;
extern uint16 M[];

if ((ct_dib.dev != DEV_CT) || unitno)                   /* only std devno */
     return STOP_NOTSTD;
for (i = 0; i < BOOT_LEN; i++)
    M[BOOT_START + i] = boot_rom[i];
cpu_set_bootpc (BOOT_START);
return SCPE_OK;
}
Added src/PDP8/pdp8_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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_defs.h: PDP-8 simulator definitions

   Copyright (c) 1993-2016, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   18-Sep-16    RMS     Added support for 16 additional terminals
   18-Sep-13    RMS     Added set_bootpc prototype
   18-Apr-12    RMS     Removed separate timer for additional terminals;
                        Added clock_cosched prototype
   22-May-10    RMS     Added check for 64b definitions
   21-Aug-07    RMS     Added FPP8 support
   13-Dec-06    RMS     Added TA8E support
   30-Oct-06    RMS     Added infinite loop stop
   13-Oct-03    RMS     Added TSC8-75 support
   04-Oct-02    RMS     Added variable device number support
   20-Jan-02    RMS     Fixed bug in TTx interrupt enable initialization
   25-Nov-01    RMS     Added RL8A support
   16-Sep-01    RMS     Added multiple KL support
   18-Mar-01    RMS     Added DF32 support
   15-Feb-01    RMS     Added DECtape support
   14-Apr-99    RMS     Changed t_addr to unsigned
   19-Mar-95    RMS     Added dynamic memory size
   02-May-94    RMS     Added non-existent memory handling

   The author gratefully acknowledges the help of Max Burnet, Richie Lary,
   and Bill Haygood in resolving questions about the PDP-8
*/

#ifndef PDP8_DEFS_H_
#define PDP8_DEFS_H_   0

#include "sim_defs.h"                                   /* simulator defns */

#if defined(USE_INT64) || defined(USE_ADDR64)
#error "PDP-8 does not support 64b values!"
#endif

/* Simulator stop codes */

#define STOP_RSRV       1                               /* must be 1 */
#define STOP_HALT       2                               /* HALT */
#define STOP_IBKPT      3                               /* breakpoint */
#define STOP_OPBKPT     4                               /* Opcode/Instruction breakpoint */
#define STOP_NOTSTD     5                               /* non-std devno */
#define STOP_DTOFF      6                               /* DECtape off reel */
#define STOP_LOOP       7                               /* infinite loop */

/* Memory */

#define MAXMEMSIZE      32768                           /* max memory size */
#define MEMSIZE         (cpu_unit.capac)                /* actual memory size */
#define ADDRMASK        (MAXMEMSIZE - 1)                /* address mask */
#define MEM_ADDR_OK(x)  (((uint32) (x)) < MEMSIZE)

/* IOT subroutine return codes */

#define IOT_V_SKP       12                              /* skip */
#define IOT_V_REASON    13                              /* reason */
#define IOT_SKP         (1 << IOT_V_SKP)
#define IOT_REASON      (1 << IOT_V_REASON)
#define IORETURN(f,v)   ((f)? (v): SCPE_OK)             /* stop on error */

/* Timers */

#define TMR_CLK         0                               /* timer 0 = clock */

/* Device information block */

#define DEV_MAXBLK      8                               /* max dev block */
#define DEV_MAX         64                              /* total devices */

typedef struct {
    uint32              dev;                            /* device number */
    int32               (*dsp)(int32 IR, int32 dat);    /* dispatch */
    } DIB_DSP;

typedef struct {
    uint32              dev;                            /* base dev number */
    uint32              num;                            /* number of slots */
    int32               (*dsp[DEV_MAXBLK])(int32 IR, int32 dat);
    DIB_DSP             *dsp_tbl;                       /* optional table */
    } DIB;

/* Standard device numbers */

#define DEV_PTR         001                             /* paper tape reader */
#define DEV_PTP         002                             /* paper tape punch */
#define DEV_TTI         003                             /* console input */
#define DEV_TTO         004                             /* console output */
#define DEV_CLK         013                             /* clock */
#define DEV_TSC         036
#define DEV_KJ8         040                             /* extra terminals */
#define DEV_FPP         055                             /* floating point */
#define DEV_DF          060                             /* DF32 */
#define DEV_RF          060                             /* RF08 */
#define DEV_RL          060                             /* RL8A */
#define DEV_LPT         066                             /* line printer */
#define DEV_MT          070                             /* TM8E */
#define DEV_CT          070                             /* TA8E */
#define DEV_RK          074                             /* RK8E */
#define DEV_RX          075                             /* RX8E/RX28 */
#define DEV_DTA         076                             /* TC08 */
#define DEV_TD8E        077                             /* TD8E */

/* Extra PTO8/KL8JA devices */

#define DEV_TTI1        040
#define DEV_TTO1        041
#define DEV_TTI2        042
#define DEV_TTO2        043
#define DEV_TTI3        044
#define DEV_TTO3        045
#define DEV_TTI4        046
#define DEV_TTO4        047
#define DEV_TTI5        034
#define DEV_TTO5        035
#define DEV_TTI6        011
#define DEV_TTO6        012
#define DEV_TTI7        030
#define DEV_TTO7        031
#define DEV_TTI8        032
#define DEV_TTO8        033
#define DEV_TTI9        050
#define DEV_TTO9        051
#define DEV_TTI10       052
#define DEV_TTO10       053
#define DEV_TTI11       054
#define DEV_TTO11       055                             /* conflict: FPP */
#define DEV_TTI12       056                             /* conflict: FPP */
#define DEV_TTO12       057
#define DEV_TTI13       070                             /* conflict: CT, MT */
#define DEV_TTO13       071
#define DEV_TTI14       036                             /* conflict: TSC */
#define DEV_TTO14       037
#define DEV_TTI15       072
#define DEV_TTO15       073
#define DEV_TTI16       006
#define DEV_TTO16       007

/* Interrupt flags

   The interrupt flags consist of three groups:

   1.   Devices with individual interrupt enables.  These record
        their interrupt requests in device_done and their enables
        in device_enable, and must occupy the low bit positions.

   2.   Devices without interrupt enables.  These record their
        interrupt requests directly in int_req, and must occupy
        the middle bit positions.

   3.   Overhead.  These exist only in int_req and must occupy the
        high bit positions.

   Because the PDP-8 does not have priority interrupts, the order
   of devices within groups does not matter.

   Note: all extra KL input and output interrupts must be assigned
   to contiguous bits.
*/

#define INT_V_START     0                               /* enable start */
#define INT_V_LPT       (INT_V_START+0)                 /* line printer */
#define INT_V_PTP       (INT_V_START+1)                 /* tape punch */
#define INT_V_PTR       (INT_V_START+2)                 /* tape reader */
#define INT_V_TTO       (INT_V_START+3)                 /* terminal */
#define INT_V_TTI       (INT_V_START+4)                 /* keyboard */
#define INT_V_CLK       (INT_V_START+5)                 /* clock */
#define INT_V_TTO1      (INT_V_START+6)                 /* tto1 */
//#define INT_V_TTO2      (INT_V_START+7)                 /* tto2 */
//#define INT_V_TTO3      (INT_V_START+8)                 /* tto3 */
//#define INT_V_TTO4      (INT_V_START+9)                 /* tto4 */
#define INT_V_TTI1      (INT_V_START+10)                /* tti1 */
//#define INT_V_TTI2      (INT_V_START+11)                /* tti2 */
//#define INT_V_TTI3      (INT_V_START+12)                /* tti3 */
//#define INT_V_TTI4      (INT_V_START+13)                /* tti4 */
#define INT_V_DIRECT    (INT_V_START+14)                /* direct start */
#define INT_V_RX        (INT_V_DIRECT+0)                /* RX8E */
#define INT_V_RK        (INT_V_DIRECT+1)                /* RK8E */
#define INT_V_RF        (INT_V_DIRECT+2)                /* RF08 */
#define INT_V_DF        (INT_V_DIRECT+3)                /* DF32 */
#define INT_V_MT        (INT_V_DIRECT+4)                /* TM8E */
#define INT_V_DTA       (INT_V_DIRECT+5)                /* TC08 */
#define INT_V_RL        (INT_V_DIRECT+6)                /* RL8A */
#define INT_V_CT        (INT_V_DIRECT+7)                /* TA8E int */
#define INT_V_PWR       (INT_V_DIRECT+8)                /* power int */
#define INT_V_UF        (INT_V_DIRECT+9)                /* user int */
#define INT_V_TSC       (INT_V_DIRECT+10)               /* TSC8-75 int */
#define INT_V_FPP       (INT_V_DIRECT+11)               /* FPP8 */
#define INT_V_OVHD      (INT_V_DIRECT+12)               /* overhead start */
#define INT_V_NO_ION_PENDING (INT_V_OVHD+0)             /* ion pending */
#define INT_V_NO_CIF_PENDING (INT_V_OVHD+1)             /* cif pending */
#define INT_V_ION       (INT_V_OVHD+2)                  /* interrupts on */

#define INT_LPT         (1 << INT_V_LPT)
#define INT_PTP         (1 << INT_V_PTP)
#define INT_PTR         (1 << INT_V_PTR)
#define INT_TTO         (1 << INT_V_TTO)
#define INT_TTI         (1 << INT_V_TTI)
#define INT_CLK         (1 << INT_V_CLK)
#define INT_TTO1        (1 << INT_V_TTO1)
//#define INT_TTO2        (1 << INT_V_TTO2)
//#define INT_TTO3        (1 << INT_V_TTO3)
//#define INT_TTO4        (1 << INT_V_TTO4)
#define INT_TTI1        (1 << INT_V_TTI1)
//#define INT_TTI2        (1 << INT_V_TTI2)
//#define INT_TTI3        (1 << INT_V_TTI3)
//#define INT_TTI4        (1 << INT_V_TTI4)
#define INT_RX          (1 << INT_V_RX)
#define INT_RK          (1 << INT_V_RK)
#define INT_RF          (1 << INT_V_RF)
#define INT_DF          (1 << INT_V_DF)
#define INT_MT          (1 << INT_V_MT)
#define INT_DTA         (1 << INT_V_DTA)
#define INT_RL          (1 << INT_V_RL)
#define INT_CT          (1 << INT_V_CT)
#define INT_PWR         (1 << INT_V_PWR)
#define INT_UF          (1 << INT_V_UF)
#define INT_TSC         (1 << INT_V_TSC)
#define INT_FPP         (1 << INT_V_FPP)
#define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING)
#define INT_NO_CIF_PENDING (1 << INT_V_NO_CIF_PENDING)
#define INT_ION         (1 << INT_V_ION)
#define INT_DEV_ENABLE  ((1 << INT_V_DIRECT) - 1)       /* devices w/enables */
#define INT_ALL         ((1 << INT_V_OVHD) - 1)         /* all interrupts */
#define INT_INIT_ENABLE (INT_TTI+INT_TTO+INT_PTR+INT_PTP+INT_LPT) | \
                        (INT_TTI1+INT_TTO1)
#define INT_PENDING     (INT_ION+INT_NO_CIF_PENDING+INT_NO_ION_PENDING)
#define INT_UPDATE      ((int_req & ~INT_DEV_ENABLE) | (dev_done & int_enable))

/* Function prototypes */

t_stat set_dev (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat show_dev (FILE *st, UNIT *uptr, int32 val, CONST void *desc);

void cpu_set_bootpc (int32 pc);

#endif
Added src/PDP8/pdp8_df.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_df.c: DF32 fixed head disk simulator

   Copyright (c) 1993-2013, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   df           DF32 fixed head disk

   17-Sep-13    RMS     Changed to use central set_bootpc routine
   03-Sep-13    RMS     Added explicit void * cast
   15-May-06    RMS     Fixed bug in autosize attach (Dave Gesswein)
   07-Jan-06    RMS     Fixed unaligned register access bug (Doug Carman)
   04-Jan-04    RMS     Changed sim_fsize calling sequence
   26-Oct-03    RMS     Cleaned up buffer copy code
   26-Jul-03    RMS     Fixed bug in set size routine
   14-Mar-03    RMS     Fixed variable platter interaction with save/restore
   03-Mar-03    RMS     Fixed autosizing
   02-Feb-03    RMS     Added variable platter and autosizing support
   04-Oct-02    RMS     Added DIBs, device number support
   28-Nov-01    RMS     Added RL8A support
   25-Apr-01    RMS     Added device enable/disable support

   The DF32 is a head-per-track disk.  It uses the three cycle data break
   facility.  To minimize overhead, the entire DF32 is buffered in memory.

   Two timing parameters are provided:

   df_time      Interword timing, must be non-zero
   df_burst     Burst mode, if 0, DMA occurs cycle by cycle; otherwise,
                DMA occurs in a burst
*/

#include "pdp8_defs.h"
#include <math.h>

#define UNIT_V_AUTO     (UNIT_V_UF + 0)                 /* autosize */
#define UNIT_V_PLAT     (UNIT_V_UF + 1)                 /* #platters - 1 */
#define UNIT_M_PLAT     03
#define UNIT_PLAT       (UNIT_M_PLAT << UNIT_V_PLAT)
#define UNIT_GETP(x)    ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1)
#define UNIT_AUTO       (1 << UNIT_V_AUTO)
#define UNIT_PLAT       (UNIT_M_PLAT << UNIT_V_PLAT)

/* Constants */

#define DF_NUMWD        2048                            /* words/track */
#define DF_NUMTR        16                              /* tracks/disk */
#define DF_DKSIZE       (DF_NUMTR * DF_NUMWD)           /* words/disk */
#define DF_NUMDK        4                               /* disks/controller */
#define DF_WC           07750                           /* word count */
#define DF_MA           07751                           /* mem address */
#define DF_WMASK        (DF_NUMWD - 1)                  /* word mask */

/* Parameters in the unit descriptor */

#define FUNC            u4                              /* function */
#define DF_READ         2                               /* read */
#define DF_WRITE        4                               /* write */

/* Status register */

#define DFS_PCA         04000                           /* photocell status */
#define DFS_DEX         03700                           /* disk addr extension */
#define DFS_MEX         00070                           /* mem addr extension */
#define DFS_DRL         00004                           /* data late error */
#define DFS_WLS         00002                           /* write lock error */
#define DFS_NXD         00002                           /* non-existent disk */
#define DFS_PER         00001                           /* parity error */
#define DFS_ERR         (DFS_DRL | DFS_WLS | DFS_PER)
#define DFS_V_DEX       6
#define DFS_V_MEX       3

#define GET_MEX(x)      (((x) & DFS_MEX) << (12 - DFS_V_MEX))
#define GET_DEX(x)      (((x) & DFS_DEX) << (12 - DFS_V_DEX))
#define GET_POS(x)      ((int) fmod (sim_gtime() / ((double) (x)), \
                        ((double) DF_NUMWD)))
#define UPDATE_PCELL    if (GET_POS (df_time) < 6) df_sta = df_sta | DFS_PCA; \
                        else df_sta = df_sta & ~DFS_PCA

extern uint16 M[];
extern int32 int_req, stop_inst;
extern UNIT cpu_unit;

int32 df_sta = 0;                                       /* status register */
int32 df_da = 0;                                        /* disk address */
int32 df_done = 0;                                      /* done flag */
int32 df_wlk = 0;                                       /* write lock */
int32 df_time = 10;                                     /* inter-word time */
int32 df_burst = 1;                                     /* burst mode flag */
int32 df_stopioe = 1;                                   /* stop on error */

int32 df60 (int32 IR, int32 AC);
int32 df61 (int32 IR, int32 AC);
int32 df62 (int32 IR, int32 AC);
t_stat df_svc (UNIT *uptr);
t_stat pcell_svc (UNIT *uptr);
t_stat df_reset (DEVICE *dptr);
t_stat df_boot (int32 unitno, DEVICE *dptr);
t_stat df_attach (UNIT *uptr, CONST char *cptr);
t_stat df_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc);

/* DF32 data structures

   df_dev       RF device descriptor
   df_unit      RF unit descriptor
   pcell_unit   photocell timing unit (orphan)
   df_reg       RF register list
*/

DIB df_dib = { DEV_DF, 3, { &df60, &df61, &df62 } };

UNIT df_unit = {
    UDATA (&df_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
           DF_DKSIZE)
    };

REG df_reg[] = {
    { ORDATAD (STA, df_sta, 12, "status, disk and memory address extension") },
    { ORDATAD (DA, df_da, 12, "low order disk address") },
    { ORDATAD (WC, M[DF_WC], 12, "word count (in memory)"), REG_FIT },
    { ORDATAD (MA, M[DF_MA], 12, "memory address (in memory)"), REG_FIT },
    { FLDATAD (DONE, df_done, 0, "device done flag") },
    { FLDATAD (INT, int_req, INT_V_DF, "interrupt pending flag") },
    { ORDATAD (WLS, df_wlk, 8, "write lock switches") },
    { DRDATAD (TIME, df_time, 24, "rotational delay, per word"), REG_NZ + PV_LEFT },
    { FLDATAD (BURST, df_burst, 0, "burst flag") },
    { FLDATAD (STOP_IOE, df_stopioe, 0, "stop on I/O error") },
    { DRDATA (CAPAC, df_unit.capac, 18), REG_HRO },
    { ORDATA (DEVNUM, df_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB df_mod[] = {
    { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &df_set_size },
    { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &df_set_size },
    { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &df_set_size },
    { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &df_set_size },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { 0 }
    };

DEVICE df_dev = {
    "DF", &df_unit, df_reg, df_mod,
    1, 8, 17, 1, 8, 12,
    NULL, NULL, &df_reset,
    &df_boot, &df_attach, NULL,
    &df_dib, DEV_DISABLE
    };

/* IOT routines */

int32 df60 (int32 IR, int32 AC)
{
int32 t;
int32 pulse = IR & 07;

UPDATE_PCELL;                                           /* update photocell */
if (pulse & 1) {                                        /* DCMA */
    df_da = 0;                                          /* clear disk addr */
    df_done = 0;                                        /* clear done */
    df_sta = df_sta & ~DFS_ERR;                         /* clear errors */
    int_req = int_req & ~INT_DF;                        /* clear int req */
    }
if (pulse & 6) {                                        /* DMAR, DMAW */
    df_da = df_da | AC;                                 /* disk addr |= AC */
    df_unit.FUNC = pulse & ~1;                          /* save function */
    t = (df_da & DF_WMASK) - GET_POS (df_time);         /* delta to new loc */
    if (t < 0)                                          /* wrap around? */
        t = t + DF_NUMWD;
    sim_activate (&df_unit, t * df_time);               /* schedule op */
    AC = 0;                                             /* clear AC */
    }
return AC;
}

/* Based on the hardware implementation.  DEAL and DEAC work as follows:

   6615         pulse 1 = clear df_sta<dex,mex>
                pulse 4 = df_sta = df_sta | AC<dex,mex>
                          AC = AC | old_df_sta
   6616         pulse 2 = clear AC, skip if address confirmed
                pulse 4 = df_sta = df_sta | AC<dex,mex> = 0 (nop)
                          AC = AC | old_df_sta
*/

int32 df61 (int32 IR, int32 AC)
{
int32 old_df_sta = df_sta;
int32 pulse = IR & 07;

UPDATE_PCELL;                                           /* update photocell */
if (pulse & 1)                                          /* DCEA */
    df_sta = df_sta & ~(DFS_DEX | DFS_MEX);             /* clear dex, mex */
if (pulse & 2)                                          /* DSAC */
    AC = ((df_da & DF_WMASK) == GET_POS (df_time))? IOT_SKP: 0;
if (pulse & 4) {
    df_sta = df_sta | (AC & (DFS_DEX | DFS_MEX));       /* DEAL */
    AC = AC | old_df_sta;                               /* DEAC */
    }
return AC;
}

int32 df62 (int32 IR, int32 AC)
{
int32 pulse = IR & 07;

UPDATE_PCELL;                                           /* update photocell */
if (pulse & 1) {                                        /* DFSE */
    if ((df_sta & DFS_ERR) == 0)
        AC = AC | IOT_SKP;
    }
if (pulse & 2) {                                        /* DFSC */
    if (pulse & 4)                                      /* for DMAC */
        AC = AC & ~07777;
    else if (df_done)
        AC = AC | IOT_SKP;
    }
if (pulse & 4)                                          /* DMAC */
    AC = AC | df_da;
return AC;
}

/* Unit service

   Note that for reads and writes, memory addresses wrap around in the
   current field.  This code assumes the entire disk is buffered.
*/

t_stat df_svc (UNIT *uptr)
{
int32 pa, t, mex;
uint32 da;
int16 *fbuf = (int16 *) uptr->filebuf;

UPDATE_PCELL;                                           /* update photocell */
if ((uptr->flags & UNIT_BUF) == 0) {                    /* not buf? abort */
    df_done = 1;
    int_req = int_req | INT_DF;                         /* update int req */
    return IORETURN (df_stopioe, SCPE_UNATT);
    }

mex = GET_MEX (df_sta);
da = GET_DEX (df_sta) | df_da;                          /* form disk addr */
do {
    if (da >= uptr->capac) {                            /* nx disk addr? */
        df_sta = df_sta | DFS_NXD;
        break;
        }
    M[DF_WC] = (M[DF_WC] + 1) & 07777;                  /* incr word count */
    M[DF_MA] = (M[DF_MA] + 1) & 07777;                  /* incr mem addr */
    pa = mex | M[DF_MA];                                /* add extension */
    if (uptr->FUNC == DF_READ) {                        /* read? */
        if (MEM_ADDR_OK (pa)) M[pa] = fbuf[da];         /* if !nxm, read wd */
        }
    else {                                              /* write */
        t = (da >> 14) & 07;                            /* check wr lock */
        if ((df_wlk >> t) & 1)                          /* locked? set err */
            df_sta = df_sta | DFS_WLS;
        else {                                          /* not locked */
            fbuf[da] = M[pa];                           /* write word */
            if (da >= uptr->hwmark) uptr->hwmark = da + 1;
            }
        }
    da = (da + 1) & 0377777;                            /* incr disk addr */
    } while ((M[DF_WC] != 0) && (df_burst != 0));       /* brk if wc, no brst */

if ((M[DF_WC] != 0) && ((df_sta & DFS_ERR) == 0))       /* more to do? */
    sim_activate (&df_unit, df_time);                   /* sched next */
else {
    if (uptr->FUNC != DF_READ)
        da = (da - 1) & 0377777;
    df_done = 1;                                        /* done */
    int_req = int_req | INT_DF;                         /* update int req */
    }
df_sta = (df_sta & ~DFS_DEX) | ((da >> (12 - DFS_V_DEX)) & DFS_DEX);
df_da = da & 07777;                                     /* separate disk addr */
return SCPE_OK;
}

/* Reset routine */

t_stat df_reset (DEVICE *dptr)
{
df_sta = df_da = 0;
df_done = 1;
int_req = int_req & ~INT_DF;                            /* clear interrupt */
sim_cancel (&df_unit);
return SCPE_OK;
}

/* Bootstrap routine */

#define OS8_START       07750
#define OS8_LEN         (sizeof (os8_rom) / sizeof (int16))
#define DM4_START       00200
#define DM4_LEN         (sizeof (dm4_rom) / sizeof (int16))

static const uint16 os8_rom[] = {
    07600,                      /* 7750, CLA CLL        ; also word count */
    06603,                      /* 7751, DMAR           ; also address */
    06622,                      /* 7752, DFSC           ; done? */
    05352,                      /* 7753, JMP .-1        ; no */
    05752                       /* 7754, JMP @.-2       ; enter boot */
    };

static const uint16 dm4_rom[] = {
    00200, 07600,               /* 0200, CLA CLL */
    00201, 06603,               /* 0201, DMAR           ; read */
    00202, 06622,               /* 0202, DFSC           ; done? */
    00203, 05202,               /* 0203, JMP .-1        ; no */
    00204, 05600,               /* 0204, JMP @.-4       ; enter boot */
    07750, 07576,               /* 7750, 7576           ; word count */
    07751, 07576                /* 7751, 7576           ; address */
    };

t_stat df_boot (int32 unitno, DEVICE *dptr)
{
size_t i;

if (sim_switches & SWMASK ('D')) {
    for (i = 0; i < DM4_LEN; i = i + 2)
        M[dm4_rom[i]] = dm4_rom[i + 1];
    cpu_set_bootpc (DM4_START);
    }
else {
    for (i = 0; i < OS8_LEN; i++)
         M[OS8_START + i] = os8_rom[i];
    cpu_set_bootpc (OS8_START);
    }
return SCPE_OK;
}

/* Attach routine */

t_stat df_attach (UNIT *uptr, CONST char *cptr)
{
uint32 p, sz;
uint32 ds_bytes = DF_DKSIZE * sizeof (int16);

if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) {
    p = (sz + ds_bytes - 1) / ds_bytes;
    if (p >= DF_NUMDK)
        p = DF_NUMDK - 1;
    uptr->flags = (uptr->flags & ~UNIT_PLAT) |
         (p << UNIT_V_PLAT);
    }
uptr->capac = UNIT_GETP (uptr->flags) * DF_DKSIZE;
return attach_unit (uptr, cptr);
}

/* Change disk size */

t_stat df_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if (val < 0)
    return SCPE_IERR;
if (uptr->flags & UNIT_ATT)
    return SCPE_ALATT;
uptr->capac = UNIT_GETP (val) * DF_DKSIZE;
uptr->flags = uptr->flags & ~UNIT_AUTO;
return SCPE_OK;
}
Added src/PDP8/pdp8_dt.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_dt.c: PDP-8 DECtape simulator

   Copyright (c) 1993-2013, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   dt           TC08/TU56 DECtape

   17-Sep-13    RMS     Changed to use central set_bootpc routine
   23-Jun-06    RMS     Fixed switch conflict in ATTACH
   07-Jan-06    RMS     Fixed unaligned register access bug (Doug Carman)
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   25-Jan-04    RMS     Revised for device debug support
   09-Jan-04    RMS     Changed sim_fsize calling sequence, added STOP_OFFR
   18-Oct-03    RMS     Fixed bugs in read all, tightened timing
   25-Apr-03    RMS     Revised for extended file support
   14-Mar-03    RMS     Fixed sizing interaction with save/restore
   17-Oct-02    RMS     Fixed bug in end of reel logic
   04-Oct-02    RMS     Added DIB, device number support
   12-Sep-02    RMS     Added support for 16b format
   30-May-02    RMS     Widened POS to 32b
   06-Jan-02    RMS     Changed enable/disable support
   30-Nov-01    RMS     Added read only unit, extended SET/SHOW support
   24-Nov-01    RMS     Changed POS, STATT, LASTT, FLG to arrays
   29-Aug-01    RMS     Added casts to PDP-18b packup routine
   17-Jul-01    RMS     Moved function prototype
   11-May-01    RMS     Fixed bug in reset
   25-Apr-01    RMS     Added device enable/disable support
   18-Apr-01    RMS     Changed to rewind tape before boot
   19-Mar-01    RMS     Changed bootstrap to support 4k disk monitor
   15-Mar-01    RMS     Added 129th word to PDP-8 format

   PDP-8 DECtapes are represented in memory by fixed length buffer of 16b words.
   Three file formats are supported:

        18b/36b                 256 words per block [256 x 18b]
        16b                     256 words per block [256 x 16b]
        12b                     129 words per block [129 x 12b]

   When a 16b or 18/36bb DECtape file is read in, it is converted to 12b format.

   DECtape motion is measured in 3b lines.  Time between lines is 33.33us.
   Tape density is nominally 300 lines per inch.  The format of a DECtape (as
   taken from the TD8E formatter) is:

        reverse end zone        8192 reverse end zone codes ~ 10 feet
        reverse buffer          200 interblock codes
        block 0
         :
        block n
        forward buffer          200 interblock codes
        forward end zone        8192 forward end zone codes ~ 10 feet

   A block consists of five 18b header words, a tape-specific number of data
   words, and five 18b trailer words.  All systems except the PDP-8 use a
   standard block length of 256 words; the PDP-8 uses a standard block length
   of 86 words (x 18b = 129 words x 12b).

   Because a DECtape file only contains data, the simulator cannot support
   write timing and mark track and can only do a limited implementation
   of read all and write all.  Read all assumes that the tape has been
   conventionally written forward:

        header word 0           0
        header word 1           block number (for forward reads)
        header words 2,3        0
        header word 4           checksum (for reverse reads)
        :
        trailer word 4          checksum (for forward reads)
        trailer words 3,2       0
        trailer word 1          block number (for reverse reads)
        trailer word 0          0

   Write all writes only the data words and dumps the non-data words in the
   bit bucket.
*/

#include "pdp8_defs.h"

#define DT_NUMDR        8                               /* #drives */
#define UNIT_V_WLK      (UNIT_V_UF + 0)                 /* write locked */
#define UNIT_V_8FMT     (UNIT_V_UF + 1)                 /* 12b format */
#define UNIT_V_11FMT    (UNIT_V_UF + 2)                 /* 16b format */
#define UNIT_WLK        (1 << UNIT_V_WLK)
#define UNIT_8FMT       (1 << UNIT_V_8FMT)
#define UNIT_11FMT      (1 << UNIT_V_11FMT)
#define STATE           u3                              /* unit state */
#define LASTT           u4                              /* last time update */
#define WRITTEN         u5                              /* device buffer is dirty and needs flushing */
#define DT_WC           07754                           /* word count */
#define DT_CA           07755                           /* current addr */
#define UNIT_WPRT       (UNIT_WLK | UNIT_RO)            /* write protect */

/* System independent DECtape constants */

#define DT_LPERMC       6                               /* lines per mark track */
#define DT_BLKWD        1                               /* blk no word in h/t */
#define DT_CSMWD        4                               /* checksum word in h/t */
#define DT_HTWRD        5                               /* header/trailer words */
#define DT_EZLIN        (8192 * DT_LPERMC)              /* end zone length */
#define DT_BFLIN        (200 * DT_LPERMC)               /* buffer length */
#define DT_BLKLN        (DT_BLKWD * DT_LPERMC)          /* blk no line in h/t */
#define DT_CSMLN        (DT_CSMWD * DT_LPERMC)          /* csum line in h/t */
#define DT_HTLIN        (DT_HTWRD * DT_LPERMC)          /* header/trailer lines */

/* 16b, 18b, 36b DECtape constants */

#define D18_WSIZE       6                               /* word size in lines */
#define D18_BSIZE       384                             /* block size in 12b */
#define D18_TSIZE       578                             /* tape size */
#define D18_LPERB       (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)
#define D18_FWDEZ       (DT_EZLIN + (D18_LPERB * D18_TSIZE))
#define D18_CAPAC       (D18_TSIZE * D18_BSIZE)         /* tape capacity */

#define D18_NBSIZE      ((D18_BSIZE * D8_WSIZE) / D18_WSIZE)
#define D18_FILSIZ      (D18_NBSIZE * D18_TSIZE * sizeof (int32))
#define D11_FILSIZ      (D18_NBSIZE * D18_TSIZE * sizeof (int16))

/* 12b DECtape constants */

#define D8_WSIZE        4                               /* word size in lines */
#define D8_BSIZE        129                             /* block size in 12b */
#define D8_TSIZE        1474                            /* tape size */
#define D8_LPERB        (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)
#define D8_FWDEZ        (DT_EZLIN + (D8_LPERB * D8_TSIZE))
#define D8_CAPAC        (D8_TSIZE * D8_BSIZE)           /* tape capacity */
#define D8_FILSIZ       (D8_CAPAC * sizeof (int16))

/* This controller */

#define DT_CAPAC        D8_CAPAC                        /* default */
#define DT_WSIZE        D8_WSIZE

/* Calculated constants, per unit */

#define DTU_BSIZE(u)    (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)
#define DTU_TSIZE(u)    (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)
#define DTU_LPERB(u)    (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)
#define DTU_FWDEZ(u)    (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)
#define DTU_CAPAC(u)    (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)

#define DT_LIN2BL(p,u)  (((p) - DT_EZLIN) / DTU_LPERB (u))
#define DT_LIN2OF(p,u)  (((p) - DT_EZLIN) % DTU_LPERB (u))
#define DT_LIN2WD(p,u)  ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE)
#define DT_BLK2LN(p,u)  (((p) * DTU_LPERB (u)) + DT_EZLIN)
#define DT_QREZ(u)      (((u)->pos) < DT_EZLIN)
#define DT_QFEZ(u)      (((u)->pos) >= ((uint32) DTU_FWDEZ (u)))
#define DT_QEZ(u)       (DT_QREZ (u) || DT_QFEZ (u))

/* Status register A */

#define DTA_V_UNIT      9                               /* unit select */
#define DTA_M_UNIT      07
#define DTA_UNIT        (DTA_M_UNIT << DTA_V_UNIT)
#define DTA_V_MOT       7                               /* motion */
#define DTA_M_MOT       03
#define DTA_V_MODE      6                               /* mode */
#define DTA_V_FNC       3                               /* function */
#define DTA_M_FNC       07
#define  FNC_MOVE        00                             /* move */
#define  FNC_SRCH        01                             /* search */
#define  FNC_READ        02                             /* read */
#define  FNC_RALL        03                             /* read all */
#define  FNC_WRIT        04                             /* write */
#define  FNC_WALL        05                             /* write all */
#define  FNC_WMRK        06                             /* write timing */
#define DTA_V_ENB       2                               /* int enable */
#define DTA_V_CERF      1                               /* clr error flag */
#define DTA_V_CDTF      0                               /* clr DECtape flag */
#define DTA_FWDRV       (1u << (DTA_V_MOT + 1))
#define DTA_STSTP       (1u << DTA_V_MOT)
#define DTA_MODE        (1u << DTA_V_MODE)
#define DTA_ENB         (1u << DTA_V_ENB)
#define DTA_CERF        (1u << DTA_V_CERF)
#define DTA_CDTF        (1u << DTA_V_CDTF)
#define DTA_RW          (07777 & ~(DTA_CERF | DTA_CDTF))

#define DTA_GETUNIT(x)  (((x) >> DTA_V_UNIT) & DTA_M_UNIT)
#define DTA_GETMOT(x)   (((x) >> DTA_V_MOT) & DTA_M_MOT)
#define DTA_GETFNC(x)   (((x) >> DTA_V_FNC) & DTA_M_FNC)

/* Status register B */

#define DTB_V_ERF       11                              /* error flag */
#define DTB_V_MRK       10                              /* mark trk err */
#define DTB_V_END       9                               /* end zone err */
#define DTB_V_SEL       8                               /* select err */
#define DTB_V_PAR       7                               /* parity err */
#define DTB_V_TIM       6                               /* timing err */
#define DTB_V_MEX       3                               /* memory extension */
#define DTB_M_MEX       07
#define DTB_MEX         (DTB_M_MEX << DTB_V_MEX)
#define DTB_V_DTF       0                               /* DECtape flag */
#define DTB_ERF         (1u << DTB_V_ERF)
#define DTB_MRK         (1u << DTB_V_MRK)
#define DTB_END         (1u << DTB_V_END)
#define DTB_SEL         (1u << DTB_V_SEL)
#define DTB_PAR         (1u << DTB_V_PAR)
#define DTB_TIM         (1u << DTB_V_TIM)
#define DTB_DTF         (1u << DTB_V_DTF)
#define DTB_ALLERR      (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \
                        DTB_PAR | DTB_TIM)
#define DTB_GETMEX(x)   (((x) & DTB_MEX) << (12 - DTB_V_MEX))

/* DECtape state */

#define DTS_V_MOT       3                               /* motion */
#define DTS_M_MOT       07
#define  DTS_STOP        0                              /* stopped */
#define  DTS_DECF        2                              /* decel, fwd */
#define  DTS_DECR        3                              /* decel, rev */
#define  DTS_ACCF        4                              /* accel, fwd */
#define  DTS_ACCR        5                              /* accel, rev */
#define  DTS_ATSF        6                              /* @speed, fwd */
#define  DTS_ATSR        7                              /* @speed, rev */
#define DTS_DIR         01                              /* dir mask */
#define DTS_V_FNC       0                               /* function */
#define DTS_M_FNC       07
#define  DTS_OFR        7                               /* "off reel" */
#define DTS_GETMOT(x)   (((x) >> DTS_V_MOT) & DTS_M_MOT)
#define DTS_GETFNC(x)   (((x) >> DTS_V_FNC) & DTS_M_FNC)
#define DTS_V_2ND       6                               /* next state */
#define DTS_V_3RD       (DTS_V_2ND + DTS_V_2ND)         /* next next */
#define DTS_STA(y,z)    (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC))
#define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z)
#define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \
                        ((DTS_STA (y, z)) << DTS_V_2ND)
#define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \
                        ((DTS_STA (y, z)) << DTS_V_3RD)
#define DTS_NXTSTA(x)   (x >> DTS_V_2ND)

/* Operation substates */

#define DTO_WCO         1                               /* wc overflow */
#define DTO_SOB         2                               /* start of block */

/* Logging */

#define LOG_MS          001                             /* move, search */
#define LOG_RW          002                             /* read, write */
#define LOG_BL          004                             /* block # lblk */

#define DT_UPDINT       if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \
                        int_req = int_req | INT_DTA; \
                        else int_req = int_req & ~INT_DTA;
#define ABS(x)          (((x) < 0)? (-(x)): (x))

extern uint16 M[];
extern int32 int_req;
extern UNIT cpu_unit;

int32 dtsa = 0;                                         /* status A */
int32 dtsb = 0;                                         /* status B */
int32 dt_ltime = 12;                                    /* interline time */
int32 dt_dctime = 40000;                                /* decel time */
int32 dt_substate = 0;
int32 dt_logblk = 0;
int32 dt_stopoffr = 0;

int32 dt76 (int32 IR, int32 AC);
int32 dt77 (int32 IR, int32 AC);
t_stat dt_svc (UNIT *uptr);
t_stat dt_reset (DEVICE *dptr);
t_stat dt_attach (UNIT *uptr, CONST char *cptr);
void dt_flush (UNIT *uptr);
t_stat dt_detach (UNIT *uptr);
t_stat dt_boot (int32 unitno, DEVICE *dptr);
void dt_deselect (int32 oldf);
void dt_newsa (int32 newf);
void dt_newfnc (UNIT *uptr, int32 newsta);
t_bool dt_setpos (UNIT *uptr);
void dt_schedez (UNIT *uptr, int32 dir);
void dt_seterr (UNIT *uptr, int32 e);
int32 dt_comobv (int32 val);
int32 dt_csum (UNIT *uptr, int32 blk);
int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir);

/* DT data structures

   dt_dev       DT device descriptor
   dt_unit      DT unit list
   dt_reg       DT register list
   dt_mod       DT modifier list
*/

DIB dt_dib = { DEV_DTA, 2, { &dt76, &dt77 } };

UNIT dt_unit[] = {
    { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
    { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
    { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
    { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
    { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
    { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
    { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
    { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }
    };

REG dt_reg[] = {
    { ORDATAD (DTSA, dtsa, 12, "status register A") },
    { ORDATAD (DTSB, dtsb, 12, "status register B") },
    { FLDATAD (INT, int_req, INT_V_DTA, "interrupt pending flag") },
    { FLDATAD (ENB, dtsa, DTA_V_ENB, "interrupt enable flag") },
    { FLDATAD (DTF, dtsb, DTB_V_DTF, "DECtape flag") },
    { FLDATAD (ERF, dtsb, DTB_V_ERF, "error flag") },
    { ORDATAD (WC, M[DT_WC], 12, "word count (memory location 7755)"), REG_FIT },
    { ORDATAD (CA, M[DT_CA], 12, "current address (memory location 7754)"), REG_FIT },
    { DRDATAD (LTIME, dt_ltime, 24, "time between lines"), REG_NZ | PV_LEFT },
    { DRDATAD (DCTIME, dt_dctime, 24, "time to decelerate to a full stop"), REG_NZ | PV_LEFT },
    { ORDATAD (SUBSTATE, dt_substate, 2, "read/write command substate") },
    { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN },
    { URDATAD (POS, dt_unit[0].pos, 10, T_ADDR_W, 0,
              DT_NUMDR, PV_LEFT | REG_RO, "position, in lines, units 0 to 7") },
    { URDATAD (STATT, dt_unit[0].STATE, 8, 18, 0,
              DT_NUMDR, REG_RO, "unit state, units 0 to 7") },
    { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0,
              DT_NUMDR, REG_HRO) },
    { FLDATAD (STOP_OFFR, dt_stopoffr, 0, "stop on off-reel error") },
    { ORDATA (DEVNUM, dt_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB dt_mod[] = {
    { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
    { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, 
    { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL },
    { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL },
    { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { 0 }
    };

DEBTAB dt_deb[] = {
    { "MOTION", LOG_MS },
    { "DATA", LOG_RW },
    { "BLOCK", LOG_BL },
    { NULL, 0 }
    };

DEVICE dt_dev = {
    "DT", dt_unit, dt_reg, dt_mod,
    DT_NUMDR, 8, 24, 1, 8, 12,
    NULL, NULL, &dt_reset,
    &dt_boot, &dt_attach, &dt_detach,
    &dt_dib, DEV_DISABLE | DEV_DEBUG, 0,
    dt_deb, NULL, NULL
    };

/* IOT routines */

int32 dt76 (int32 IR, int32 AC)
{
int32 pulse = IR & 07;
int32 old_dtsa = dtsa, fnc;
UNIT *uptr;

if (pulse & 01)                                         /* DTRA */
    AC = AC | dtsa;
if (pulse & 06) {                                       /* select */
    if (pulse & 02)                                     /* DTCA */
        dtsa = 0;
    if (pulse & 04) {                                   /* DTXA */
        if ((AC & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR;
        if ((AC & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF;
        dtsa = dtsa ^ (AC & DTA_RW);
        AC = 0;                                         /* clr AC */
        }
    if ((old_dtsa ^ dtsa) & DTA_UNIT)
        dt_deselect (old_dtsa);
    uptr = dt_dev.units + DTA_GETUNIT (dtsa);           /* get unit */
    fnc = DTA_GETFNC (dtsa);                            /* get fnc */
    if (((uptr->flags) & UNIT_DIS) ||                   /* disabled? */
         (fnc >= FNC_WMRK) ||                           /* write mark? */
        ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) ||
        ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT)))
        dt_seterr (uptr, DTB_SEL);                      /* select err */
    else dt_newsa (dtsa);
    DT_UPDINT;
    }
return AC;
}

int32 dt77 (int32 IR, int32 AC)
{
int32 pulse = IR & 07;

if ((pulse & 01) && (dtsb & (DTB_ERF |DTB_DTF)))        /* DTSF */
    AC = IOT_SKP | AC;
if (pulse & 02)                                         /* DTRB */
    AC = AC | dtsb;
if (pulse & 04) {                                       /* DTLB */
    dtsb = (dtsb & ~DTB_MEX) | (AC & DTB_MEX);
    AC = AC & ~07777;                                   /* clear AC */
    }
return AC;
}

/* Unit deselect */

void dt_deselect (int32 oldf)
{
int32 old_unit = DTA_GETUNIT (oldf);
UNIT *uptr = dt_dev.units + old_unit;
int32 old_mot = DTS_GETMOT (uptr->STATE);

if (old_mot >= DTS_ATSF)                                /* at speed? */
    dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR));
else if (old_mot >= DTS_ACCF)                           /* accelerating? */
    DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR);
return;
}

/* Command register change

   1. If change in motion, stop to start
        - schedule acceleration
        - set function as next state
   2. If change in motion, start to stop
        - if not already decelerating (could be reversing),
          schedule deceleration
   3. If change in direction,
        - if not decelerating, schedule deceleration
        - set accelerating (other dir) as next state
        - set function as next next state
   4. If not accelerating or at speed,
        - schedule acceleration
        - set function as next state
   5. If not yet at speed,
        - set function as next state
   6. If at speed,
        - set function as current state, schedule function
*/

void dt_newsa (int32 newf)
{
int32 new_unit, prev_mot, new_fnc;
int32 prev_mving, new_mving, prev_dir, new_dir;
UNIT *uptr;

new_unit = DTA_GETUNIT (newf);                          /* new, old units */
uptr = dt_dev.units + new_unit;
if ((uptr->flags & UNIT_ATT) == 0) {                    /* new unit attached? */
    dt_seterr (uptr, DTB_SEL);                          /* no, error */
    return;
    }
prev_mot = DTS_GETMOT (uptr->STATE);                    /* previous motion */
prev_mving = prev_mot != DTS_STOP;                      /* previous moving? */
prev_dir = prev_mot & DTS_DIR;                          /* previous dir? */
new_mving = (newf & DTA_STSTP) != 0;                    /* new moving? */
new_dir = (newf & DTA_FWDRV) != 0;                      /* new dir? */
new_fnc = DTA_GETFNC (newf);                            /* new function? */

if ((prev_mving | new_mving) == 0)                      /* stop to stop */
    return;

if (new_mving & ~prev_mving) {                          /* start? */
    if (dt_setpos (uptr))                               /* update pos */
        return;
    sim_cancel (uptr);                                  /* stop current */
    sim_activate (uptr, dt_dctime - (dt_dctime >> 2));  /* schedule acc */
    DTS_SETSTA (DTS_ACCF | new_dir, 0);                 /* state = accel */
    DTS_SET2ND (DTS_ATSF | new_dir, new_fnc);           /* next = fnc */
    return;
    }

if (prev_mving & ~new_mving) {                          /* stop? */
    if ((prev_mot & ~DTS_DIR) != DTS_DECF) {            /* !already stopping? */
        if (dt_setpos (uptr))                           /* update pos */
            return;
        sim_cancel (uptr);                              /* stop current */
        sim_activate (uptr, dt_dctime);                 /* schedule decel */
        }
    DTS_SETSTA (DTS_DECF | prev_dir, 0);                /* state = decel */
    return;
    }

if (prev_dir ^ new_dir) {                               /* dir chg? */
    if ((prev_mot & ~DTS_DIR) != DTS_DECF) {            /* !already stopping? */
        if (dt_setpos (uptr))                           /* update pos */
            return;
        sim_cancel (uptr);                              /* stop current */
        sim_activate (uptr, dt_dctime);                 /* schedule decel */
        }
    DTS_SETSTA (DTS_DECF | prev_dir, 0);                /* state = decel */
    DTS_SET2ND (DTS_ACCF | new_dir, 0);                 /* next = accel */
    DTS_SET3RD (DTS_ATSF | new_dir, new_fnc);           /* next next = fnc */
    return;
    }

if (prev_mot < DTS_ACCF) {                              /* not accel/at speed? */
    if (dt_setpos (uptr))                               /* update pos */
        return;
    sim_cancel (uptr);                                  /* cancel cur */
    sim_activate (uptr, dt_dctime - (dt_dctime >> 2));  /* sched accel */
    DTS_SETSTA (DTS_ACCF | new_dir, 0);                 /* state = accel */
    DTS_SET2ND (DTS_ATSF | new_dir, new_fnc);           /* next = fnc */
    return;
    }

if (prev_mot < DTS_ATSF) {                              /* not at speed? */
    DTS_SET2ND (DTS_ATSF | new_dir, new_fnc);           /* next = fnc */
    return;
    }

dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */
return; 
}

/* Schedule new DECtape function

   This routine is only called if
   - the selected unit is attached
   - the selected unit is at speed (forward or backward)

   This routine
   - updates the selected unit's position
   - updates the selected unit's state
   - schedules the new operation
*/

void dt_newfnc (UNIT *uptr, int32 newsta)
{
int32 fnc, dir, blk, unum, relpos, newpos;
uint32 oldpos;

oldpos = uptr->pos;                                     /* save old pos */
if (dt_setpos (uptr))                                   /* update pos */
    return;
uptr->STATE = newsta;                                   /* update state */
fnc = DTS_GETFNC (uptr->STATE);                         /* set variables */
dir = DTS_GETMOT (uptr->STATE) & DTS_DIR;
unum = (int32) (uptr - dt_dev.units);
if (oldpos == uptr->pos)                                /* bump pos */
    uptr->pos = uptr->pos + (dir? -1: 1);
blk = DT_LIN2BL (uptr->pos, uptr);

if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) {              /* wrong ez? */
    dt_seterr (uptr, DTB_END);                          /* set ez flag, stop */
    return;
    }
sim_cancel (uptr);                                      /* cancel cur op */
dt_substate = DTO_SOB;                                  /* substate = block start */
switch (fnc) {                                          /* case function */

    case DTS_OFR:                                       /* off reel */
        if (dir)                                        /* rev? < start */
            newpos = -1000;
        else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */
        break;

    case FNC_MOVE:                                      /* move */
        dt_schedez (uptr, dir);                         /* sched end zone */
        if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n",
            unum, (dir? "backward": "forward"));
        return;                                         /* done */

    case FNC_SRCH:                                      /* search */
        if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)?
            DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE;
        else newpos = DT_BLK2LN ((DT_QREZ (uptr)?
            0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1);
        if (DEBUG_PRI (dt_dev, LOG_MS))
            fprintf (sim_deb, ">>DT%d: searching %s]\n",
                     unum, (dir? "backward": "forward"));
        break;

    case FNC_WRIT:                                      /* write */
    case FNC_READ:                                      /* read */
    case FNC_RALL:                                      /* read all */
    case FNC_WALL:                                      /* write all */
        if (DT_QEZ (uptr)) {                            /* in "ok" end zone? */
            if (dir)
                newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE;
            else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1);
            break;
            }
        relpos = DT_LIN2OF (uptr->pos, uptr);           /* cur pos in blk */
        if ((relpos >= DT_HTLIN) &&                     /* in data zone? */
            (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
            dt_seterr (uptr, DTB_SEL);
            return;
            }
        if (dir)
            newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))?
                blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE;
        else newpos = DT_BLK2LN (((relpos < DT_HTLIN)?
                blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1);
        break;

    default:
        dt_seterr (uptr, DTB_SEL);                      /* bad state */
        return;
        }

sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);
return;
}

/* Update DECtape position

   DECtape motion is modeled as a constant velocity, with linear
   acceleration and deceleration.  The motion equations are as follows:

        t       =       time since operation started
        tmax    =       time for operation (accel, decel only)
        v       =       at speed velocity in lines (= 1/dt_ltime)

   Then:
        at speed dist = t * v
        accel dist = (t^2 * v) / (2 * tmax)
        decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)

   This routine uses the relative (integer) time, rather than the absolute
   (floating point) time, to allow save and restore of the start times.
*/

t_bool dt_setpos (UNIT *uptr)
{
uint32 new_time, ut, ulin, udelt;
int32 mot = DTS_GETMOT (uptr->STATE);
int32 unum, delta;

new_time = sim_grtime ();                               /* current time */
ut = new_time - uptr->LASTT;                            /* elapsed time */
if (ut == 0)                                            /* no time gone? exit */
    return FALSE;
uptr->LASTT = new_time;                                 /* update last time */
switch (mot & ~DTS_DIR) {                               /* case on motion */

    case DTS_STOP:                                      /* stop */
        delta = 0;
        break;

    case DTS_DECF:                                      /* slowing */
        ulin = ut / (uint32) dt_ltime;
        udelt = dt_dctime / dt_ltime;
        delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);
        break;

    case DTS_ACCF:                                      /* accelerating */
        ulin = ut / (uint32) dt_ltime;
        udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime;
        delta = (ulin * ulin) / (2 * udelt);
        break;

    case DTS_ATSF:                                      /* at speed */
        delta = ut / (uint32) dt_ltime;
        break;
        }

if (mot & DTS_DIR)                                      /* update pos */
    uptr->pos = uptr->pos - delta;
else uptr->pos = uptr->pos + delta;
if (((int32) uptr->pos < 0) ||
    ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) {
    detach_unit (uptr);                                 /* off reel? */
    uptr->STATE = uptr->pos = 0;
    unum = (int32) (uptr - dt_dev.units);
    if (unum == DTA_GETUNIT (dtsa))                     /* if selected, */
        dt_seterr (uptr, DTB_SEL);                      /* error */
    return TRUE;
    }
return FALSE;
}

/* Unit service

   Unit must be attached, detach cancels operation
*/

t_stat dt_svc (UNIT *uptr)
{
int32 mot = DTS_GETMOT (uptr->STATE);
int32 dir = mot & DTS_DIR;
int32 fnc = DTS_GETFNC (uptr->STATE);
int16 *fbuf = (int16 *) uptr->filebuf;
int32 unum = uptr - dt_dev.units;
int32 blk, wrd, ma, relpos, dat;
uint32 ba;

/* Motion cases

   Decelerating - if next state != stopped, must be accel reverse
   Accelerating - next state must be @speed, schedule function
   At speed - do functional processing
*/

switch (mot) {

    case DTS_DECF: case DTS_DECR:                       /* decelerating */
        if (dt_setpos (uptr))                           /* upd pos; off reel? */
            return IORETURN (dt_stopoffr, STOP_DTOFF);
        uptr->STATE = DTS_NXTSTA (uptr->STATE);         /* advance state */
        if (uptr->STATE)                                /* not stopped? */
            sim_activate (uptr, dt_dctime - (dt_dctime >> 2));  /* must be reversing */
        return SCPE_OK;

    case DTS_ACCF: case DTS_ACCR:                       /* accelerating */
        dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE));     /* adv state, sched */
        return SCPE_OK;

    case DTS_ATSF: case DTS_ATSR:                       /* at speed */
        break;                                          /* check function */

    default:                                            /* other */
        dt_seterr (uptr, DTB_SEL);                      /* state error */
        return SCPE_OK;
        }

/* Functional cases

   Move - must be at end zone
   Search - transfer block number, schedule next block
   Off reel - detach unit (it must be deselected)
*/

if (dt_setpos (uptr))                                   /* upd pos; off reel? */
    return IORETURN (dt_stopoffr, STOP_DTOFF);
if (DT_QEZ (uptr)) {                                    /* in end zone? */
    dt_seterr (uptr, DTB_END);                          /* end zone error */
    return SCPE_OK;
    }
blk = DT_LIN2BL (uptr->pos, uptr);                      /* get block # */
switch (fnc) {                                          /* at speed, check fnc */

    case FNC_MOVE:                                      /* move */
        dt_seterr (uptr, DTB_END);                      /* end zone error */
        return SCPE_OK;

    case FNC_SRCH:                                      /* search */
        if (dtsb & DTB_DTF) {                           /* DTF set? */
            dt_seterr (uptr, DTB_TIM);                  /* timing error */
            return SCPE_OK;
            }
        sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */
        M[DT_WC] = (M[DT_WC] + 1) & 07777;              /* incr word cnt */
        ma = DTB_GETMEX (dtsb) | M[DT_CA];              /* get mem addr */
        if (MEM_ADDR_OK (ma))                           /* store block # */
            M[ma] = blk & 07777;
        if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
            dtsb = dtsb | DTB_DTF;                      /* set DTF */
        break;

    case DTS_OFR:                                       /* off reel */
        detach_unit (uptr);                             /* must be deselected */
        uptr->STATE = uptr->pos = 0;                    /* no visible action */
        break;

/* Read has four subcases

   Start of block, not wc ovf - check that DTF is clear, otherwise normal
   Normal - increment MA, WC, copy word from tape to memory
        if read dir != write dir, bits must be scrambled
        if wc overflow, next state is wc overflow
        if end of block, possibly set DTF, next state is start of block
   Wc ovf, not start of block - 
        if end of block, possibly set DTF, next state is start of block
   Wc ovf, start of block - if end of block reached, timing error,
        otherwise, continue to next word
*/

    case FNC_READ:                                      /* read */
        wrd = DT_LIN2WD (uptr->pos, uptr);              /* get word # */
        switch (dt_substate) {                          /* case on substate */

        case DTO_SOB:                                   /* start of block */
            if (dtsb & DTB_DTF) {                       /* DTF set? */
                dt_seterr (uptr, DTB_TIM);              /* timing error */
                return SCPE_OK;
                }
            if (DEBUG_PRI (dt_dev, LOG_RW) ||
               (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))
                fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n",
                    unum, blk, (dir? "backward": "forward"),
                    ((dtsa & DTA_MODE)? " continuous": " "));
            dt_substate = 0;                            /* fall through */
        case 0:                                         /* normal read */
            M[DT_WC] = (M[DT_WC] + 1) & 07777;          /* incr WC, CA */
            M[DT_CA] = (M[DT_CA] + 1) & 07777;
            ma = DTB_GETMEX (dtsb) | M[DT_CA];          /* get mem addr */
            ba = (blk * DTU_BSIZE (uptr)) + wrd;        /* buffer ptr */
            dat = fbuf[ba];                             /* get tape word */
            if (dir)                                    /* rev? comp obv */
                dat = dt_comobv (dat);
            if (MEM_ADDR_OK (ma))                       /* mem addr legal? */
                M[ma] = dat;
            if (M[DT_WC] == 0)                          /* wc ovf? */
                dt_substate = DTO_WCO;
        case DTO_WCO:                                   /* wc ovf, not sob */
            if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1))  /* not last? */
                sim_activate (uptr, DT_WSIZE * dt_ltime);
            else {
                dt_substate = dt_substate | DTO_SOB;
                sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);
                if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
                    dtsb = dtsb | DTB_DTF;              /* set DTF */
                }
            break;                      

        case DTO_WCO | DTO_SOB:                         /* next block */        
            if (wrd == (dir? 0: DTU_BSIZE (uptr)))      /* end of block? */
                dt_seterr (uptr, DTB_TIM);              /* timing error */
            else sim_activate (uptr, DT_WSIZE * dt_ltime);
            break;
            }

        break;

/* Write has four subcases

   Start of block, not wc ovf - check that DTF is clear, set block direction
   Normal - increment MA, WC, copy word from memory to tape
        if wc overflow, next state is wc overflow
        if end of block, possibly set DTF, next state is start of block
   Wc ovf, not start of block -
        copy 0 to tape
        if end of block, possibly set DTF, next state is start of block
   Wc ovf, start of block - schedule end zone
*/

    case FNC_WRIT:                                      /* write */
        wrd = DT_LIN2WD (uptr->pos, uptr);              /* get word # */
        switch (dt_substate) {                          /* case on substate */

        case DTO_SOB:                                   /* start block */
            if (dtsb & DTB_DTF) {                       /* DTF set? */
                dt_seterr (uptr, DTB_TIM);              /* timing error */
                return SCPE_OK;
                }
            if (DEBUG_PRI (dt_dev, LOG_RW) ||
               (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))
                fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk,
                    (dir? "backward": "forward"),
                    ((dtsa & DTA_MODE)? " continuous": " "));
            dt_substate = 0;                            /* fall through */
        case 0:                                         /* normal write */
            M[DT_WC] = (M[DT_WC] + 1) & 07777;          /* incr WC, CA */
            M[DT_CA] = (M[DT_CA] + 1) & 07777;
        case DTO_WCO:                                   /* wc ovflo */
            ma = DTB_GETMEX (dtsb) | M[DT_CA];          /* get mem addr */
            ba = (blk * DTU_BSIZE (uptr)) + wrd;        /* buffer ptr */
            dat = dt_substate? 0: M[ma];                /* get word */
            if (dir)                                    /* rev? comp obv */
                dat = dt_comobv (dat);
            fbuf[ba] = dat;                             /* write word */
            uptr->WRITTEN = TRUE;
            if (ba >= uptr->hwmark)
                uptr->hwmark = ba + 1;
            if (M[DT_WC] == 0)
                dt_substate = DTO_WCO;
            if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1))  /* not last? */
                sim_activate (uptr, DT_WSIZE * dt_ltime);
            else {
                dt_substate = dt_substate | DTO_SOB;
                sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);
                if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
                    dtsb = dtsb | DTB_DTF;              /* set DTF */
                }
            break;                      

        case DTO_WCO | DTO_SOB:                         /* all done */
            dt_schedez (uptr, dir);                     /* sched end zone */
            break;
            }

        break;

/* Read all has two subcases

        Not word count overflow - increment MA, WC, copy word from tape to memory
        Word count overflow - schedule end zone
*/

    case FNC_RALL:
        switch (dt_substate) {                          /* case on substate */

        case 0: case DTO_SOB:                           /* read in progress */
            if (dtsb & DTB_DTF) {                       /* DTF set? */
                dt_seterr (uptr, DTB_TIM);              /* timing error */
                return SCPE_OK;
                }
            relpos = DT_LIN2OF (uptr->pos, uptr);       /* cur pos in blk */
            M[DT_WC] = (M[DT_WC] + 1) & 07777;          /* incr WC, CA */
            M[DT_CA] = (M[DT_CA] + 1) & 07777;
            ma = DTB_GETMEX (dtsb) | M[DT_CA];          /* get mem addr */
            if ((relpos >= DT_HTLIN) &&                 /* in data zone? */
                (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
                wrd = DT_LIN2WD (uptr->pos, uptr);
                ba = (blk * DTU_BSIZE (uptr)) + wrd;
                dat = fbuf[ba];                         /* get tape word */
                if (dir)                                /* rev? comp obv */
                    dat = dt_comobv (dat);
                }
            else dat = dt_gethdr (uptr, blk, relpos, dir);      /* get hdr */
            sim_activate (uptr, DT_WSIZE * dt_ltime);
            if (MEM_ADDR_OK (ma))                       /* mem addr legal? */
                M[ma] = dat;
            if (M[DT_WC] == 0)
                dt_substate = DTO_WCO;
            if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
                dtsb = dtsb | DTB_DTF;                  /* set DTF */
            break;

        case DTO_WCO: case DTO_WCO | DTO_SOB:           /* all done */
            dt_schedez (uptr, dir);                     /* sched end zone */
            break;
            }                                           /* end case substate */

        break;

/* Write all has two subcases

        Not word count overflow - increment MA, WC, copy word from memory to tape
        Word count overflow - schedule end zone
*/

    case FNC_WALL:
        switch (dt_substate) {                          /* case on substate */

        case 0: case DTO_SOB:                           /* read in progress */
            if (dtsb & DTB_DTF) {                       /* DTF set? */
                dt_seterr (uptr, DTB_TIM);              /* timing error */
                return SCPE_OK;
                }
            relpos = DT_LIN2OF (uptr->pos, uptr);       /* cur pos in blk */
            M[DT_WC] = (M[DT_WC] + 1) & 07777;          /* incr WC, CA */
            M[DT_CA] = (M[DT_CA] + 1) & 07777;
            ma = DTB_GETMEX (dtsb) | M[DT_CA];          /* get mem addr */
            if ((relpos >= DT_HTLIN) &&                 /* in data zone? */
                (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
                dat = M[ma];                            /* get mem word */
                if (dir)
                    dat = dt_comobv (dat);
                wrd = DT_LIN2WD (uptr->pos, uptr);
                ba = (blk * DTU_BSIZE (uptr)) + wrd;
                fbuf[ba] = dat;                         /* write word */
                if (ba >= uptr->hwmark)
                    uptr->hwmark = ba + 1;
                }
                                                        /* ignore hdr */
            sim_activate (uptr, DT_WSIZE * dt_ltime);
            if (M[DT_WC] == 0)
                dt_substate = DTO_WCO;
            if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
                dtsb = dtsb | DTB_DTF;                  /* set DTF */
                break;

        case DTO_WCO: case DTO_WCO | DTO_SOB:           /* all done */
            dt_schedez (uptr, dir);                     /* sched end zone */
            break;
            }                                           /* end case substate */
        break;

    default:
        dt_seterr (uptr, DTB_SEL);                      /* impossible state */
        break;
        }

DT_UPDINT;                                              /* update interrupts */
return SCPE_OK;
}

/* Reading the header is complicated, because 18b words are being parsed
   out 12b at a time.  The sequence of word numbers is directionally
   sensitive

                Forward                         Reverse
        Word    Word    Content         Word    Word    Content
        (abs)   (rel)                   (abs)   (rel)

        137     8       fwd csm'00      6       6       rev csm'00
        138     9       0000            5       5       0000
        139     10      0000            4       4       0000
        140     11      0000            3       3       0000
        141     12      00'lo rev blk   2       2       00'lo fwd blk
        142     13      hi rev blk      1       1       hi fwd blk
        143     14      0000            0       0       0000
        0       0       0000            143     14      0000
        1       1       0000            142     13      0000
        2       2       hi fwd blk      141     12      hi rev blk
        3       3       lo fwd blk'00   140     11      lo rev blk'00
        4       4       0000            139     10      0000
        5       5       0000            138     9       0000
        6       6       0000            137     8       0000
        7       7       rev csm         136     7       00'fwd csm
*/

int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir)
{
if (relpos >= DT_HTLIN)
    relpos = relpos - (DT_WSIZE * DTU_BSIZE (uptr));
if (dir) {                                              /* reverse */
    switch (relpos / DT_WSIZE) {
    case 6:                                             /* rev csm */
        return 077;
    case 2:                                             /* lo fwd blk */
        return dt_comobv ((blk & 077) << 6);
    case 1:                                             /* hi fwd blk */
        return dt_comobv (blk >> 6);
    case 12:                                            /* hi rev blk */
        return (blk >> 6) & 07777;
    case 11:                                            /* lo rev blk */
        return ((blk & 077) << 6);
    case 7:                                             /* fwd csum */
        return (dt_comobv (dt_csum (uptr, blk)) << 6);
    default:                                            /* others */
        return 07777;
        }
    }
else {                                                  /* forward */
    switch (relpos / DT_WSIZE) {
    case 8:                                             /* fwd csum */
        return (dt_csum (uptr, blk) << 6);
    case 12:                                            /* lo rev blk */
        return dt_comobv ((blk & 077) << 6);
    case 13:                                            /* hi rev blk */
        return dt_comobv (blk >> 6);
    case 2:                                             /* hi fwd blk */
        return ((blk >> 6) & 07777);
    case 3:                                             /* lo fwd blk */
        return ((blk & 077) << 6);
    case 7:                                             /* rev csum */
        return 077;
    default:                                            /* others */
        break;
        }
    }
return 0;
}

/* Utility routines */

/* Set error flag */

void dt_seterr (UNIT *uptr, int32 e)
{
int32 mot = DTS_GETMOT (uptr->STATE);

dtsa = dtsa & ~DTA_STSTP;                               /* clear go */
dtsb = dtsb | DTB_ERF | e;                              /* set error flag */
if (mot >= DTS_ACCF) {                                  /* ~stopped or stopping? */
    sim_cancel (uptr);                                  /* cancel activity */
    if (dt_setpos (uptr))                               /* update position */
        return;
    sim_activate (uptr, dt_dctime);                     /* sched decel */
    DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0);         /* state = decel */
    }
DT_UPDINT;
return;
}

/* Schedule end zone */

void dt_schedez (UNIT *uptr, int32 dir)
{
int32 newpos;

if (dir)                                                /* rev? rev ez */
    newpos = DT_EZLIN - DT_WSIZE;
else newpos = DTU_FWDEZ (uptr) + DT_WSIZE;              /* fwd? fwd ez */
sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);
return;
}

/* Complement obverse routine */

int32 dt_comobv (int32 dat)
{
dat = dat ^ 07777;                                      /* compl obverse */
dat = ((dat >> 9) & 07) | ((dat >> 3) & 070) |
    ((dat & 070) << 3) | ((dat & 07) << 9);
return dat;
}

/* Checksum routine */

int32 dt_csum (UNIT *uptr, int32 blk)
{
int16 *fbuf = (int16 *) uptr->filebuf;
int32 ba = blk * DTU_BSIZE (uptr);
int32 i, csum, wrd;

csum = 077;                                             /* init csum */
for (i = 0; i < DTU_BSIZE (uptr); i++) {                /* loop thru buf */
    wrd = fbuf[ba + i] ^ 07777;                         /* get ~word */
    csum = csum ^ (wrd >> 6) ^ wrd;
    }
return (csum & 077);
}

/* Reset routine */

t_stat dt_reset (DEVICE *dptr)
{
int32 i, prev_mot;
UNIT *uptr;

for (i = 0; i < DT_NUMDR; i++) {                        /* stop all activity */
    uptr = dt_dev.units + i;
    if (sim_is_running) {                               /* CAF? */
        prev_mot = DTS_GETMOT (uptr->STATE);            /* get motion */
        if ((prev_mot & ~DTS_DIR) > DTS_DECF) {         /* accel or spd? */
            if (dt_setpos (uptr))                       /* update pos */
                continue;
            sim_cancel (uptr);
            sim_activate (uptr, dt_dctime);             /* sched decel */
            DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0);
            }
        }
    else {
        sim_cancel (uptr);                              /* sim reset */
        uptr->STATE = 0;  
        uptr->LASTT = sim_grtime ();
        }
    }
dtsa = dtsb = 0;                                        /* clear status */
DT_UPDINT;                                              /* reset interrupt */
return SCPE_OK;
}

/* Bootstrap routine 

   This is actually the 4K disk monitor bootstrap, which also
   works with OS/8.  The reverse is not true - the OS/8 bootstrap
   doesn't work with the disk monitor.
*/

#define BOOT_START      0200
#define BOOT_LEN        (sizeof (boot_rom) / sizeof (int16))

static const uint16 boot_rom[] = {
    07600,                      /* 200, CLA CLL */
    01216,                      /*      TAD MVB         ; move back */
    04210,                      /*      JMS DO          ; action */
    01217,                      /*      TAD K7577       ; addr */
    03620,                      /*      DCA I CA */
    01222,                      /*      TAD RDF         ; read fwd */
    04210,                      /*      JMS DO          ; action */
    05600,                      /*      JMP I 200       ; enter boot */
    00000,                      /* DO,  0 */
    06766,                      /*      DTCA!DTXA       ; start tape */
    03621,                      /*      DCA I WC        ; clear wc */
    06771,                      /*      DTSF            ; wait */
    05213,                      /*      JMP .-1 */
    05610,                      /*      JMP I DO */
    00600,                      /* MVB, 0600 */
    07577,                      /* K7577, 7757 */
    07755,                      /* CA,  7755 */
    07754,                      /* WC,  7754 */
    00220                       /* RF,  0220 */
    };

t_stat dt_boot (int32 unitno, DEVICE *dptr)
{
size_t i;

if (unitno)                                             /* only unit 0 */
    return SCPE_ARG;
if (dt_dib.dev != DEV_DTA)                              /* only std devno */
    return STOP_NOTSTD;
dt_unit[unitno].pos = DT_EZLIN;
for (i = 0; i < BOOT_LEN; i++)
    M[BOOT_START + i] = boot_rom[i];
cpu_set_bootpc (BOOT_START);
return SCPE_OK;
}

/* Attach routine

   Determine 12b, 16b, or 18b/36b format
   Allocate buffer
   If 16b or 18b, read 16b or 18b format and convert to 12b in buffer
   If 12b, read data into buffer
*/

t_stat dt_attach (UNIT *uptr, CONST char *cptr)
{
uint32 pdp18b[D18_NBSIZE];
uint16 pdp11b[D18_NBSIZE], *fbuf;
int32 i, k;
int32 u = uptr - dt_dev.units;
t_stat r;
uint32 ba, sz;

r = attach_unit (uptr, cptr);                           /* attach */
if (r != SCPE_OK) return r;                             /* fail? */
if ((sim_switches & SIM_SW_REST) == 0) {                /* not from rest? */
    uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;
    if (sim_switches & SWMASK ('F'))                    /* att 18b? */
        uptr->flags = uptr->flags & ~UNIT_8FMT;
    else if (sim_switches & SWMASK ('S'))               /* att 16b? */
        uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
    else if (!(sim_switches & SWMASK ('A')) &&          /* autosize? */
        (sz = sim_fsize (uptr->fileref))) {
        if (sz == D11_FILSIZ)
            uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
        else if (sz > D8_FILSIZ)
            uptr->flags = uptr->flags & ~UNIT_8FMT;
        }
    }
uptr->capac = DTU_CAPAC (uptr);                         /* set capacity */
uptr->filebuf = calloc (uptr->capac, sizeof (uint16));
if (uptr->filebuf == NULL) {                            /* can't alloc? */
    detach_unit (uptr);
    return SCPE_MEM;
    }
fbuf = (uint16 *) uptr->filebuf;                        /* file buffer */
sim_printf ("%s%d: ", sim_dname (&dt_dev), u);
if (uptr->flags & UNIT_8FMT)
    sim_printf ("12b format");
else if (uptr->flags & UNIT_11FMT)
    sim_printf ("16b format");
else sim_printf ("18b/36b format");
sim_printf (", buffering file in memory\n");
uptr->io_flush = dt_flush;
if (uptr->flags & UNIT_8FMT)                            /* 12b? */
    uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16),
            uptr->capac, uptr->fileref);
else {                                                  /* 16b/18b */
    for (ba = 0; ba < uptr->capac; ) {                  /* loop thru file */
        if (uptr->flags & UNIT_11FMT) {
            k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref);
            for (i = 0; i < k; i++)
                pdp18b[i] = pdp11b[i];
            }
        else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref);
        if (k == 0)
            break;
        for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0;
        for (k = 0; k < D18_NBSIZE; k = k + 2) {        /* loop thru blk */
            fbuf[ba] = (pdp18b[k] >> 6) & 07777;
            fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) |
                ((pdp18b[k + 1] >> 12) & 077);
            fbuf[ba + 2] = pdp18b[k + 1] & 07777;
            ba = ba + 3;
            }                                           /* end blk loop */
        }                                               /* end file loop */
    uptr->hwmark = ba;
    }                                                   /* end else */
uptr->flags = uptr->flags | UNIT_BUF;                   /* set buf flag */
uptr->pos = DT_EZLIN;                                   /* beyond leader */
uptr->LASTT = sim_grtime ();                            /* last pos update */
return SCPE_OK;
}

/* Detach routine

   Cancel in progress operation
   If 12b, write buffer to file
   If 16b or 18b, convert 12b buffer to 16b or 18b and write to file
   Deallocate buffer
*/
void dt_flush (UNIT* uptr)
{
uint32 pdp18b[D18_NBSIZE];
uint16 pdp11b[D18_NBSIZE], *fbuf;
int32 i, k;
uint32 ba;

if (uptr->WRITTEN && uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) {    /* any data? */
    rewind (uptr->fileref);                             /* start of file */
    fbuf = (uint16 *) uptr->filebuf;                    /* file buffer */
    if (uptr->flags & UNIT_8FMT)                        /* PDP8? */
        fxwrite (uptr->filebuf, sizeof (uint16),        /* write file */
            uptr->hwmark, uptr->fileref);
    else {                                              /* 16b/18b */
        for (ba = 0; ba < uptr->hwmark; ) {             /* loop thru buf */
            for (k = 0; k < D18_NBSIZE; k = k + 2) {
                pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) |
                    ((uint32) (fbuf[ba + 1] >> 6) & 077);
                pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) |
                    ((uint32) (fbuf[ba + 2] & 07777));
                ba = ba + 3;
                }                                       /* end loop blk */
            if (uptr->flags & UNIT_11FMT) {             /* 16b? */
                for (i = 0; i < D18_NBSIZE; i++)
                    pdp11b[i] = pdp18b[i];
                fxwrite (pdp11b, sizeof (uint16),
                    D18_NBSIZE, uptr->fileref);
                }
            else fxwrite (pdp18b, sizeof (uint32),
                D18_NBSIZE, uptr->fileref);
            }                                           /* end loop buf */
        }                                               /* end else */
    if (ferror (uptr->fileref))
        sim_perror ("I/O error");
    }
uptr->WRITTEN = FALSE;                                  /* no longer dirty */
}

t_stat dt_detach (UNIT* uptr)
{
int u = (int)(uptr - dt_dev.units);

if (!(uptr->flags & UNIT_ATT))                          /* attached? */
    return SCPE_OK;
if (sim_is_active (uptr)) {
    sim_cancel (uptr);
    if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) {
        dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF;
        DT_UPDINT;
        }
    uptr->STATE = uptr->pos = 0;
    }
if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) {    /* any data? */
    sim_printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u);
    dt_flush (uptr);
    }                                                   /* end if hwmark */
free (uptr->filebuf);                                   /* release buf */
uptr->flags = uptr->flags & ~UNIT_BUF;                  /* clear buf flag */
uptr->filebuf = NULL;                                   /* clear buf ptr */
uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;  /* default fmt */
uptr->capac = DT_CAPAC;                                 /* default size */
return detach_unit (uptr);
}
Added src/PDP8/pdp8_fpp.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_fpp.c: PDP-8 floating point processor (FPP8A)

   Copyright (c) 2007-2011, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   fpp          FPP8A floating point processor

   03-Jan-10    RMS     Initialized variables statically, for VMS compiler
   19-Apr-09    RHM     FPICL does not clear all command and status reg bits
                            modify fpp_reset to conform with FPP
   27-Mar-09    RHM     Fixed handling of Underflow fix (zero FAC on underflow)
                        Implemented FPP division and multiplication algorithms
                        FPP behavior on traps - FEXIT does not update APT
                        Follow FPP settings for OPADD 
                        Correct detection of DP add/sub overflow
                        Detect and handle add/sub overshift
                        Single-step mode made consistent with FPP
                        Write calculation results prior to traps
   24-Mar-09    RMS     Many fixes from Rick Murphy:
                        Fix calculation of ATX shift amount
                        Added missing () to read, write XR macros
                        Fixed indirect address calculation
                        Fixed == written as = in normalization
                        Fixed off-by-one count bug in multiplication
                        Removed extraneous ; in divide
                        Fixed direction of compare in divide
                        Fixed count direction bug in alignment

   Floating point formats:

    00 01 02 03 04 05 06 07 08 09 10 11
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          hi integer            | : double precision
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |             lo integer            |
   +--+--+--+--+--+--+--+--+--+--+--+--+

    00 01 02 03 04 05 06 07 08 09 10 11
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          exponent              | : floating point
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          hi fraction           |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |             lo fraction           |
   +--+--+--+--+--+--+--+--+--+--+--+--+


    00 01 02 03 04 05 06 07 08 09 10 11
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          exponent              | : extended precision
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          hi fraction           |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |            next fraction          |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |            next fraction          |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |            next fraction          |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |             lo fraction           |
   +--+--+--+--+--+--+--+--+--+--+--+--+

   Exponents are 2's complement, as are fractions.  Normalized numbers have
   the form:

   0.0...0
   0.<non-zero>
   1.<non-zero>
   1.1...0

   Note that 1.0...0 is normalized but considered illegal, since it cannot
   be represented as a positive number. When a result is normalized, 1.0...0
   is converted to 1.1...0 with exp+1.
*/

#include "pdp8_defs.h"

extern int32 int_req;
extern uint16 M[];
extern int32 stop_inst;
extern UNIT cpu_unit;

#define SEXT12(x)       (((x) & 04000)? (x) | ~07777: (x) & 03777)

/* Index registers are in memory */

#define fpp_read_xr(xr) fpp_read (fpp_xra + (xr))
#define fpp_write_xr(xr,d) fpp_write (fpp_xra + (xr), d)

/* Command register */

#define FPC_DP          04000                           /* integer double */
#define FPC_UNFX        02000                           /* exit on fl undf */
#define FPC_FIXF        01000                           /* lock mem field */
#define FPC_IE          00400                           /* int enable */
#define FPC_V_FAST      4                               /* startup bits */
#define FPC_M_FAST      017
#define FPC_LOCK        00010                           /* lockout */
#define FPC_V_APTF      0
#define FPC_M_APTF      07                              /* apta field */
#define FPC_STA         (FPC_DP|FPC_LOCK)
#define FPC_GETFAST(x)  (((x) >> FPC_V_FAST) & FPC_M_FAST)
#define FPC_GETAPTF(x)  (((x) >> FPC_V_APTF) & FPC_M_APTF)

/* Status register */

#define FPS_DP          (FPC_DP)                        /* integer double */
#define FPS_TRPX        02000                           /* trap exit */
#define FPS_HLTX        01000                           /* halt exit */
#define FPS_DVZX        00400                           /* div zero exit */
#define FPS_IOVX        00200                           /* int ovf exit */
#define FPS_FOVX        00100                           /* flt ovf exit */
#define FPS_UNF         00040                           /* underflow */
#define FPS_XXXM        00020                           /* FADDM/FMULM */
#define FPS_LOCK        (FPC_LOCK)                      /* lockout */
#define FPS_EP          00004                           /* ext prec */
#define FPS_PAUSE       00002                           /* paused */
#define FPS_RUN         00001                           /* running */

/* Floating point number: 3-6 words */

#define FPN_FRSIGN      04000
#define FPN_NFR_FP      2                               /* std precision */
#define FPN_NFR_EP      5                               /* ext precision */
#define FPN_NFR_MDS     6                               /* mul/div precision */
#define EXACT           (uint32)((fpp_sta & FPS_EP)? FPN_NFR_EP: FPN_NFR_FP)
#define EXTEND          ((uint32) FPN_NFR_EP)

typedef struct {
    int32       exp;
    uint32      fr[FPN_NFR_MDS+1];
    } FPN;

uint32 fpp_apta = 0;                                    /* APT pointer */
uint32 fpp_aptsvf = 0;                                  /* APT saved field */
uint32 fpp_opa = 0;                                     /* operand pointer */
uint32 fpp_fpc = 0;                                     /* FP PC */
uint32 fpp_bra = 0;                                     /* base reg pointer */
uint32 fpp_xra = 0;                                     /* indx reg pointer */
uint32 fpp_cmd = 0;                                     /* command */
uint32 fpp_sta = 0;                                     /* status */
uint32 fpp_flag = 0;                                    /* flag */
FPN fpp_ac;                                             /* FAC */
uint32 fpp_ssf = 0;                                     /* single-step flag */
uint32 fpp_last_lockbit = 0;                            /* last lockbit */

static FPN fpp_zero = { 0, { 0, 0, 0, 0, 0 } };
static FPN fpp_one = { 1, { 02000, 0, 0, 0, 0 } };

int32 fpp55 (int32 IR, int32 AC);
int32 fpp56 (int32 IR, int32 AC);
void fpp_load_apt (uint32 apta);
void fpp_dump_apt (uint32 apta, uint32 sta);
uint32 fpp_1wd_dir (uint32 ir);
uint32 fpp_2wd_dir (uint32 ir);
uint32 fpp_indir (uint32 ir);
uint32 fpp_ad15 (uint32 hi);
uint32 fpp_adxr (uint32 ir, uint32 base_ad);
void fpp_add (FPN *a, FPN *b, uint32 sub);
void fpp_mul (FPN *a, FPN *b);
void fpp_div (FPN *a, FPN *b);
t_bool fpp_imul (FPN *a, FPN *b);
uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b, uint32 cnt);
void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b, uint32 cnt);
void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b, t_bool fix);
t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b);
uint32 fpp_fr_neg (uint32 *a, uint32 cnt);
int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt);
int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt);
uint32 fpp_fr_abs (uint32 *a, uint32 *b, uint32 cnt);
void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt);
void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt);
void fpp_fr_lsh12 (uint32 *a, uint32 cnt);
void fpp_fr_lsh1 (uint32 *a, uint32 cnt);
void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt);
void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt);
t_bool fpp_cond_met (uint32 cond);
t_bool fpp_norm (FPN *a, uint32 cnt);
void fpp_round (FPN *a);
t_bool fpp_test_xp (FPN *a);
void fpp_copy (FPN *a, FPN *b);
void fpp_zcopy (FPN *a, FPN *b);
void fpp_read_op (uint32 ea, FPN *a);
void fpp_write_op (uint32 ea, FPN *a);
uint32 fpp_read (uint32 ea);
void fpp_write (uint32 ea, uint32 val);
uint32 apt_read (uint32 ea);
void apt_write (uint32 ea, uint32 val);
t_stat fpp_svc (UNIT *uptr);
t_stat fpp_reset (DEVICE *dptr);

/* FPP data structures

   fpp_dev      FPP device descriptor
   fpp_unit     FPP unit descriptor
   fpp_reg      FPP register list
*/

DIB fpp_dib = { DEV_FPP, 2, { &fpp55, &fpp56 } };

UNIT fpp_unit = { UDATA (&fpp_svc, 0, 0) };

REG fpp_reg[] = {
    { ORDATAD (FPACE, fpp_ac.exp, 12, "floating accumulator") },
    { ORDATAD (FPAC0, fpp_ac.fr[0], 12, "first mantissa") },
    { ORDATAD (FPAC1, fpp_ac.fr[1], 12, "second mantissa") },
    { ORDATAD (FPAC2, fpp_ac.fr[2], 12, "third mantissa") },
    { ORDATAD (FPAC3, fpp_ac.fr[3], 12, "fourth mantissa") },
    { ORDATAD (FPAC4, fpp_ac.fr[4], 12, "fifth mantissa") },
    { ORDATAD (CMD, fpp_cmd, 12, "FPP command register") },
    { ORDATAD (STA, fpp_sta, 12, "status register") },
    { ORDATAD (APTA, fpp_apta, 15, "active parameter table (APT) pointer") },
    { GRDATAD (APTSVF, fpp_aptsvf, 8, 3, 12, "APT field") },
    { ORDATAD (FPC, fpp_fpc, 15, "floating program counter") },
    { ORDATAD (BRA, fpp_bra, 15, "base register") },
    { ORDATAD (XRA, fpp_xra, 15, "pointer to index register 0") },
    { ORDATAD (OPA, fpp_opa, 15, "operand address register") },
    { ORDATAD (SSF, fpp_ssf, 12, "single step flag") },
    { ORDATAD (LASTLOCK, fpp_last_lockbit, 12, "lockout from FPCOM") },
    { FLDATAD (FLAG, fpp_flag, 0, "done flag") },
    { NULL }
    };

DEVICE fpp_dev = {
    "FPP", &fpp_unit, fpp_reg, NULL,
    1, 10, 31, 1, 8, 8,
    NULL, NULL, &fpp_reset,
    NULL, NULL, NULL,
    &fpp_dib, DEV_DISABLE | DEV_DIS
    };

/* IOT routines */

int32 fpp55 (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 1:                                             /* FPINT */
        return (fpp_flag? IOT_SKP | AC: AC);            /* skip on flag */

    case 2:                                             /* FPICL */
        fpp_reset (&fpp_dev);                           /* reset device */
        break;

    case 3:                                             /* FPCOM */
        if (!fpp_flag && !(fpp_sta & FPS_RUN)) {        /* flag clr, !run? */
            fpp_cmd = AC;                               /* load cmd */
            fpp_last_lockbit = fpp_cmd & FPS_LOCK;      /* remember lock state */
            fpp_sta = (fpp_sta & ~FPC_STA) |            /* copy flags */
                (fpp_cmd & FPC_STA);                    /* to status */
            }
        break;

    case 4:                                             /* FPHLT */
        if (fpp_sta & FPS_RUN) {                        /* running? */
            if (fpp_sta & FPS_PAUSE)                    /* paused? */
                fpp_fpc = (fpp_fpc - 1) & ADDRMASK;     /* decr FPC */
            fpp_sta &= ~FPS_PAUSE;                      /* no longer paused */
            sim_cancel (&fpp_unit);                     /* stop execution */
            fpp_dump_apt (fpp_apta, FPS_HLTX);          /* dump APT */
            fpp_ssf = 1;                                /* assume sstep */
            }
        else if (!fpp_flag)
            fpp_ssf = 1;                                /* FPST sing steps */
        if (fpp_sta & FPS_DVZX)                         /* fix diag timing */
            fpp_sta |= FPS_HLTX;
        break;

    case 5:                                             /* FPST */
        if (!fpp_flag && !(fpp_sta & FPS_RUN)) {        /* flag clr, !run? */
            if (fpp_ssf)
                fpp_sta |= fpp_last_lockbit; 
            fpp_sta &= ~FPS_HLTX;                       /* Clear halted */
            fpp_apta = (FPC_GETAPTF (fpp_cmd) << 12) | AC;
            fpp_load_apt (fpp_apta);                    /* load APT */
            fpp_opa = fpp_fpc;
            sim_activate (&fpp_unit, 0);                /* start unit */
            return IOT_SKP | AC;
            }
        if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == (FPS_RUN|FPS_PAUSE)) {
            fpp_sta &= ~FPS_PAUSE;                      /* continue */
            sim_activate (&fpp_unit, 0);                /* start unit */
            return (IOT_SKP | AC);
            }
        break;

    case 6:                                             /* FPRST */
        return fpp_sta;

    case 7:                                             /* FPIST */
        if (fpp_flag) {                                 /* if flag set */
            uint32 old_sta = fpp_sta;
            fpp_flag = 0;                               /* clr flag, status */
            fpp_sta &= ~(FPS_DP|FPS_EP|FPS_TRPX|FPS_DVZX|FPS_IOVX|FPS_FOVX|FPS_UNF);
            int_req &= ~INT_FPP;                        /* clr int req */
            return IOT_SKP | old_sta;                   /* ret old status */
            }
        break;

    default:
        return (stop_inst << IOT_V_REASON) | AC;
        }                                               /* end switch */

return AC;
}

int32 fpp56 (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 7:                                             /* FPEP */
        if ((AC & 04000) && !(fpp_sta & FPS_RUN)) {     /* if AC0, not run, */
            fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP;     /* set ep */
            AC = 0;
            }
        break;

    default:
        return (stop_inst << IOT_V_REASON) | AC;
        }                                               /* end switch */

return AC;
}

/* Service routine */

t_stat fpp_svc (UNIT *uptr)
{
FPN x;
uint32 ir, op, op2, op3, ad, ea, wd;
uint32 i;
int32 sc;

fpp_ac.exp = SEXT12 (fpp_ac.exp);                       /* sext AC exp */
do {                                                    /* repeat */
    ir = fpp_read (fpp_fpc);                            /* get instr */
    fpp_fpc = (fpp_fpc + 1) & ADDRMASK;                 /* incr FP PC */
    op = (ir >> 7) & 037;                               /* get op+mode */
    op2 = (ir >> 3) & 017;                              /* get subop */
    op3 = ir & 07;                                      /* get field/xr */
    fpp_sta &= ~FPS_XXXM;                               /* not mem op */

    switch (op) {                                       /* case on op+mode */
    case 000:                                           /* operates */

        switch (op2) {                                  /* case on subop */
        case 000:                                       /* no-operands */
            switch (op3) {                              /* case on subsubop */

            case 0:                                     /* FEXIT */
            /* if already trapped, don't update APT, just update status */
                if (fpp_sta & (FPS_DVZX|FPS_IOVX|FPS_FOVX|FPS_UNF)) 
                    fpp_sta |= FPS_HLTX;
                else
                    fpp_dump_apt (fpp_apta, 0);
                break;

            case 1:                                     /* FPAUSE */
                fpp_sta |= FPS_PAUSE;
                break;

            case 2:                                     /* FCLA */
                fpp_copy (&fpp_ac, &fpp_zero);          /* clear FAC */
                break;

            case 3:                                     /* FNEG */
                fpp_fr_neg (fpp_ac.fr, EXACT);          /* do exact length */
                break;

            case 4:                                     /* FNORM */
                if (!(fpp_sta & FPS_DP)) {              /* fp or ep only */
                    fpp_copy (&x, &fpp_ac);             /* copy AC */
                    fpp_norm (&x, EXACT);               /* do exact length */
                    fpp_copy (&fpp_ac, &x);             /* copy back */
                    }
                break;

            case 5:                                     /* STARTF */
                if (fpp_sta & FPS_EP) {                 /* if ep, */
                    fpp_copy (&x, &fpp_ac);             /* copy AC */
                    fpp_round (&x);                     /* round */
                    fpp_copy (&fpp_ac, &x);             /* copy back */
                    }
                fpp_sta &= ~(FPS_DP|FPS_EP);
                break;

            case 6:                                     /* STARTD */
                fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP;
                break;

            case 7:                                     /* JAC */
                fpp_fpc = ((fpp_ac.fr[0] & 07) << 12) | fpp_ac.fr[1];
                break;
                }
            break;

        case 001:                                       /* ALN */
            if (op3 != 0) {                             /* if xr, */
                wd = fpp_read_xr (op3);                 /* use val */
                fpp_opa = fpp_xra + op3;
                }
            else wd = 027;                              /* else 23 */
            if (!(fpp_sta & FPS_DP)) {                  /* fp or ep? */
                sc = (SEXT12(wd) - fpp_ac.exp) & 07777; /* alignment */
                sc = SEXT12 (sc);
                fpp_ac.exp = SEXT12(wd);                /* new exp */
                }
            else sc = SEXT12 (wd);                      /* dp - simple cnt */
            if (sc < 0)                                 /* left? */
                fpp_fr_lshn (fpp_ac.fr,  -sc, EXACT);
            else fpp_fr_algn (fpp_ac.fr, sc, EXACT);
            if (fpp_fr_test (fpp_ac.fr, 0, EXACT) == 0) /* zero? */
                fpp_ac.exp = 0;                         /* clean exp */
            break;

        case 002:                                       /* ATX */
            if (fpp_sta & FPS_DP)                       /* dp? */
                fpp_write_xr (op3, fpp_ac.fr[1]);       /* xr<-FAC<12:23> */
            else {
                fpp_copy (&x, &fpp_ac);                 /* copy AC */
                sc = 027 - x.exp;                       /* shift amt */
                if (sc < 0)                             /* left? */
                    fpp_fr_lshn (x.fr, -sc, EXACT);
                else fpp_fr_algn (x.fr, sc, EXACT);
                fpp_write_xr (op3, x.fr[1]);            /* xr<-val<12:23> */
                }
            break;

        case 003:                                       /* XTA */
            for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++)
                x.fr[i] = 0;                            /* clear FOP2-4 */
            x.fr[1] = fpp_read_xr (op3);                /* get XR value */
            x.fr[0] = (x.fr[1] & 04000)? 07777: 0;
            x.exp = 027;                                /* standard exp */
            if (!(fpp_sta & FPS_DP)) {                  /* fp or ep? */
                fpp_norm (&x, EXACT);                   /* normalize */
                }
            fpp_copy (&fpp_ac, &x);                     /* result to AC */
            if (fpp_sta & FPS_DP)                       /* dp skips exp */
                fpp_ac.exp = x.exp;                     /*  so force copy */
            fpp_opa = fpp_xra + op3;
            break;

        case 004:                                       /* NOP */
            break;

        case 005:                                       /* STARTE */
            if (!(fpp_sta & FPS_EP)) {
                fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP;
                for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++)
                    fpp_ac.fr[i] = 0;                   /* clear FAC2-4 */
                }
            break;

        case 010:                                       /* LDX */
            wd = fpp_ad15 (0);                          /* load XR immed */
            fpp_write_xr (op3, wd);
            fpp_opa = fpp_xra + op3;
            break;

        case 011:                                       /* ADDX */
            wd = fpp_ad15 (0);
            wd = wd + fpp_read_xr (op3);                /* add to XR immed */
            fpp_write_xr (op3, wd);                     /* trims to 12b */
            fpp_opa = fpp_xra + op3;
            break;

        default:
            return stop_inst;
            }                                           /* end case subop */
        break;

    case 001:                                           /* FLDA */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &fpp_ac);
        break;

    case 002:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &fpp_ac);
        if (fpp_sta & FPS_DP)
            fpp_opa = ea + 1;
        else fpp_opa = ea + 2;
        break;

    case 003:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &fpp_ac);
        break;

    case 004:                                           /* jumps and sets */
        ad = fpp_ad15 (op3);                            /* get 15b address */
        switch (op2) {                                  /* case on subop */

        case 000: case 001: case 002: case 003:         /* cond jump */
        case 004: case 005: case 006: case 007:
            if (fpp_cond_met (op2))                     /* br if cond */
                fpp_fpc = ad;
            break;

        case 010:                                       /* SETX */
            fpp_xra = ad;
            break;

        case 011:                                       /* SETB */
            fpp_bra = ad;
            break;

        case 012:                                       /* JSA */
            fpp_write (ad, 01030 + (fpp_fpc >> 12));    /* save return */
            fpp_write (ad + 1, fpp_fpc);                /* trims to 12b */
            fpp_fpc = (ad + 2) & ADDRMASK;
            fpp_opa = fpp_fpc - 1;
            break;

        case 013:                                       /* JSR */
            fpp_write (fpp_bra + 1, 01030 + (fpp_fpc >> 12));
            fpp_write (fpp_bra + 2, fpp_fpc);           /* trims to 12b */
            fpp_opa = fpp_fpc = ad;
            break;

        default:
            return stop_inst;
            }                                           /* end case subop */
        break;

    case 005:                                           /* FADD */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 0);
        break;
        
    case 006:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 0);
        break;

    case 007:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 0);
        break;

    case 010: {                                         /* JNX */
        uint32 xrn = op2 & 07;
        ad = fpp_ad15 (op3);                            /* get 15b addr */
        wd = fpp_read_xr (xrn);                         /* read xr */
        if (op2 & 010) {                                /* inc? */
            wd = (wd + 1) & 07777;
            fpp_write_xr (xrn, wd);                     /* ++xr */
            }
        if (wd != 0)                                    /* xr != 0? */
            fpp_fpc = ad;                               /* jump */
        break;
        }
    case 011:                                           /* FSUB */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 1);
        break;
        
    case 012:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 1);
        break;

    case 013:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 1);
        break;

    case 014:                                           /* TRAP3 */
    case 020:                                           /* TRAP4 */
        fpp_opa = fpp_ad15 (op3);
        fpp_dump_apt (fpp_apta, FPS_TRPX);
        break;

    case 015:                                           /* FDIV */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_div (&fpp_ac, &x);
        break;
        
    case 016:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_div (&fpp_ac, &x);
        break;

    case 017:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_div (&fpp_ac, &x);
        break;

    case 021:                                           /* FMUL */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&fpp_ac, &x);
        break;
        
    case 022:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&fpp_ac, &x);
        break;

    case 023:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&fpp_ac, &x);
        break;

    case 024:                                           /* LTR */
        fpp_copy (&fpp_ac, (fpp_cond_met (op2 & 07)? &fpp_one: &fpp_zero));
        break;

    case 025:                                           /* FADDM */
        fpp_sta |= FPS_XXXM;
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&x, &fpp_ac, 0);
        fpp_write_op (ea, &x);                          /* store result */
        break;
        
    case 026:
        fpp_sta |= FPS_XXXM;
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&x, &fpp_ac, 0);
        fpp_write_op (ea, &x);                          /* store result */
        break;

    case 027:
        fpp_sta |= FPS_XXXM;
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&x, &fpp_ac, 0);
        fpp_write_op (ea, &x);                          /* store result */
        break;

    case 030:                                           /* IMUL/LEA */
        ea = fpp_2wd_dir (ir);                          /* 2-word direct */
        if (fpp_sta & FPS_DP) {                         /* dp? */
            fpp_read_op (ea, &x);                       /* IMUL */
            fpp_imul (&fpp_ac, &x);
            }
        else {                                          /* LEA */
            fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP;     /* set dp */
            fpp_ac.fr[0] = (ea >> 12) & 07;
            fpp_ac.fr[1] = ea & 07777;
            }
        break;

    case 031:                                           /* FSTA */
        ea = fpp_1wd_dir (ir);
        fpp_write_op (ea, &fpp_ac);
        break;

    case 032:
        ea = fpp_2wd_dir (ir);
        fpp_write_op (ea, &fpp_ac);
        break;

    case 033:
        ea = fpp_indir (ir);
        fpp_write_op (ea, &fpp_ac);
        break;

    case 034:                                           /* IMULI/LEAI */
        ea = fpp_indir (ir);                            /* 1-word indir */
        if (fpp_sta & FPS_DP) {                         /* dp? */
            fpp_read_op (ea, &x);                       /* IMUL */
            fpp_imul (&fpp_ac, &x);
            }
        else {                                          /* LEA */
            fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP;     /* set dp */
            fpp_ac.fr[0] = (ea >> 12) & 07;
            fpp_ac.fr[1] = ea & 07777;
            fpp_opa = ea;
            }
        break;

    case 035:                                           /* FMULM */
        fpp_sta |= FPS_XXXM;
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&x, &fpp_ac);
        fpp_write_op (ea, &x);                          /* store result */
        break;
        
    case 036:
        fpp_sta |= FPS_XXXM;
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&x, &fpp_ac);
        fpp_write_op (ea, &x);                          /* store result */
        break;

    case 037:
        fpp_sta |= FPS_XXXM;
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&x, &fpp_ac);
        fpp_write_op (ea, &x);                          /* store result */
        break;
        }                                               /* end sw op+mode */

    if (fpp_ssf) {
        fpp_dump_apt (fpp_apta, FPS_HLTX);              /* dump APT */
        fpp_ssf = 0;
        }

    if (sim_interval)
        sim_interval = sim_interval - 1;
    } while ((sim_interval > 0) && 
             ((fpp_sta & (FPS_RUN|FPS_PAUSE|FPS_LOCK)) == (FPS_RUN|FPS_LOCK)));
if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == FPS_RUN)
    sim_activate (uptr, 1);
fpp_ac.exp &= 07777;                                    /* mask AC exp */
return SCPE_OK;
}

/* Address decoding routines */

uint32 fpp_1wd_dir (uint32 ir)
{
uint32 ad; 

ad = fpp_bra + ((ir & 0177) * 3);                       /* base + 3*7b off */
if (fpp_sta & FPS_DP)                                   /* dp? skip exp */
    ad = ad + 1;
ad = ad & ADDRMASK;
if (fpp_sta & FPS_DP)
    fpp_opa = ad + 1;
else fpp_opa = ad + 2;
return ad;
}

uint32 fpp_2wd_dir (uint32 ir)
{
uint32 ad;

ad = fpp_ad15 (ir);                                     /* get 15b addr */
return fpp_adxr (ir, ad);                               /* do indexing */
}

uint32 fpp_indir (uint32 ir)
{
uint32 ad, wd1, wd2;

ad = fpp_bra + ((ir & 07) * 3);                         /* base + 3*3b off */
wd1 = fpp_read (ad + 1);                                /* bp+off points to */
wd2 = fpp_read (ad + 2);
ad = ((wd1 & 07) << 12) | wd2;                          /* indirect ptr */

ad = fpp_adxr (ir, ad);                                 /* do indexing */
if (fpp_sta & FPS_DP)
    fpp_opa = ad + 1;
else fpp_opa = ad + 2;
return ad;
}

uint32 fpp_ad15 (uint32 hi)
{
uint32 ad;

ad = ((hi & 07) << 12) | fpp_read (fpp_fpc);            /* 15b addr */
fpp_fpc = (fpp_fpc + 1) & ADDRMASK;                     /* incr FPC */
return ad;                                              /* return addr */
}

uint32 fpp_adxr (uint32 ir, uint32 base_ad)
{
uint32 xr, wd;

xr = (ir >> 3) & 07;
wd = fpp_read_xr (xr);                                  /* get xr */
if (ir & 0100) {                                        /* increment? */
    wd = (wd + 1) & 07777;                              /* inc, rewrite */
    fpp_write_xr (xr, wd);
    }
if (xr != 0) {                                          /* indexed? */
    if (fpp_sta & FPS_EP)
        wd = wd * 6;                                    /* scale by len */
    else if (fpp_sta & FPS_DP)
        wd = wd * 2;
    else wd = wd * 3;
    return (base_ad + wd) & ADDRMASK;                   /* return index */
    }
else return base_ad & ADDRMASK;                         /* return addr */
}

/* Computation routines */

/* Fraction/floating add */

void fpp_add (FPN *a, FPN *b, uint32 sub)
{
FPN x, y, z;
uint32 c, ediff;

fpp_zcopy (&x, a);                                      /* copy opnds */
fpp_zcopy (&y, b);
if (sub)                                                /* subtract? */
    fpp_fr_neg (y.fr, EXACT);                           /* neg B, exact */
if (fpp_sta & FPS_DP) {                                 /* dp? */
    uint32 cout = fpp_fr_add (z.fr, x.fr, y.fr, EXTEND);/* z = a + b */
    uint32 zsign = z.fr[0] & FPN_FRSIGN;
    cout = (cout? 04000: 0);                            /* make sign bit */
    /* overflow is indicated when signs are equal and overflow does not
       match the result sign bit */
    fpp_copy (a, &z);                                   /* result is z */
    if (!((x.fr[0] ^ y.fr[0]) & FPN_FRSIGN) && (cout != zsign)) {
        fpp_copy (a, &z);                               /* copy out result */
        fpp_dump_apt (fpp_apta, FPS_IOVX);              /* int ovf? */
        return;
        }
    }
else {                                                  /* fp or ep */
    if (fpp_fr_test (b->fr, 0, EXACT) == 0)             /* B == 0? */
        z = x;                                          /* result is A */
    else if (fpp_fr_test (a->fr, 0, EXACT) == 0)        /* A == 0? */
        z = y;                                          /* result is B */
    else {                                              /* fp or ep */
        if (x.exp < y.exp) {                            /* |a| < |b|? */
            z = x;                                      /* exchange ops */
            x = y;
            y = z;
            }
        ediff = x.exp - y.exp;                          /* exp diff */
        if (ediff <= (uint32) ((fpp_sta & FPS_EP)? 59: 24)) { /* any add? */
            z.exp = x.exp;                              /* result exp */
            if (ediff != 0)                             /* any align? */
                fpp_fr_algn (y.fr, ediff, EXTEND);      /* align, 60b */
            c = fpp_fr_add (z.fr, x.fr, y.fr, EXTEND);  /* add fractions */
            if ((((x.fr[0] ^ y.fr[0]) & FPN_FRSIGN) == 0) && /* same signs? */
                (c ||                                   /* carry out? */
                ((~x.fr[0] & z.fr[0] & FPN_FRSIGN)))) { /* + to - change? */
                fpp_fr_rsh1 (z.fr, c << 11, EXTEND);    /* rsh, insert cout */
                z.exp = z.exp + 1;                      /* incr exp */
                }                                       /* end same signs */
            }                                           /* end in range */
            else z = x;                                 /* ovrshift */
        }                                               /* end ops != 0 */
    if (fpp_norm (&z, EXTEND))                          /* norm, !exact? */
        fpp_round (&z);                                 /* round */
    fpp_copy (a, &z);                                   /* copy out */
    fpp_test_xp (&z);                                   /* ovf, unf? */
    }                                                   /* end else */
return;
}

/* Fraction/floating multiply */

void fpp_mul (FPN *a, FPN *b)
{
FPN x, y, z;

fpp_zcopy (&x, a);                                      /* copy opnds */
fpp_zcopy (&y, b);
if ((fpp_fr_test(y.fr, 0, EXACT-1) == 0) && (y.fr[EXACT-1] < 2)) {
    y.exp = 0;
    y.fr[EXACT-1] = 0;
}
if (fpp_sta & FPS_DP)                                   /* dp? */
    fpp_fr_mul (z.fr, x.fr, y.fr, TRUE);                /* mult frac */
else {                                                  /* fp or ep */
    fpp_norm (&x, EXACT);
    fpp_norm (&y, EXACT);
    z.exp = x.exp + y.exp;                              /* add exp */
    fpp_fr_mul (z.fr, x.fr, y.fr, TRUE);                /* mult frac */
    if (fpp_norm (&z, EXTEND))                          /* norm, !exact? */
        fpp_round (&z);                                 /* round */
    fpp_copy (a, &z);
    if (z.exp > 2047)
        fpp_dump_apt (fpp_apta, FPS_FOVX);              /* trap */
    return;
    }
fpp_copy (a, &z);                                       /* result is z */
return;
}

/* Fraction/floating divide */

void fpp_div (FPN *a, FPN *b)
{
FPN x, y, z;

if (fpp_fr_test (b->fr, 0, EXACT) == 0) {               /* divisor 0? */
    fpp_dump_apt (fpp_apta, FPS_DVZX);                  /* error */
    return;
    }
if (fpp_fr_test (a->fr, 0, EXACT) == 0)                 /* dividend 0? */
    return;                                             /* quotient is 0 */
fpp_zcopy (&x, a);                                      /* copy opnds */
fpp_zcopy (&y, b);
if (fpp_sta & FPS_DP) {                                 /* dp? */
    if (fpp_fr_div (z.fr, x.fr, y.fr)) {                /* fr div, ovflo? */
        fpp_dump_apt (fpp_apta, FPS_IOVX);              /* error */
        return;
        }
    fpp_copy (a, &z);                                   /* result is z */
    }
else {                                                  /* fp or ep */
    fpp_norm (&y, EXACT);                               /* norm divisor */
    if (fpp_fr_test (x.fr, 04000, EXACT) == 0) {        /* divd 1.000...? */
        x.fr[0] = 06000;                                /* fix */
        x.exp = x.exp + 1;
        }
    z.exp = x.exp - y.exp;                              /* calc exp */
    if (fpp_fr_div (z.fr, x.fr, y.fr)) {                /* fr div, ovflo? */
        uint32 cin = (a->fr[0] ^ b->fr[0]) & FPN_FRSIGN;
        fpp_fr_rsh1 (z.fr, cin, EXTEND);                /* rsh, insert sign */
        z.exp = z.exp + 1;                              /* incr exp */
        }
    if (fpp_norm (&z, EXTEND))                          /* norm, !exact? */
        fpp_round (&z);                                 /* round */
    fpp_copy (a, &z);
    if (z.exp > 2048) {                                /* underflow? */
        if (fpp_cmd & FPC_UNFX) {                       /* trap? */
            fpp_dump_apt (fpp_apta, FPS_UNF);
            return;
            }
        }
    }
return;
}

/* Integer multiply - returns true if overflow */

t_bool fpp_imul (FPN *a, FPN *b)
{
uint32 sext;
FPN x, y, z;

fpp_zcopy (&x, a);                                      /* copy args */
fpp_zcopy (&y, b);
fpp_fr_mul (z.fr, x.fr, y.fr, FALSE);                   /* mult fracs */
a->fr[0] = z.fr[1];                                     /* low 24b */
a->fr[1] = z.fr[2];
if ((a->fr[0] == 0) && (a->fr[1] == 0))                 /* fpp zeroes exp */
    a->exp = 0;                                         /* even in dp mode */
sext = (z.fr[2] & FPN_FRSIGN)? 07777: 0;
if (((z.fr[0] | z.fr[1] | sext) != 0) &&                /* hi 25b == 0 */
    ((z.fr[0] & z.fr[1] & sext) != 07777)) {            /* or 777777774? */
    fpp_dump_apt (fpp_apta, FPS_IOVX);
    return TRUE;
    }
return FALSE;
}

/* Auxiliary floating point routines */

t_bool fpp_cond_met (uint32 cond)
{
switch (cond) {

    case 0:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) == 0);

    case 1:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) >= 0);

    case 2:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) <= 0);

    case 3:
        return 1;

    case 4:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) != 0);

    case 5:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) < 0);

    case 6:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) > 0);

    case 7:
        return (fpp_ac.exp > 027);
    }
return 0;
}

/* Normalization - returns TRUE if rounding possible, FALSE if exact */

t_bool fpp_norm (FPN *a, uint32 cnt)
{
if (fpp_fr_test (a->fr, 0, cnt) == 0) {                 /* zero? */
    a->exp = 0;                                         /* clean exp */
    return FALSE;                                       /* don't round */
    }
while (((a->fr[0] == 0) && !(a->fr[1] & 04000)) ||      /* lead 13b same? */
       ((a->fr[0] == 07777) && (a->fr[1] & 04000))) {
    fpp_fr_lsh12 (a->fr, cnt);                          /* move word */
    a->exp = a->exp - 12;
    }
while (((a->fr[0] ^ (a->fr[0] << 1)) & FPN_FRSIGN) == 0) { /* until norm */
    fpp_fr_lsh1 (a->fr, cnt);                           /* shift 1b */
    a->exp = a->exp - 1;
    }
if (fpp_fr_test (a->fr, 04000, EXACT) == 0) {           /* 4000...0000? */
    a->fr[0] = 06000;                                   /* chg to 6000... */
    a->exp = a->exp + 1;                                /* with exp+1 */
    return FALSE;                                       /* don't round */
    }
return TRUE;
}

/* Exact fp number copy */

void fpp_copy (FPN *a, FPN *b)
{
uint32 i;

if (!(fpp_sta & FPS_DP))
    a->exp = b->exp;
for (i = 0; i < EXACT; i++)
    a->fr[i] = b->fr[i];
return;
}

/* Zero extended fp number copy (60b) */

void fpp_zcopy (FPN *a, FPN *b)
{
uint32 i;

a->exp = b->exp;
for (i = 0; i < FPN_NFR_EP; i++) {
    if ((i < FPN_NFR_FP) || (fpp_sta & FPS_EP))
        a->fr[i] = b->fr[i];
    else a->fr[i] = 0;
    }
a->fr[i++] = 0;
a->fr[i] = 0;
return;
}

/* Test exp for overflow or underflow, returns TRUE on trap */

t_bool fpp_test_xp (FPN *a)
{
if (a->exp > 2047) {                                /* overflow? */
    fpp_dump_apt (fpp_apta, FPS_FOVX);              /* trap */
    return TRUE;
    }
if (a->exp < -2048) {                               /* underflow? */
    if (fpp_cmd & FPC_UNFX) {                       /* trap? */
        fpp_dump_apt (fpp_apta, FPS_UNF);
        return TRUE;
        }
    fpp_copy (a, &fpp_zero);                        /* flush to 0 */
    }
return FALSE;
}

/* Round dp/fp value */

void fpp_round (FPN *a)
{
int32 i;
uint32 cin, afr0_sign;

if (fpp_sta & FPS_EP)                               /* ep? */
    return;                                         /* don't round */
afr0_sign = a->fr[0] & FPN_FRSIGN;                  /* save input sign */
cin = afr0_sign? 03777: 04000;
for (i = FPN_NFR_FP; i >= 0; i--) {                 /* 3 words */
   a->fr[i] = a->fr[i] + cin;                       /* add in carry */
   cin = (a->fr[i] >> 12) & 1;
   a->fr[i] = a->fr[i] & 07777;
   }
if (!(fpp_sta & FPS_DP) &&                          /* fp? */
    (afr0_sign ^ (a->fr[0] & FPN_FRSIGN))) {        /* sign change? */
    fpp_fr_rsh1 (a->fr, afr0_sign, EXACT);          /* rsh, insert sign */
    a->exp = a->exp + 1;
    }
return;
}

/* N-precision integer routines */

/* Fraction add/sub */

uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b, uint32 cnt)

{
uint32 i, cin;

for (i = cnt, cin = 0; i > 0; i--) {
    c[i - 1] = a[i - 1] + b[i - 1] + cin;
    cin = (c[i - 1] >> 12) & 1;
    c[i - 1] = c[i - 1] & 07777;
    }
return cin;
}

void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b, uint32 cnt)
{
uint32 i, cin;

for (i = cnt, cin = 0; i > 0; i--) {
    c[i - 1] = a[i - 1] - b[i - 1] - cin;
    cin = (c[i - 1] >> 12) & 1;
    c[i - 1] = c[i - 1] & 07777;
    }
return;
}

/* Fraction multiply - always develop 60b, multiply is
   either 24b*24b or 60b*60b
   
   This is a signed multiply.  The shift in for signed multiply is
   technically ALU_N XOR ALU_V.  This can be simplified as follows:

   a-sign   c-sign  result-sign     cout    overflow    N XOR V = shift in

   0        0       0               0       0           0
   0        0       1               0       1           0
   0        1       0               1       0           0
   0        1       1               0       0           1
   1        0       0               1       0           0
   1        0       1               0       0           1
   1        1       0               1       1           1
   1        1       1               1       0           1

   If a-sign == c-sign, shift-in = a-sign
   If a-sign != c-sign, shift-in = result-sign
   */

void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b, t_bool fix)
{
uint32 i, cnt, lo, wc, fill, b_sign;

b_sign = b[0] & FPN_FRSIGN;                         /* remember b's sign */

fpp_fr_fill (c, 0, FPN_NFR_MDS);                    /* clr answer */
if (fpp_sta & FPS_EP)                               /* ep? */
    lo = FPN_NFR_EP;                                /* low order mpyr word */
else  
    lo = FPN_NFR_FP;                                /* low order mpyr word */

if (fix)
    fpp_fr_algn (a, 12, FPN_NFR_MDS + 1);           /* fill left with sign */
wc = 2;                                             /* 3 words at start */
fill = 0;
cnt = lo * 12;                                      /* total steps */
for (i = 0; i < cnt; i++) { 
    if ((i % 12) == 0) {
        wc++;                                       /* do another word */
        lo--;                                       /* and next mpyr word */
        fpp_fr_algn (c, 24, wc + 1);
        c[wc] = 0;
        c[0] = c[1] = fill;                         /* propagate sign */
        }
    if (b[lo] & FPN_FRSIGN)                         /* mpyr bit set? */
        fpp_fr_add(c, a, c, wc);
    fill = ((c[0] & FPN_FRSIGN) ? 07777 : 0);       /* remember sign */
    fpp_fr_lsh1 (c, wc);                            /* shift the result */
    fpp_fr_lsh1 (b + lo, 1);                        /* shift mpcd */
    
    }

if (!fix)                                           /* imul shifts result */
    fpp_fr_rsh1 (c, c[0] & FPN_FRSIGN, EXACT + 1);  /* result is 1 wd right */
if (b_sign) {                                       /* if mpyr was negative */
    if (fix)
        fpp_fr_lsh12 (a, FPN_NFR_MDS+1);            /* restore a */
    fpp_fr_sub (c, c, a, EXACT);                    /* adjust result */
    fpp_fr_sub (c, c, a, EXACT);
    }

return;
}

/* Fraction divide */

t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b)
{
uint32 i, old_c, lo, cnt, sign, b_sign, addsub, limit;
/* Number of words processed by each divide step */
static uint32 limits[7] = {6, 6, 5, 4, 3, 3, 2};

fpp_fr_fill (c, 0, FPN_NFR_MDS);                    /* clr answer */
sign = (a[0] ^ b[0]) & FPN_FRSIGN;                  /* sign of result */
b_sign = (b[0] & FPN_FRSIGN);
if (a[0] & FPN_FRSIGN)                              /* |a| */
    fpp_fr_neg (a, EXACT);
if (fpp_sta & FPS_EP)                               /* ep? 6 words */
    lo = FPN_NFR_EP-1;
else lo = FPN_NFR_FP-1;                             /* fp, dp? 3 words */
cnt = (lo + 1) * 12;
addsub = 04000;                                     /* setup first op */
for (i = 0; i < cnt; i++) {                         /* loop */
    limit = limits[i / 12];                         /* how many wds this time */
    fpp_fr_lsh1 (c, FPN_NFR_MDS);                   /* shift quotient */
    if (addsub ^ b_sign)                            /* diff signs, subtr */
        fpp_fr_sub (a, a, b, limit);                /* divd - divr */
    else
        fpp_fr_add (a, a, b, limit);                /* restore */
    if (!(a[0] & FPN_FRSIGN)) {
        c[lo] |= 1;                                 /* set quo bit */
        addsub = 04000;                             /* sign for nxt loop */
        }
    else addsub = 0;
    fpp_fr_lsh1 (a, limit);                         /* shift dividend */
    }
old_c = c[0];                                       /* save ho quo */
if (sign)                                           /* expect neg ans? */
    fpp_fr_neg (c, EXTEND);                         /* -quo */
if (old_c & FPN_FRSIGN)                             /* sign set before */
    return TRUE;                                    /* neg? */
return FALSE;
}

/* Negate - 24b or 60b */

uint32 fpp_fr_neg (uint32 *a, uint32 cnt)
{
uint32 i, cin;

for (i = cnt, cin = 1; i > 0; i--) {
    a[i - 1] = (~a[i - 1] + cin) & 07777;
    cin = (cin != 0 && a[i - 1] == 0);
    }
return cin;
}

/* Test (compare to x'0...0) - 24b or 60b */

int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt)
{
uint32 i;

if (a[0] != v0)
    return (a[0] & FPN_FRSIGN)? -1: +1;
for (i = 1; i < cnt; i++) {
    if (a[i] != 0)
        return (a[0] & FPN_FRSIGN)? -1: +1;
    }
return 0;
}

/* Fraction compare - 24b or 60b */

int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt)
{
uint32 i;

if ((a[0] ^ b[0]) & FPN_FRSIGN)
    return (b[0] & FPN_FRSIGN)? +1: -1;
for (i = 0; i < cnt; i++) {
    if (a[i] > b[i])
        return (b[0] & FPN_FRSIGN)? +1: -1;
    if (a[i] < b[i])
        return (b[0] & FPN_FRSIGN)? -1: +1;
    }
return 0;
}

/* Fraction fill */

void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt)
{
uint32 i;

for (i = 0; i < cnt; i++)
    a[i] = v;
return;
}

/* Left shift n (unsigned) */

void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt)
{
uint32 i;

if (sc >= (cnt * 12)) {                             /* out of range? */
    fpp_fr_fill (a, 0, cnt);
    return;
    }
while (sc >= 12) {                                  /* word shift? */
    fpp_fr_lsh12 (a, cnt);
    sc = sc - 12;
    }
if (sc == 0)                                        /* any more? */
    return;
for (i = 1; i < cnt; i++)                           /* bit shift */
    a[i - 1] = ((a[i - 1] << sc) | (a[i] >> (12 - sc))) & 07777;
a[cnt - 1] = (a[cnt - 1] << sc) & 07777;
return;
}

/* Left shift 12b (unsigned) */

void fpp_fr_lsh12 (uint32 *a, uint32 cnt)
{
uint32 i;

for (i = 1; i < cnt; i++)
    a[i - 1] = a[i];
a[cnt - 1] = 0;
return;
}

/* Left shift 1b (unsigned) */

void fpp_fr_lsh1 (uint32 *a, uint32 cnt)
{
uint32 i;

for (i = 1; i < cnt; i++)
    a[i - 1] = ((a[i - 1] << 1) | (a[i] >> 11)) & 07777;
a[cnt - 1] = (a[cnt - 1] << 1) & 07777;
return;
}

/* Right shift 1b, with shift in */

void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt)
{
uint32 i;

for (i = cnt - 1; i > 0; i--)
    a[i] = ((a[i] >> 1) | (a[i - 1] << 11)) & 07777;
a[0] = (a[0] >> 1) | sign;
return;
}

/* Right shift n (signed) */

void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt)
{
uint32 i, sign;

sign = (a[0] & FPN_FRSIGN)? 07777: 0;
if (sc >= (cnt * 12)) {                             /* out of range? */
    fpp_fr_fill (a, sign, cnt);
    return;
    }
while (sc >= 12) {
    for (i = cnt - 1; i > 0; i--)
        a[i] = a[i - 1];
    a[0] = sign;
    sc = sc - 12;
    }
if (sc == 0)
    return;
for (i = cnt - 1; i > 0; i--)
    a[i] = ((a[i] >> sc) | (a[i - 1] << (12 - sc))) & 07777;
a[0] = ((a[0] >> sc) | (sign << (12 - sc))) & 07777;
return;
}

/* Read/write routines */

void fpp_read_op (uint32 ea, FPN *a)
{
uint32 i;

if (!(fpp_sta & FPS_DP)) {
    a->exp = fpp_read (ea++);
    a->exp = SEXT12 (a->exp);
    }
for (i = 0; i < EXACT; i++)
    a->fr[i] = fpp_read (ea + i);
return;
}

void fpp_write_op (uint32 ea, FPN *a)
{
uint32 i;

fpp_opa = ea + 2;
if (!(fpp_sta & FPS_DP))
    fpp_write (ea++, a->exp);
for (i = 0; i < EXACT; i++)
    fpp_write (ea + i, a->fr[i]);
return;
}

uint32 fpp_read (uint32 ea)
{
ea = ea & ADDRMASK;
if (fpp_cmd & FPC_FIXF)
    ea = fpp_aptsvf | (ea & 07777);
return M[ea];
}

void fpp_write (uint32 ea, uint32 val)
{
ea = ea & ADDRMASK;
if (fpp_cmd & FPC_FIXF)
    ea = fpp_aptsvf | (ea & 07777);
if (MEM_ADDR_OK (ea))
    M[ea] = val & 07777;
return;
}

uint32 apt_read (uint32 ea)
{
ea = ea & ADDRMASK;
return M[ea];
}

void apt_write (uint32 ea, uint32 val)
{
ea = ea & ADDRMASK;
if (MEM_ADDR_OK (ea))
    M[ea] = val & 07777;
return;
}

/* Utility routines */

void fpp_load_apt (uint32 ad)
{
uint32 wd0, i;

wd0 = apt_read (ad++);
fpp_fpc = ((wd0 & 07) << 12) | apt_read (ad++);
if (FPC_GETFAST (fpp_cmd) != 017) {
    fpp_xra = ((wd0 & 00070) << 9) | apt_read (ad++);
    fpp_bra = ((wd0 & 00700) << 6) | apt_read (ad++);
    fpp_opa = ((wd0 & 07000) << 3) | apt_read (ad++);
    fpp_ac.exp = apt_read (ad++);
    for (i = 0; i < EXACT; i++)
        fpp_ac.fr[i] = apt_read (ad++);
    }
fpp_aptsvf = (ad - 1) & 070000;
fpp_sta |= FPS_RUN;
return;
}

void fpp_dump_apt (uint32 ad, uint32 sta)
{
uint32 wd0, i;

wd0 = (fpp_fpc >> 12) & 07;
if (FPC_GETFAST (fpp_cmd) != 017)
    wd0 = wd0 |
        ((fpp_opa >> 3) & 07000) |
        ((fpp_bra >> 6) & 00700) |
        ((fpp_xra >> 9) & 00070);
apt_write (ad++, wd0);
apt_write (ad++, fpp_fpc);
if (FPC_GETFAST (fpp_cmd) != 017) {
    apt_write (ad++, fpp_xra);
    apt_write (ad++, fpp_bra);
    apt_write (ad++, fpp_opa);
    apt_write (ad++, fpp_ac.exp);
    for (i = 0; i < EXACT; i++)
        apt_write (ad++, fpp_ac.fr[i]);
    }
fpp_sta = (fpp_sta | sta) & ~FPS_RUN;
fpp_flag = 1;
if (fpp_cmd & FPC_IE)
    int_req |= INT_FPP;
return;
}

/* Reset routine */

t_stat fpp_reset (DEVICE *dptr)
{
sim_cancel (&fpp_unit);
fpp_flag = 0;
fpp_last_lockbit = 0;
int_req &= ~INT_FPP;
if (sim_switches & SWMASK ('P')) {
    fpp_apta = 0;
    fpp_aptsvf = 0;
    fpp_fpc = 0;
    fpp_bra = 0;
    fpp_xra = 0;
    fpp_opa = 0;
    fpp_ac = fpp_zero;
    fpp_ssf = 0;
    fpp_sta = 0;
    fpp_cmd = 0;
    }
else {
    fpp_sta &= ~(FPS_DP|FPS_EP|FPS_TRPX|FPS_DVZX|FPS_IOVX|FPS_FOVX|FPS_UNF);
    fpp_cmd &= (FPC_DP|FPC_UNFX|FPC_IE);
    }

return SCPE_OK;
}
Added src/PDP8/pdp8_lp.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_lp.c: PDP-8 line printer simulator

   Copyright (c) 1993-2016, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   lpt          LP8E line printer

   16-Dec-16    DJG     Added IOT 6660 to allow WPS WS78 3.4 to print
   19-Jan-07    RMS     Added UNIT_TEXT
   25-Apr-03    RMS     Revised for extended file support
   04-Oct-02    RMS     Added DIB, enable/disable, device number support
   30-May-02    RMS     Widened POS to 32b
*/

#include "pdp8_defs.h"

extern int32 int_req, int_enable, dev_done, stop_inst;

int32 lpt_err = 0;                                      /* error flag */
int32 lpt_stopioe = 0;                                  /* stop on error */

int32 lpt (int32 IR, int32 AC);
t_stat lpt_svc (UNIT *uptr);
t_stat lpt_reset (DEVICE *dptr);
t_stat lpt_attach (UNIT *uptr, CONST char *cptr);
t_stat lpt_detach (UNIT *uptr);

/* LPT data structures

   lpt_dev      LPT device descriptor
   lpt_unit     LPT unit descriptor
   lpt_reg      LPT register list
*/

DIB lpt_dib = { DEV_LPT, 1, { &lpt } };

UNIT lpt_unit = {
    UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT
    };

REG lpt_reg[] = {
    { ORDATAD (BUF, lpt_unit.buf, 8,"last data item processed") },
    { FLDATAD (ERR, lpt_err, 0, "error status flag") },
    { FLDATAD (DONE, dev_done, INT_V_LPT, "device done flag") },
    { FLDATAD (ENABLE, int_enable, INT_V_LPT, "interrupt enable flag") },
    { FLDATAD (INT, int_req, INT_V_LPT, "interrupt pending flag") },
    { DRDATAD (POS, lpt_unit.pos, T_ADDR_W, "position in the output file"), PV_LEFT },
    { DRDATAD (TIME, lpt_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT },
    { FLDATAD (STOP_IOE, lpt_stopioe, 0, "stop on I/O error") },
    { ORDATA (DEVNUM, lpt_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB lpt_mod[] = {
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { 0 }
    };

DEVICE lpt_dev = {
    "LPT", &lpt_unit, lpt_reg, lpt_mod,
    1, 10, 31, 1, 8, 8,
    NULL, NULL, &lpt_reset,
    NULL, &lpt_attach, &lpt_detach,
    &lpt_dib, DEV_DISABLE
    };

/* IOT routine */

int32 lpt (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 0:                                             /* PKSTF */
        dev_done = dev_done | INT_LPT;                  /* set flag */
        int_req = INT_UPDATE;                           /* update interrupts */
        return AC;

    case 1:                                             /* PSKF */
        return (dev_done & INT_LPT)? IOT_SKP + AC: AC;

    case 2:                                             /* PCLF */
        dev_done = dev_done & ~INT_LPT;                 /* clear flag */
        int_req = int_req & ~INT_LPT;                   /* clear int req */
        return AC;

    case 3:                                             /* PSKE */
        return (lpt_err)? IOT_SKP + AC: AC;

    case 6:                                             /* PCLF!PSTB */
        dev_done = dev_done & ~INT_LPT;                 /* clear flag */
        int_req = int_req & ~INT_LPT;                   /* clear int req */

    case 4:                                             /* PSTB */
        lpt_unit.buf = AC & 0177;                       /* load buffer */
        if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) ||
            (lpt_unit.buf == 012)) {
            sim_activate (&lpt_unit, lpt_unit.wait);
            return AC;
            }
        return (lpt_svc (&lpt_unit) << IOT_V_REASON) + AC;

    case 5:                                             /* PSIE */
        int_enable = int_enable | INT_LPT;              /* set enable */
        int_req = INT_UPDATE;                           /* update interrupts */
        return AC;

    case 7:                                             /* PCIE */
        int_enable = int_enable & ~INT_LPT;             /* clear enable */
        int_req = int_req & ~INT_LPT;                   /* clear int req */
        return AC;

    default:
        return (stop_inst << IOT_V_REASON) + AC;
        }                                               /* end switch */
}

/* Unit service */

t_stat lpt_svc (UNIT *uptr)
{
dev_done = dev_done | INT_LPT;                          /* set done */
int_req = INT_UPDATE;                                   /* update interrupts */
if ((uptr->flags & UNIT_ATT) == 0) {
    lpt_err = 1;
    return IORETURN (lpt_stopioe, SCPE_UNATT);
    }
fputc (uptr->buf, uptr->fileref);                       /* print char */
uptr->pos = ftell (uptr->fileref);
if (ferror (uptr->fileref)) {                           /* error? */
    sim_perror ("LPT I/O error");
    clearerr (uptr->fileref);
    return SCPE_IOERR;
    }
return SCPE_OK;
}

/* Reset routine */

t_stat lpt_reset (DEVICE *dptr)
{
lpt_unit.buf = 0;
dev_done = dev_done & ~INT_LPT;                         /* clear done, int */
int_req = int_req & ~INT_LPT;
int_enable = int_enable | INT_LPT;                      /* set enable */
lpt_err = (lpt_unit.flags & UNIT_ATT) == 0;
sim_cancel (&lpt_unit);                                 /* deactivate unit */
return SCPE_OK;
}

/* Attach routine */

t_stat lpt_attach (UNIT *uptr, CONST char *cptr)
{
t_stat reason;

reason = attach_unit (uptr, cptr);
lpt_err = (lpt_unit.flags & UNIT_ATT) == 0;
return reason;
}

/* Detach routine */

t_stat lpt_detach (UNIT *uptr)
{
lpt_err = 1;
return detach_unit (uptr);
}
Added src/PDP8/pdp8_mt.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_mt.c: PDP-8 magnetic tape simulator

   Copyright (c) 1993-2011, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   mt           TM8E/TU10 magtape

   16-Feb-06    RMS     Added tape capacity checking
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   18-Mar-05    RMS     Added attached test to detach routine
   25-Apr-03    RMS     Revised for extended file support
   29-Mar-03    RMS     Added multiformat support
   04-Mar-03    RMS     Fixed bug in SKTR
   01-Mar-03    RMS     Fixed interrupt handling
                        Revised for magtape library
   30-Oct-02    RMS     Revised BOT handling, added error record handling
   04-Oct-02    RMS     Added DIBs, device number support
   30-Aug-02    RMS     Revamped error handling
   28-Aug-02    RMS     Added end of medium support
   30-May-02    RMS     Widened POS to 32b
   22-Apr-02    RMS     Added maximum record length test
   06-Jan-02    RMS     Changed enable/disable support
   30-Nov-01    RMS     Added read only unit, extended SET/SHOW support
   24-Nov-01    RMS     Changed UST, POS, FLG to arrays
   25-Apr-01    RMS     Added device enable/disable support
   04-Oct-98    RMS     V2.4 magtape format
   22-Jan-97    RMS     V2.3 magtape format
   01-Jan-96    RMS     Rewritten from TM8-E Maintenance Manual

   Magnetic tapes are represented as a series of variable records
   of the form:

        32b byte count
        byte 0
        byte 1
        :
        byte n-2
        byte n-1
        32b byte count

   If the byte count is odd, the record is padded with an extra byte
   of junk.  File marks are represented by a byte count of 0.
*/

#include "pdp8_defs.h"
#include "sim_tape.h"

#define MT_NUMDR        8                               /* #drives */
#define USTAT           u3                              /* unit status */
#define MT_MAXFR        (1 << 16)                       /* max record lnt */
#define WC_SIZE         (1 << 12)                       /* max word count */
#define WC_MASK         (WC_SIZE - 1)

/* Command/unit - mt_cu */

#define CU_V_UNIT       9                               /* unit */
#define CU_M_UNIT       07
#define CU_PARITY       00400                           /* parity select */
#define CU_IEE          00200                           /* error int enable */
#define CU_IED          00100                           /* done int enable */
#define CU_V_EMA        3                               /* ext mem address */
#define CU_M_EMA        07
#define CU_EMA          (CU_M_EMA << CU_V_EMA)
#define CU_DTY          00002                           /* drive type */
#define CU_UNPAK        00001                           /* 6b vs 8b mode */
#define GET_UNIT(x)     (((x) >> CU_V_UNIT) & CU_M_UNIT)
#define GET_EMA(x)      (((x) & CU_EMA) << (12 - CU_V_EMA))

/* Function - mt_fn */

#define FN_V_FNC        9                               /* function */
#define FN_M_FNC        07
#define  FN_UNLOAD       00
#define  FN_REWIND       01
#define  FN_READ         02
#define  FN_CMPARE       03
#define  FN_WRITE        04
#define  FN_WREOF        05
#define  FN_SPACEF       06
#define  FN_SPACER       07
#define FN_ERASE        00400                           /* erase */
#define FN_CRC          00200                           /* read CRC */
#define FN_GO           00100                           /* go */
#define FN_INC          00040                           /* incr mode */
#define FN_RMASK        07700                           /* readable bits */
#define GET_FNC(x)      (((x) >> FN_V_FNC) & FN_M_FNC)

/* Status - stored in mt_sta or (*) uptr->USTAT */

#define STA_ERR         (04000 << 12)                   /* error */
#define STA_REW         (02000 << 12)                   /* *rewinding */
#define STA_BOT         (01000 << 12)                   /* *start of tape */
#define STA_REM         (00400 << 12)                   /* *offline */
#define STA_PAR         (00200 << 12)                   /* parity error */
#define STA_EOF         (00100 << 12)                   /* *end of file */
#define STA_RLE         (00040 << 12)                   /* rec lnt error */
#define STA_DLT         (00020 << 12)                   /* data late */
#define STA_EOT         (00010 << 12)                   /* *end of tape */
#define STA_WLK         (00004 << 12)                   /* *write locked */
#define STA_CPE         (00002 << 12)                   /* compare error */
#define STA_ILL         (00001 << 12)                   /* illegal */
#define STA_9TK         00040                           /* 9 track */
/* #define STA_BAD      00020                         *//* bad tape?? */
#define STA_INC         00010                           /* increment error */
#define STA_LAT         00004                           /* lateral par error */
#define STA_CRC         00002                           /* CRC error */
#define STA_LON         00001                           /* long par error */

#define STA_CLR         (FN_RMASK | 00020)              /* always clear */
#define STA_DYN         (STA_REW | STA_BOT | STA_REM | STA_EOF | \
                         STA_EOT | STA_WLK)             /* kept in USTAT */

extern uint16 M[];
extern int32 int_req, stop_inst;
extern UNIT cpu_unit;

int32 mt_cu = 0;                                        /* command/unit */
int32 mt_fn = 0;                                        /* function */
int32 mt_ca = 0;                                        /* current address */
int32 mt_wc = 0;                                        /* word count */
int32 mt_sta = 0;                                       /* status register */
int32 mt_db = 0;                                        /* data buffer */
int32 mt_done = 0;                                      /* mag tape flag */
int32 mt_time = 10;                                     /* record latency */
int32 mt_stopioe = 1;                                   /* stop on error */
uint8 *mtxb = NULL;                                     /* transfer buffer */

int32 mt70 (int32 IR, int32 AC);
int32 mt71 (int32 IR, int32 AC);
int32 mt72 (int32 IR, int32 AC);
t_stat mt_svc (UNIT *uptr);
t_stat mt_reset (DEVICE *dptr);
t_stat mt_attach (UNIT *uptr, CONST char *cptr);
t_stat mt_detach (UNIT *uptr);
int32 mt_updcsta (UNIT *uptr);
int32 mt_ixma (int32 xma);
t_stat mt_map_err (UNIT *uptr, t_stat st);
t_stat mt_vlock (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
UNIT *mt_busy (void);
void mt_set_done (void);

/* MT data structures

   mt_dev       MT device descriptor
   mt_unit      MT unit list
   mt_reg       MT register list
   mt_mod       MT modifier list
*/

DIB mt_dib = { DEV_MT, 3, { &mt70, &mt71, &mt72 } };

UNIT mt_unit[] = {
    { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
    { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
    { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
    { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
    { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
    { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
    { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
    { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }
    };

REG mt_reg[] = {
    { ORDATAD (CMD, mt_cu, 12, "command") },
    { ORDATAD (FNC, mt_fn, 12, "function") },
    { ORDATAD (CA, mt_ca, 12, "memory address") },
    { ORDATAD (WC, mt_wc, 12, "word count") },
    { ORDATAD (DB, mt_db, 12, "data buffer") },
    { GRDATAD (STA, mt_sta, 8, 12, 12, "status buffer") },
    { ORDATAD (STA2, mt_sta, 6, "secondary status") },
    { FLDATAD (DONE, mt_done, 0, "device done flag") },
    { FLDATAD (INT, int_req, INT_V_MT, "interrupt pending flag") },
    { FLDATAD (STOP_IOE, mt_stopioe, 0, "stop on I/O error") },
    { DRDATAD (TIME, mt_time, 24, "record delay"), PV_LEFT },
    { URDATAD (UST, mt_unit[0].USTAT, 8, 16, 0, MT_NUMDR, 0, "unit status, units 0 to 7") },
    { URDATAD (POS, mt_unit[0].pos, 10, T_ADDR_W, 0,
              MT_NUMDR, PV_LEFT | REG_RO, "position, units 0 to 7") },
    { FLDATA (DEVNUM, mt_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB mt_mod[] = {
    { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &mt_vlock },
    { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &mt_vlock }, 
    { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
      &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
    { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",
      &sim_tape_set_capac, &sim_tape_show_capac, NULL },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { 0 }
    };

DEVICE mt_dev = {
    "MT", mt_unit, mt_reg, mt_mod,
    MT_NUMDR, 10, 31, 1, 8, 8,
    NULL, NULL, &mt_reset,
    NULL, &mt_attach, &mt_detach,
    &mt_dib, DEV_DISABLE | DEV_TAPE
    };

/* IOT routines */

int32 mt70 (int32 IR, int32 AC)
{
int32 f;
UNIT *uptr;

uptr = mt_dev.units + GET_UNIT (mt_cu);                 /* get unit */
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 1:                                             /* LWCR */
        mt_wc = AC;                                     /* load word count */
        return 0;

    case 2:                                             /* CWCR */
        mt_wc = 0;                                      /* clear word count */
        return AC;

    case 3:                                             /* LCAR */
        mt_ca = AC;                                     /* load mem address */
        return 0;

    case 4:                                             /* CCAR */
        mt_ca = 0;                                      /* clear mem address */
        return AC;

    case 5:                                             /* LCMR */
        if (mt_busy ())                                 /* busy? illegal op */
            mt_sta = mt_sta | STA_ILL | STA_ERR;
        mt_cu = AC;                                     /* load command reg */
        mt_updcsta (mt_dev.units + GET_UNIT (mt_cu));
        return 0;

    case 6:                                             /* LFGR */
        if (mt_busy ())                                 /* busy? illegal op */
            mt_sta = mt_sta | STA_ILL | STA_ERR;
        mt_fn = AC;                                     /* load function */
        if ((mt_fn & FN_GO) == 0) {                     /* go set? */
            mt_updcsta (uptr);                          /* update status */
            return 0;
            }
        f = GET_FNC (mt_fn);                            /* get function */
        if (((uptr->flags & UNIT_ATT) == 0) ||
              sim_is_active (uptr) ||
           (((f == FN_WRITE) || (f == FN_WREOF)) && sim_tape_wrp (uptr))
           || (((f == FN_SPACER) || (f == FN_REWIND)) && sim_tape_bot (uptr))) {
            mt_sta = mt_sta | STA_ILL | STA_ERR;        /* illegal op error */
            mt_set_done ();                             /* set done */
            mt_updcsta (uptr);                          /* update status */
            return 0;
            }
        uptr->USTAT = uptr->USTAT & STA_WLK;            /* clear status */
        if (f == FN_UNLOAD) {                           /* unload? */
            detach_unit (uptr);                         /* set offline */
            uptr->USTAT = STA_REW | STA_REM;            /* rewinding, off */
            mt_set_done ();                             /* set done */
            }
        else if (f == FN_REWIND) {                      /* rewind */
            uptr->USTAT = uptr->USTAT | STA_REW;        /* rewinding */
            mt_set_done ();                             /* set done */
            }
        else mt_done = 0;                               /* clear done */
        mt_updcsta (uptr);                              /* update status */
        sim_activate (uptr, mt_time);                   /* start io */
        return 0;

    case 7:                                             /* LDBR */
        if (mt_busy ())                                 /* busy? illegal op */
            mt_sta = mt_sta | STA_ILL | STA_ERR;
        mt_db = AC;                                     /* load buffer */
        mt_set_done ();                                 /* set done */
        mt_updcsta (uptr);                              /* update status */
        return 0;
        }                                               /* end switch */

return (stop_inst << IOT_V_REASON) + AC;                /* ill inst */
}

int32 mt71 (int32 IR, int32 AC)
{
UNIT *uptr;

uptr = mt_dev.units + GET_UNIT (mt_cu);
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 1:                                             /* RWCR */
        return mt_wc;                                   /* read word count */

    case 2:                                             /* CLT */
        mt_reset (&mt_dev);                             /* reset everything */
        return AC;

    case 3:                                             /* RCAR */
        return mt_ca;                                   /* read mem address */

    case 4:                                             /* RMSR */
        return ((mt_updcsta (uptr) >> 12) & 07777);     /* read status */

    case 5:                                             /* RCMR */
        return mt_cu;                                   /* read command */

    case 6:                                             /* RFSR */
        return (((mt_fn & FN_RMASK) | (mt_updcsta (uptr) & ~FN_RMASK))
                 & 07777);                              /* read function */

    case 7:                                             /* RDBR */
        return mt_db;                                   /* read data buffer */
        }

return (stop_inst << IOT_V_REASON) + AC;                /* ill inst */
}

int32 mt72 (int32 IR, int32 AC)
{
UNIT *uptr;

uptr = mt_dev.units + GET_UNIT (mt_cu);                 /* get unit */
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 1:                                             /* SKEF */
        return (mt_sta & STA_ERR)? IOT_SKP + AC: AC;

    case 2:                                             /* SKCB */
        return (!mt_busy ())? IOT_SKP + AC: AC;

    case 3:                                             /* SKJD */
        return mt_done? IOT_SKP + AC: AC;

    case 4:                                             /* SKTR */
        return (!sim_is_active (uptr) &&
            (uptr->flags & UNIT_ATT))? IOT_SKP + AC: AC;

    case 5:                                             /* CLF */
        if (!sim_is_active (uptr)) mt_reset (&mt_dev);  /* if TUR, zap */
        else {                                          /* just ctrl zap */
            mt_sta = 0;                                 /* clear status */
            mt_done = 0;                                /* clear done */
            mt_updcsta (uptr);                          /* update status */
            }
        return AC;
        }                                               /* end switch */

return (stop_inst << IOT_V_REASON) + AC;                /* ill inst */
}

/* Unit service

   If rewind done, reposition to start of tape, set status
   else, do operation, set done, interrupt
*/

t_stat mt_svc (UNIT *uptr)
{
int32 f, i, p, u, wc, xma;
t_mtrlnt tbc, cbc;
t_bool passed_eot;
uint16 c, c1, c2;
t_stat st, r = SCPE_OK;

u = (int32) (uptr - mt_dev.units);                      /* get unit number */
f = GET_FNC (mt_fn);                                    /* get command */
xma = GET_EMA (mt_cu) + mt_ca;                          /* get mem addr */
wc = WC_SIZE - mt_wc;                                   /* get wc */

if (uptr->USTAT & STA_REW) {                            /* rewind? */
    sim_tape_rewind (uptr);                             /* update position */
    if (uptr->flags & UNIT_ATT)                         /* still on line? */
        uptr->USTAT = (uptr->USTAT & STA_WLK) | STA_BOT;
    else uptr->USTAT = STA_REM;
    if (u == GET_UNIT (mt_cu)) {                        /* selected? */
        mt_set_done ();                                 /* set done */
        mt_updcsta (uptr);                              /* update status */
        }
    return SCPE_OK;
    }

if ((uptr->flags & UNIT_ATT) == 0) {                    /* if not attached */
    uptr->USTAT = STA_REM;                              /* unit off line */
    mt_sta = mt_sta | STA_ILL | STA_ERR;                /* illegal operation */
    mt_set_done ();                                     /* set done */
    mt_updcsta (uptr);                                  /* update status */
    return IORETURN (mt_stopioe, SCPE_UNATT);
    }

passed_eot = sim_tape_eot (uptr);                       /* passed eot? */
switch (f) {                                            /* case on function */

    case FN_READ:                                       /* read */
    case FN_CMPARE:                                     /* read/compare */
        st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR);      /* read rec */
        if (st == MTSE_RECE)                            /* rec in err? */
            mt_sta = mt_sta | STA_PAR | STA_ERR;
        else if (st != MTSE_OK) {                       /* other error? */
            r = mt_map_err (uptr, st);                  /* map error */
            mt_sta = mt_sta | STA_RLE | STA_ERR;        /* err, eof/eom, tmk */
            break;
            }
        cbc = (mt_cu & CU_UNPAK)? wc: wc * 2;           /* expected bc */
        if (tbc != cbc)                                 /* wrong size? */
            mt_sta = mt_sta | STA_RLE | STA_ERR;
        if (tbc < cbc) {                                /* record small? */
            cbc = tbc;                                  /* use smaller */
            wc = (mt_cu & CU_UNPAK)? cbc: (cbc + 1) / 2;
            }
        for (i = p = 0; i < wc; i++) {                  /* copy buffer */
            xma = mt_ixma (xma);                        /* increment xma */
            mt_wc = (mt_wc + 1) & 07777;                /* incr word cnt */
            if (mt_cu & CU_UNPAK) c = mtxb[p++];
            else {
                c1 = mtxb[p++] & 077;
                c2 = mtxb[p++] & 077;
                c = (c1 << 6) | c2;
                }
            if ((f == FN_READ) && MEM_ADDR_OK (xma))
                M[xma] = c;
            else if ((f == FN_CMPARE) && (M[xma] != c)) {
                mt_sta = mt_sta | STA_CPE | STA_ERR;
                break;
                }
            }
        break;

    case FN_WRITE:                                      /* write */
        tbc = (mt_cu & CU_UNPAK)? wc: wc * 2;
        for (i = p = 0; i < wc; i++) {                  /* copy buf to tape */
            xma = mt_ixma (xma);                        /* incr mem addr */
            if (mt_cu & CU_UNPAK)
                mtxb[p++] = M[xma] & 0377;
            else {
                mtxb[p++] = (M[xma] >> 6) & 077;
                mtxb[p++] = M[xma] & 077;
                }
            }
        if ((st = sim_tape_wrrecf (uptr, mtxb, tbc))) { /* write rec, err? */
            r = mt_map_err (uptr, st);                  /* map error */
            xma = GET_EMA (mt_cu) + mt_ca;              /* restore xma */
            }
        else mt_wc = 0;                                 /* ok, clear wc */
        break;

    case FN_WREOF:
        if ((st = sim_tape_wrtmk (uptr)))               /* write tmk, err? */
            r = mt_map_err (uptr, st);                  /* map error */
        break;

    case FN_SPACEF:                                     /* space forward */
        do {
            mt_wc = (mt_wc + 1) & 07777;                /* incr wc */
            if ((st = sim_tape_sprecf (uptr, &tbc))) {  /* space rec fwd, err? */
                r = mt_map_err (uptr, st);              /* map error */
                break;                                  /* stop */
                }
            } while ((mt_wc != 0) && (passed_eot || !sim_tape_eot (uptr)));
        break;

    case FN_SPACER:                                     /* space reverse */
        do {
            mt_wc = (mt_wc + 1) & 07777;                /* incr wc */
            if ((st = sim_tape_sprecr (uptr, &tbc))) {  /* space rec rev, err? */
                r = mt_map_err (uptr, st);              /* map error */
                break;                                  /* stop */
                }
            } while (mt_wc != 0);
        break;
        }                                               /* end case */

if (!passed_eot && sim_tape_eot (uptr))                 /* just passed EOT? */
    uptr->USTAT = uptr->USTAT | STA_EOT;
mt_cu = (mt_cu & ~CU_EMA) | ((xma >> (12 - CU_V_EMA)) & CU_EMA);
mt_ca = xma & 07777;                                    /* update mem addr */
mt_set_done ();                                         /* set done */
mt_updcsta (uptr);                                      /* update status */
return r;
}

/* Update controller status */

int32 mt_updcsta (UNIT *uptr)
{
mt_sta = (mt_sta & ~(STA_DYN | STA_CLR)) | (uptr->USTAT & STA_DYN);
if (((mt_sta & STA_ERR) && (mt_cu & CU_IEE)) ||
     (mt_done && (mt_cu & CU_IED)))
     int_req = int_req | INT_MT;
else int_req = int_req & ~INT_MT;
return mt_sta;
}

/* Test if controller busy */

UNIT *mt_busy (void)
{
int32 u;
UNIT *uptr;

for (u = 0; u < MT_NUMDR; u++) {                        /* loop thru units */
    uptr = mt_dev.units + u;
    if (sim_is_active (uptr) && ((uptr->USTAT & STA_REW) == 0))
        return uptr;
    }
return NULL;
}

/* Increment extended memory address */

int32 mt_ixma (int32 xma)                               /* incr extended ma */
{
int32 v;

v = ((xma + 1) & 07777) | (xma & 070000);               /* wrapped incr */
if (mt_fn & FN_INC) {                                   /* increment mode? */
    if (xma == 077777)                                  /* at limit? error */
        mt_sta = mt_sta | STA_INC | STA_ERR;
    else v = xma + 1;                                   /* else 15b incr */
    }
return v;
}

/* Set done */

void mt_set_done (void)
{
mt_done = 1;                                            /* set done */
mt_fn = mt_fn & ~(FN_CRC | FN_GO | FN_INC);             /* clear func<4:6> */
return;
}

/* Map tape error status */

t_stat mt_map_err (UNIT *uptr, t_stat st)
{
switch (st) {

    case MTSE_FMT:                                      /* illegal fmt */
    case MTSE_UNATT:                                    /* unattached */
        mt_sta = mt_sta | STA_ILL | STA_ERR;
    case MTSE_OK:                                       /* no error */
        return SCPE_IERR;                               /* never get here! */

    case MTSE_TMK:                                      /* end of file */
        uptr->USTAT = uptr->USTAT | STA_EOF;            /* set EOF */
        mt_sta = mt_sta | STA_ERR;
        break;

    case MTSE_IOERR:                                    /* IO error */
        mt_sta = mt_sta | STA_PAR | STA_ERR;            /* set par err */
        if (mt_stopioe)
            return SCPE_IOERR;
        break;

    case MTSE_INVRL:                                    /* invalid rec lnt */
        mt_sta = mt_sta | STA_PAR | STA_ERR;            /* set par err */
        return SCPE_MTRLNT;

    case MTSE_RECE:                                     /* record in error */
    case MTSE_EOM:                                      /* end of medium */
        mt_sta = mt_sta | STA_PAR | STA_ERR;            /* set par err */
        break;

    case MTSE_BOT:                                      /* reverse into BOT */
        uptr->USTAT = uptr->USTAT | STA_BOT;            /* set status */
        mt_sta = mt_sta | STA_ERR;
        break;

    case MTSE_WRP:                                      /* write protect */
        mt_sta = mt_sta | STA_ILL | STA_ERR;            /* illegal operation */
        break;
        }

return SCPE_OK;
}

/* Reset routine */

t_stat mt_reset (DEVICE *dptr)
{
int32 u;
UNIT *uptr;

mt_cu = mt_fn = mt_wc = mt_ca = mt_db = mt_sta = mt_done = 0;
int_req = int_req & ~INT_MT;                            /* clear interrupt */
for (u = 0; u < MT_NUMDR; u++) {                        /* loop thru units */
    uptr = mt_dev.units + u;
    sim_cancel (uptr);                                  /* cancel activity */
    sim_tape_reset (uptr);                              /* reset tape */
    if (uptr->flags & UNIT_ATT) uptr->USTAT =
        (sim_tape_bot (uptr)? STA_BOT: 0) |
        (sim_tape_wrp (uptr)? STA_WLK: 0);
    else uptr->USTAT = STA_REM;
    }
if (mtxb == NULL)
    mtxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8));
if (mtxb == NULL)
    return SCPE_MEM;
return SCPE_OK;
}

/* Attach routine */

t_stat mt_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;
int32 u = uptr - mt_dev.units;                          /* get unit number */

r = sim_tape_attach (uptr, cptr);
if (r != SCPE_OK)
    return r;
uptr->USTAT = STA_BOT | (sim_tape_wrp (uptr)? STA_WLK: 0);
if (u == GET_UNIT (mt_cu))
    mt_updcsta (uptr);
return r;
}

/* Detach routine */

t_stat mt_detach (UNIT* uptr)
{
int32 u = uptr - mt_dev.units;                          /* get unit number */

if (!(uptr->flags & UNIT_ATT))                          /* check for attached */
    return SCPE_OK;
if (!sim_is_active (uptr))
    uptr->USTAT = STA_REM;
if (u == GET_UNIT (mt_cu))
    mt_updcsta (uptr);
return sim_tape_detach (uptr);
}

/* Write lock/enable routine */

t_stat mt_vlock (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 u = uptr - mt_dev.units;                          /* get unit number */

if ((uptr->flags & UNIT_ATT) && (val || sim_tape_wrp (uptr)))
    uptr->USTAT = uptr->USTAT | STA_WLK;
else uptr->USTAT = uptr->USTAT & ~STA_WLK;
if (u == GET_UNIT (mt_cu))
    mt_updcsta (uptr);
return SCPE_OK;
}
Added src/PDP8/pdp8_pt.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_pt.c: PDP-8 paper tape reader/punch simulator

   Copyright (c) 1993-2013, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   ptr,ptp      PC8E paper tape reader/punch

   17-Mar-13    RMS     Modified to use central set_bootpc routine
   25-Apr-03    RMS     Revised for extended file support
   04-Oct-02    RMS     Added DIBs
   30-May-02    RMS     Widened POS to 32b
   30-Nov-01    RMS     Added read only unit support
   30-Mar-98    RMS     Added RIM loader as PTR bootstrap
*/

#include "pdp8_defs.h"

extern int32 int_req, int_enable, dev_done, stop_inst;

int32 ptr_stopioe = 0, ptp_stopioe = 0;                 /* stop on error */

int32 ptr (int32 IR, int32 AC);
int32 ptp (int32 IR, int32 AC);
t_stat ptr_svc (UNIT *uptr);
t_stat ptp_svc (UNIT *uptr);
t_stat ptr_reset (DEVICE *dptr);
t_stat ptp_reset (DEVICE *dptr);
t_stat ptr_boot (int32 unitno, DEVICE *dptr);

/* PTR data structures

   ptr_dev      PTR device descriptor
   ptr_unit     PTR unit descriptor
   ptr_reg      PTR register list
*/

DIB ptr_dib = { DEV_PTR, 1, { &ptr } };

UNIT ptr_unit = {
    UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0),
           SERIAL_IN_WAIT
    };

REG ptr_reg[] = {
    { ORDATAD (BUF, ptr_unit.buf, 8, "last data item processed") },
    { FLDATAD (DONE, dev_done, INT_V_PTR, "device done flag") },
    { FLDATAD (ENABLE, int_enable, INT_V_PTR, "interrupt enable flag") },
    { FLDATAD (INT, int_req, INT_V_PTR, "interrupt pending flag") },
    { DRDATAD (POS, ptr_unit.pos, T_ADDR_W, "position in the input file"), PV_LEFT },
    { DRDATAD (TIME, ptr_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT },
    { FLDATAD (STOP_IOE, ptr_stopioe, 0, "stop on I/O error") },
    { NULL }
    };

MTAB ptr_mod[] = {
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev },
    { 0 }
    };

DEVICE ptr_dev = {
    "PTR", &ptr_unit, ptr_reg, ptr_mod,
    1, 10, 31, 1, 8, 8,
    NULL, NULL, &ptr_reset,
    &ptr_boot, NULL, NULL,
    &ptr_dib, 0 };

/* PTP data structures

   ptp_dev      PTP device descriptor
   ptp_unit     PTP unit descriptor
   ptp_reg      PTP register list
*/

DIB ptp_dib = { DEV_PTP, 1, { &ptp } };

UNIT ptp_unit = {
    UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT
    };

REG ptp_reg[] = {
    { ORDATAD (BUF, ptp_unit.buf, 8, "last data item processed") },
    { FLDATAD (DONE, dev_done, INT_V_PTP, "device done flag") },
    { FLDATAD (ENABLE, int_enable, INT_V_PTP, "interrupt enable flag") },
    { FLDATAD (INT, int_req, INT_V_PTP, "interrupt pending flag") },
    { DRDATAD (POS, ptp_unit.pos, T_ADDR_W, "position in the output file"), PV_LEFT },
    { DRDATAD (TIME, ptp_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT },
    { FLDATAD (STOP_IOE, ptp_stopioe, 0, "stop on I/O error") },
    { NULL }
    };

MTAB ptp_mod[] = {
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev },
    { 0 }
    };

DEVICE ptp_dev = {
    "PTP", &ptp_unit, ptp_reg, ptp_mod,
    1, 10, 31, 1, 8, 8,
    NULL, NULL, &ptp_reset,
    NULL, NULL, NULL,
    &ptp_dib, 0
    };

/* Paper tape reader: IOT routine */

int32 ptr (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 0:                                             /* RPE */
        int_enable = int_enable | (INT_PTR+INT_PTP);    /* set enable */
        int_req = INT_UPDATE;                           /* update interrupts */
        return AC;

    case 1:                                             /* RSF */
        return (dev_done & INT_PTR)? IOT_SKP + AC: AC;  

    case 6:                                             /* RFC!RRB */
        sim_activate (&ptr_unit, ptr_unit.wait);
    case 2:                                             /* RRB */
        dev_done = dev_done & ~INT_PTR;                 /* clear flag */
        int_req = int_req & ~INT_PTR;                   /* clear int req */
        return (AC | ptr_unit.buf);                     /* or data to AC */

    case 4:                                             /* RFC */
        sim_activate (&ptr_unit, ptr_unit.wait);
        dev_done = dev_done & ~INT_PTR;                 /* clear flag */
        int_req = int_req & ~INT_PTR;                   /* clear int req */
        return AC;

    default:
        return (stop_inst << IOT_V_REASON) + AC;
        }                                               /* end switch */
}

/* Unit service */

t_stat ptr_svc (UNIT *uptr)
{
int32 temp;

if ((ptr_unit.flags & UNIT_ATT) == 0)                   /* attached? */
    return IORETURN (ptr_stopioe, SCPE_UNATT);
if ((temp = getc (ptr_unit.fileref)) == EOF) {
    if (feof (ptr_unit.fileref)) {
        if (ptr_stopioe)
            sim_printf ("PTR end of file\n");
        else return SCPE_OK;
        }
    else sim_perror ("PTR I/O error");
    clearerr (ptr_unit.fileref);
    return SCPE_IOERR;
    }
dev_done = dev_done | INT_PTR;                          /* set done */
int_req = INT_UPDATE;                                   /* update interrupts */
ptr_unit.buf = temp & 0377;
ptr_unit.pos = ptr_unit.pos + 1;
return SCPE_OK;
}

/* Reset routine */

t_stat ptr_reset (DEVICE *dptr)
{
ptr_unit.buf = 0;
dev_done = dev_done & ~INT_PTR;                         /* clear done, int */
int_req = int_req & ~INT_PTR;
int_enable = int_enable | INT_PTR;                      /* set enable */
sim_cancel (&ptr_unit);                                 /* deactivate unit */
return SCPE_OK;
}

/* Paper tape punch: IOT routine */

int32 ptp (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 0:                                             /* PCE */
        int_enable = int_enable & ~(INT_PTR+INT_PTP);   /* clear enables */
        int_req = INT_UPDATE;                           /* update interrupts */
        return AC;

    case 1:                                             /* PSF */
        return (dev_done & INT_PTP)? IOT_SKP + AC: AC;

    case 2:                                             /* PCF */
        dev_done = dev_done & ~INT_PTP;                 /* clear flag */
        int_req = int_req & ~INT_PTP;                   /* clear int req */
        return AC;

    case 6:                                             /* PLS */
        dev_done = dev_done & ~INT_PTP;                 /* clear flag */
        int_req = int_req & ~INT_PTP;                   /* clear int req */
    case 4:                                             /* PPC */
        ptp_unit.buf = AC & 0377;                       /* load punch buf */
        sim_activate (&ptp_unit, ptp_unit.wait);        /* activate unit */
        return AC;

    default:
        return (stop_inst << IOT_V_REASON) + AC;
        }                                               /* end switch */
}

/* Unit service */

t_stat ptp_svc (UNIT *uptr)
{
dev_done = dev_done | INT_PTP;                          /* set done */
int_req = INT_UPDATE;                                   /* update interrupts */
if ((ptp_unit.flags & UNIT_ATT) == 0)                   /* attached? */
    return IORETURN (ptp_stopioe, SCPE_UNATT);
if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) {
    sim_perror ("PTP I/O error");
    clearerr (ptp_unit.fileref);
    return SCPE_IOERR;
    }
ptp_unit.pos = ptp_unit.pos + 1;
return SCPE_OK;
}

/* Reset routine */

t_stat ptp_reset (DEVICE *dptr)
{
ptp_unit.buf = 0;
dev_done = dev_done & ~INT_PTP;                         /* clear done, int */
int_req = int_req & ~INT_PTP;
int_enable = int_enable | INT_PTP;                      /* set enable */
sim_cancel (&ptp_unit);                                 /* deactivate unit */
return SCPE_OK;
}

/* Bootstrap routine */

#define BOOT_START 07756
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))

static const uint16 boot_rom[] = {
    06014,                      /* 7756, RFC */
    06011,                      /* 7757, LOOP, RSF */
    05357,                      /* JMP .-1 */
    06016,                      /* RFC RRB */
    07106,                      /* CLL RTL*/
    07006,                      /* RTL */
    07510,                      /* SPA*/
    05374,                      /* JMP 7774 */
    07006,                      /* RTL */
    06011,                      /* RSF */
    05367,                      /* JMP .-1 */
    06016,                      /* RFC RRB */
    07420,                      /* SNL */
    03776,                      /* DCA I 7776 */
    03376,                      /* 7774, DCA 7776 */
    05357,                      /* JMP 7757 */
    00000,                      /* 7776, 0 */
    05301                       /* 7777, JMP 7701 */
    };

t_stat ptr_boot (int32 unitno, DEVICE *dptr)
{
size_t i;
extern uint16 M[];

if (ptr_dib.dev != DEV_PTR)                             /* only std devno */
    return STOP_NOTSTD;
for (i = 0; i < BOOT_LEN; i++)
    M[BOOT_START + i] = boot_rom[i];
cpu_set_bootpc (BOOT_START);
return SCPE_OK;
}
Added src/PDP8/pdp8_rf.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_rf.c: RF08 fixed head disk simulator

   Copyright (c) 1993-2013, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   rf           RF08 fixed head disk

   17-Sep-13    RMS     Changed to use central set_bootpc routine
   03-Sep-13    RMS     Added explicit void * cast
   15-May-06    RMS     Fixed bug in autosize attach (Dave Gesswein)
   07-Jan-06    RMS     Fixed unaligned register access bug (Doug Carman)
   04-Jan-04    RMS     Changed sim_fsize calling sequence
   26-Oct-03    RMS     Cleaned up buffer copy code
   26-Jul-03    RMS     Fixed bug in set size routine
   14-Mar-03    RMS     Fixed variable platter interaction with save/restore
   03-Mar-03    RMS     Fixed autosizing
   02-Feb-03    RMS     Added variable platter and autosizing support
   04-Oct-02    RMS     Added DIB, device number support
   28-Nov-01    RMS     Added RL8A support
   25-Apr-01    RMS     Added device enable/disable support
   19-Mar-01    RMS     Added disk monitor bootstrap, fixed IOT decoding
   15-Feb-01    RMS     Fixed 3 cycle data break sequence
   14-Apr-99    RMS     Changed t_addr to unsigned
   30-Mar-98    RMS     Fixed bug in RF bootstrap

   The RF08 is a head-per-track disk.  It uses the three cycle data break
   facility.  To minimize overhead, the entire RF08 is buffered in memory.

   Two timing parameters are provided:

   rf_time      Interword timing, must be non-zero
   rf_burst     Burst mode, if 0, DMA occurs cycle by cycle; otherwise,
                DMA occurs in a burst
*/

#include "pdp8_defs.h"
#include <math.h>

#define UNIT_V_AUTO     (UNIT_V_UF + 0)                 /* autosize */
#define UNIT_V_PLAT     (UNIT_V_UF + 1)                 /* #platters - 1 */
#define UNIT_M_PLAT     03
#define UNIT_GETP(x)    ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1)
#define UNIT_AUTO       (1 << UNIT_V_AUTO)
#define UNIT_PLAT       (UNIT_M_PLAT << UNIT_V_PLAT)

/* Constants */

#define RF_NUMWD        2048                            /* words/track */
#define RF_NUMTR        128                             /* tracks/disk */
#define RF_DKSIZE       (RF_NUMTR * RF_NUMWD)           /* words/disk */
#define RF_NUMDK        4                               /* disks/controller */
#define RF_WC           07750                           /* word count */
#define RF_MA           07751                           /* mem address */
#define RF_WMASK        (RF_NUMWD - 1)                  /* word mask */

/* Parameters in the unit descriptor */

#define FUNC            u4                              /* function */
#define RF_READ         2                               /* read */
#define RF_WRITE        4                               /* write */

/* Status register */

#define RFS_PCA         04000                           /* photocell status */
#define RFS_DRE         02000                           /* data req enable */
#define RFS_WLS         01000                           /* write lock status */
#define RFS_EIE         00400                           /* error int enable */
#define RFS_PIE         00200                           /* photocell int enb */
#define RFS_CIE         00100                           /* done int enable */
#define RFS_MEX         00070                           /* memory extension */
#define RFS_DRL         00004                           /* data late error */
#define RFS_NXD         00002                           /* non-existent disk */
#define RFS_PER         00001                           /* parity error */
#define RFS_ERR         (RFS_WLS + RFS_DRL + RFS_NXD + RFS_PER)
#define RFS_V_MEX       3

#define GET_MEX(x)      (((x) & RFS_MEX) << (12 - RFS_V_MEX))
#define GET_POS(x)      ((int) fmod (sim_gtime() / ((double) (x)), \
                        ((double) RF_NUMWD)))
#define UPDATE_PCELL    if (GET_POS(rf_time) < 6) rf_sta = rf_sta | RFS_PCA; \
                        else rf_sta = rf_sta & ~RFS_PCA
#define RF_INT_UPDATE   if ((rf_done && (rf_sta & RFS_CIE)) || \
                            ((rf_sta & RFS_ERR) && (rf_sta & RFS_EIE)) || \
                            ((rf_sta & RFS_PCA) && (rf_sta & RFS_PIE))) \
                            int_req = int_req | INT_RF; \
                        else int_req = int_req & ~INT_RF

extern uint16 M[];
extern int32 int_req, stop_inst;
extern UNIT cpu_unit;

int32 rf_sta = 0;                                       /* status register */
int32 rf_da = 0;                                        /* disk address */
int32 rf_done = 0;                                      /* done flag */
int32 rf_wlk = 0;                                       /* write lock */
int32 rf_time = 10;                                     /* inter-word time */
int32 rf_burst = 1;                                     /* burst mode flag */
int32 rf_stopioe = 1;                                   /* stop on error */

int32 rf60 (int32 IR, int32 AC);
int32 rf61 (int32 IR, int32 AC);
int32 rf62 (int32 IR, int32 AC);
int32 rf64 (int32 IR, int32 AC);
t_stat rf_svc (UNIT *uptr);
t_stat pcell_svc (UNIT *uptr);
t_stat rf_reset (DEVICE *dptr);
t_stat rf_boot (int32 unitno, DEVICE *dptr);
t_stat rf_attach (UNIT *uptr, CONST char *cptr);
t_stat rf_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc);

/* RF08 data structures

   rf_dev       RF device descriptor
   rf_unit      RF unit descriptor
   pcell_unit   photocell timing unit (orphan)
   rf_reg       RF register list
*/

DIB rf_dib = { DEV_RF, 5, { &rf60, &rf61, &rf62, NULL, &rf64 } };

UNIT rf_unit = {
    UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+
           UNIT_BUFABLE+UNIT_MUSTBUF, RF_DKSIZE)
    };

UNIT pcell_unit = { UDATA (&pcell_svc, 0, 0) };

REG rf_reg[] = {
    { ORDATAD (STA, rf_sta, 12, "status") },
    { ORDATAD (DA, rf_da, 20, "low order disk address") },
    { ORDATAD (WC, M[RF_WC], 12, "word count (in memory)"), REG_FIT },
    { ORDATAD (MA, M[RF_MA], 12, "memory address (in memory)"), REG_FIT },
    { FLDATAD (DONE, rf_done, 0, "device done flag") },
    { FLDATAD (INT, int_req, INT_V_RF, "interrupt pending flag") },
    { ORDATAD (WLK, rf_wlk, 32, "write lock switches") },
    { DRDATAD (TIME, rf_time, 24, "rotational delay, per word"), REG_NZ + PV_LEFT },
    { FLDATAD (BURST, rf_burst, 0, "burst flag") },
    { FLDATAD (STOP_IOE, rf_stopioe, 0, "stop on I/O error") },
    { DRDATA (CAPAC, rf_unit.capac, 21), REG_HRO },
    { ORDATA (DEVNUM, rf_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB rf_mod[] = {
    { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size },
    { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size },
    { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size },
    { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size },
    { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { 0 }
    };

DEVICE rf_dev = {
    "RF", &rf_unit, rf_reg, rf_mod,
    1, 8, 20, 1, 8, 12,
    NULL, NULL, &rf_reset,
    &rf_boot, &rf_attach, NULL,
    &rf_dib, DEV_DISABLE | DEV_DIS
    };

/* IOT routines */

int32 rf60 (int32 IR, int32 AC)
{
int32 t;
int32 pulse = IR & 07;

UPDATE_PCELL;                                           /* update photocell */
if (pulse & 1) {                                        /* DCMA */
    rf_da = rf_da & ~07777;                             /* clear DAR<8:19> */
    rf_done = 0;                                        /* clear done */
    rf_sta = rf_sta & ~RFS_ERR;                         /* clear errors */
    RF_INT_UPDATE;                                      /* update int req */
    }
if (pulse & 6) {                                        /* DMAR, DMAW */
    rf_da = rf_da | AC;                                 /* DAR<8:19> |= AC */
    rf_unit.FUNC = pulse & ~1;                          /* save function */
    t = (rf_da & RF_WMASK) - GET_POS (rf_time);         /* delta to new loc */
    if (t < 0)                                          /* wrap around? */
        t = t + RF_NUMWD;
    sim_activate (&rf_unit, t * rf_time);               /* schedule op */
    AC = 0;                                             /* clear AC */
    }
return AC;
}

int32 rf61 (int32 IR, int32 AC)
{
int32 pulse = IR & 07;

UPDATE_PCELL;                                           /* update photocell */
switch (pulse) {                                        /* decode IR<9:11> */

    case 1:                                             /* DCIM */
        rf_sta = rf_sta & 07007;                        /* clear STA<3:8> */
        int_req = int_req & ~INT_RF;                    /* clear int req */
        sim_cancel (&pcell_unit);                       /* cancel photocell */
        return AC;

    case 2:                                             /* DSAC */
        return ((rf_da & RF_WMASK) == GET_POS (rf_time))? IOT_SKP: 0;

    case 5:                                             /* DIML */
        rf_sta = (rf_sta & 07007) | (AC & 0770);        /* STA<3:8> <- AC */
        if (rf_sta & RFS_PIE)                           /* photocell int? */
            sim_activate (&pcell_unit, (RF_NUMWD - GET_POS (rf_time)) *
                rf_time);
        else sim_cancel (&pcell_unit);
        RF_INT_UPDATE;                                  /* update int req */
        return 0;                                       /* clear AC */

    case 6:                                             /* DIMA */
        return rf_sta;                                  /* AC <- STA<0:11> */
        }

return AC;
}

int32 rf62 (int32 IR, int32 AC)
{
int32 pulse = IR & 07;

UPDATE_PCELL;                                           /* update photocell */
if (pulse & 1) {                                        /* DFSE */
    if (rf_sta & RFS_ERR)
        AC = AC | IOT_SKP;
    }
if (pulse & 2) {                                        /* DFSC */
    if (pulse & 4)                                      /* for DMAC */
        AC = AC & ~07777;
    else if (rf_done)
        AC = AC | IOT_SKP;
    }
if (pulse & 4)                                          /* DMAC */
    AC = AC | (rf_da & 07777);
return AC;
}

int32 rf64 (int32 IR, int32 AC)
{
int32 pulse = IR & 07;

UPDATE_PCELL;                                           /* update photocell */
switch (pulse) {                                        /* decode IR<9:11> */

    case 1:                                             /* DCXA */
        rf_da = rf_da & 07777;                          /* clear DAR<0:7> */
        break;

    case 3:                                             /* DXAL */
        rf_da = rf_da & 07777;                          /* clear DAR<0:7> */
    case 2:                                             /* DXAL w/o clear */
        rf_da = rf_da | ((AC & 0377) << 12);            /* DAR<0:7> |= AC */
        AC = 0;                                         /* clear AC */
        break;

    case 5:                                             /* DXAC */
        AC = 0;                                         /* clear AC */
    case 4:                                             /* DXAC w/o clear */
        AC = AC | ((rf_da >> 12) & 0377);               /* AC |= DAR<0:7> */
        break;

    default:
        AC = (stop_inst << IOT_V_REASON) + AC;
        break;
        }                                               /* end switch */

if ((uint32) rf_da >= rf_unit.capac)
    rf_sta = rf_sta | RFS_NXD;
else rf_sta = rf_sta & ~RFS_NXD;
RF_INT_UPDATE;
return AC;
}

/* Unit service

   Note that for reads and writes, memory addresses wrap around in the
   current field.  This code assumes the entire disk is buffered.
*/

t_stat rf_svc (UNIT *uptr)
{
int32 pa, t, mex;
int16 *fbuf = (int16 *) uptr->filebuf;

UPDATE_PCELL;                                           /* update photocell */
if ((uptr->flags & UNIT_BUF) == 0) {                    /* not buf? abort */
    rf_sta = rf_sta | RFS_NXD;
    rf_done = 1;
    RF_INT_UPDATE;                                      /* update int req */
    return IORETURN (rf_stopioe, SCPE_UNATT);
    }

mex = GET_MEX (rf_sta);
do {
    if ((uint32) rf_da >= rf_unit.capac) {              /* disk overflow? */
        rf_sta = rf_sta | RFS_NXD;
        break;
        }
    M[RF_WC] = (M[RF_WC] + 1) & 07777;                  /* incr word count */
    M[RF_MA] = (M[RF_MA] + 1) & 07777;                  /* incr mem addr */
    pa = mex | M[RF_MA];                                /* add extension */
    if (uptr->FUNC == RF_READ) {                        /* read? */
        if (MEM_ADDR_OK (pa))                           /* if !nxm */
            M[pa] = fbuf[rf_da];                        /* read word */
        }
    else {                                              /* write */
        t = ((rf_da >> 15) & 030) | ((rf_da >> 14) & 07);
        if ((rf_wlk >> t) & 1)                          /* write locked? */
            rf_sta = rf_sta | RFS_WLS;
        else {                                          /* not locked */
            fbuf[rf_da] = M[pa];                        /* write word */
            if (((uint32) rf_da) >= uptr->hwmark)
                uptr->hwmark = rf_da + 1;
            }
        }
    rf_da = (rf_da + 1) & 03777777;                     /* incr disk addr */
    } while ((M[RF_WC] != 0) && (rf_burst != 0));       /* brk if wc, no brst */

if ((M[RF_WC] != 0) && ((rf_sta & RFS_ERR) == 0))       /* more to do? */
    sim_activate (&rf_unit, rf_time);                   /* sched next */
else {
    rf_done = 1;                                        /* done */
    RF_INT_UPDATE;                                      /* update int req */
    }
return SCPE_OK;
}

/* Photocell unit service */

t_stat pcell_svc (UNIT *uptr)
{
rf_sta = rf_sta | RFS_PCA;                              /* set photocell */
if (rf_sta & RFS_PIE) {                                 /* int enable? */
    sim_activate (&pcell_unit, RF_NUMWD * rf_time);
    int_req = int_req | INT_RF;
    }
return SCPE_OK;
}

/* Reset routine */

t_stat rf_reset (DEVICE *dptr)
{
rf_sta = rf_da = 0;
rf_done = 1;
int_req = int_req & ~INT_RF;                            /* clear interrupt */
sim_cancel (&rf_unit);
sim_cancel (&pcell_unit);
return SCPE_OK;
}

/* Bootstrap routine */

#define OS8_START       07750
#define OS8_LEN         (sizeof (os8_rom) / sizeof (int16))
#define DM4_START       00200
#define DM4_LEN         (sizeof (dm4_rom) / sizeof (int16))

static const uint16 os8_rom[] = {
    07600,                      /* 7750, CLA CLL        ; also word count */
    06603,                      /* 7751, DMAR           ; also address */
    06622,                      /* 7752, DFSC           ; done? */
    05352,                      /* 7753, JMP .-1        ; no */
    05752                       /* 7754, JMP @.-2       ; enter boot */
    };

static const uint16 dm4_rom[] = {
    00200, 07600,               /* 0200, CLA CLL */
    00201, 06603,               /* 0201, DMAR           ; read */
    00202, 06622,               /* 0202, DFSC           ; done? */
    00203, 05202,               /* 0203, JMP .-1        ; no */
    00204, 05600,               /* 0204, JMP @.-4       ; enter boot */
    07750, 07576,               /* 7750, 7576           ; word count */
    07751, 07576                /* 7751, 7576           ; address */
    };

t_stat rf_boot (int32 unitno, DEVICE *dptr)
{
size_t i;

if (rf_dib.dev != DEV_RF)                               /* only std devno */
    return STOP_NOTSTD;
if (sim_switches & SWMASK ('D')) {
    for (i = 0; i < DM4_LEN; i = i + 2)
        M[dm4_rom[i]] = dm4_rom[i + 1];
    cpu_set_bootpc (DM4_START);
    }
else {
    for (i = 0; i < OS8_LEN; i++)
        M[OS8_START + i] = os8_rom[i];
    cpu_set_bootpc (OS8_START);
    }
return SCPE_OK;
}

/* Attach routine */

t_stat rf_attach (UNIT *uptr, CONST char *cptr)
{
uint32 sz, p;
uint32 ds_bytes = RF_DKSIZE * sizeof (int16);

if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) {
    p = (sz + ds_bytes - 1) / ds_bytes;
    if (p >= RF_NUMDK)
        p = RF_NUMDK - 1;
    uptr->flags = (uptr->flags & ~UNIT_PLAT) |
        (p << UNIT_V_PLAT);
    }
uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE;
return attach_unit (uptr, cptr);
}

/* Change disk size */

t_stat rf_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if (val < 0)
    return SCPE_IERR;
if (uptr->flags & UNIT_ATT)
    return SCPE_ALATT;
uptr->capac = UNIT_GETP (val) * RF_DKSIZE;
uptr->flags = uptr->flags & ~UNIT_AUTO;
return SCPE_OK;
}
Added src/PDP8/pdp8_rk.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_rk.c: RK8E cartridge disk simulator

   Copyright (c) 1993-2013, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   rk           RK8E/RK05 cartridge disk

   17-Sep-13    RMS     Changed to use central set_bootpc routine
   18-Mar-13    RMS     Raised RK_MIN so that RKLFMT will work (Mark Pizzolato)
   25-Apr-03    RMS     Revised for extended file support
   04-Oct-02    RMS     Added DIB, device number support
   06-Jan-02    RMS     Changed enable/disable support
   30-Nov-01    RMS     Added read only unit, extended SET/SHOW support
   24-Nov-01    RMS     Converted FLG to array, made register names consistent
   25-Apr-01    RMS     Added device enable/disable support
   29-Jun-96    RMS     Added unit enable/disable support
*/

#include "pdp8_defs.h"

/* Constants */

#define RK_NUMSC        16                              /* sectors/surface */
#define RK_NUMSF        2                               /* surfaces/cylinder */
#define RK_NUMCY        203                             /* cylinders/drive */
#define RK_NUMWD        256                             /* words/sector */
#define RK_SIZE         (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD)
                                                        /* words/drive */
#define RK_NUMDR        4                               /* drives/controller */
#define RK_M_NUMDR      03

/* Flags in the unit flags word */

#define UNIT_V_HWLK     (UNIT_V_UF + 0)                 /* hwre write lock */
#define UNIT_V_SWLK     (UNIT_V_UF + 1)                 /* swre write lock */
#define UNIT_HWLK       (1 << UNIT_V_HWLK)
#define UNIT_SWLK       (1 << UNIT_V_SWLK)
#define UNIT_WPRT       (UNIT_HWLK|UNIT_SWLK|UNIT_RO)   /* write protect */

/* Parameters in the unit descriptor */

#define CYL             u3                              /* current cylinder */
#define FUNC            u4                              /* function */

/* Status register */

#define RKS_DONE        04000                           /* transfer done */
#define RKS_HMOV        02000                           /* heads moving */
#define RKS_SKFL        00400                           /* drive seek fail */
#define RKS_NRDY        00200                           /* drive not ready */
#define RKS_BUSY        00100                           /* control busy error */
#define RKS_TMO         00040                           /* timeout error */
#define RKS_WLK         00020                           /* write lock error */
#define RKS_CRC         00010                           /* CRC error */
#define RKS_DLT         00004                           /* data late error */
#define RKS_STAT        00002                           /* drive status error */
#define RKS_CYL         00001                           /* cyl address error */
#define RKS_ERR         (RKS_BUSY+RKS_TMO+RKS_WLK+RKS_CRC+RKS_DLT+RKS_STAT+RKS_CYL)

/* Command register */

#define RKC_M_FUNC      07                              /* function */
#define  RKC_READ       0
#define  RKC_RALL       1
#define  RKC_WLK        2
#define  RKC_SEEK       3
#define  RKC_WRITE      4
#define  RKC_WALL       5
#define RKC_V_FUNC      9
#define RKC_IE          00400                           /* interrupt enable */
#define RKC_SKDN        00200                           /* set done on seek done */
#define RKC_HALF        00100                           /* 128W sector */
#define RKC_MEX         00070                           /* memory extension */
#define RKC_V_MEX       3
#define RKC_M_DRV       03                              /* drive select */
#define RKC_V_DRV       1
#define RKC_CYHI        00001                           /* high cylinder addr */

#define GET_FUNC(x)     (((x) >> RKC_V_FUNC) & RKC_M_FUNC)
#define GET_DRIVE(x)    (((x) >> RKC_V_DRV) & RKC_M_DRV)
#define GET_MEX(x)      (((x) & RKC_MEX) << (12 - RKC_V_MEX))

/* Disk address */

#define RKD_V_SECT      0                               /* sector */
#define RKD_M_SECT      017
#define RKD_V_SUR       4                               /* surface */
#define RKD_M_SUR       01
#define RKD_V_CYL       5                               /* cylinder */
#define RKD_M_CYL       0177
#define GET_CYL(x,y)    ((((x) & RKC_CYHI) << (12-RKD_V_CYL)) | \
                        (((y) >> RKD_V_CYL) & RKD_M_CYL))
#define GET_DA(x,y)     ((((x) & RKC_CYHI) << 12) | y)

/* Reset commands */

#define RKX_CLS         0                               /* clear status */
#define RKX_CLC         1                               /* clear control */
#define RKX_CLD         2                               /* clear drive */
#define RKX_CLSA        3                               /* clear status alt */

#define RK_INT_UPDATE   if (((rk_sta & (RKS_DONE + RKS_ERR)) != 0) && \
                            ((rk_cmd & RKC_IE) != 0)) \
                            int_req = int_req | INT_RK; \
                        else int_req = int_req & ~INT_RK
#define RK_MIN          50
#define MAX(x,y)        (((x) > (y))? (x): (y))

extern uint16 M[];
extern int32 int_req, stop_inst;
extern UNIT cpu_unit;

int32 rk_busy = 0;                                      /* controller busy */
int32 rk_sta = 0;                                       /* status register */
int32 rk_cmd = 0;                                       /* command register */
int32 rk_da = 0;                                        /* disk address */
int32 rk_ma = 0;                                        /* memory address */
int32 rk_swait = 10, rk_rwait = 10;                     /* seek, rotate wait */
int32 rk_stopioe = 1;                                   /* stop on error */

int32 rk (int32 IR, int32 AC);
t_stat rk_svc (UNIT *uptr);
t_stat rk_reset (DEVICE *dptr);
t_stat rk_boot (int32 unitno, DEVICE *dptr);
void rk_go (int32 function, int32 cylinder);

/* RK-8E data structures

   rk_dev       RK device descriptor
   rk_unit      RK unit list
   rk_reg       RK register list
   rk_mod       RK modifiers list
*/

DIB rk_dib = { DEV_RK, 1, { &rk } };

UNIT rk_unit[] = {
    { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
             UNIT_ROABLE, RK_SIZE) },
    { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
             UNIT_ROABLE, RK_SIZE) },
    { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
             UNIT_ROABLE, RK_SIZE) },
    { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
             UNIT_ROABLE, RK_SIZE) }
    };

REG rk_reg[] = {
    { ORDATAD (RKSTA, rk_sta, 12, "status") },
    { ORDATAD (RKCMD, rk_cmd, 12, "disk command") },
    { ORDATAD (RKDA, rk_da, 12, "disk address") },
    { ORDATAD (RKMA, rk_ma, 12, "current memory address") },
    { FLDATAD (BUSY, rk_busy, 0, "control busy flag") },
    { FLDATAD (INT, int_req, INT_V_RK, "interrupt pending flag") },
    { DRDATAD (STIME, rk_swait, 24, "seek time, per cylinder"), PV_LEFT },
    { DRDATAD (RTIME, rk_rwait, 24, "rotational delay"), PV_LEFT },
    { FLDATAD (STOP_IOE, rk_stopioe, 0, "stop on I/O error") },
    { ORDATA (DEVNUM, rk_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB rk_mod[] = {
    { UNIT_HWLK, 0, "write enabled", "WRITEENABLED", NULL },
    { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { 0 }
    };

DEVICE rk_dev = {
    "RK", rk_unit, rk_reg, rk_mod,
    RK_NUMDR, 8, 24, 1, 8, 12,
    NULL, NULL, &rk_reset,
    &rk_boot, NULL, NULL,
    &rk_dib, DEV_DISABLE
    };

/* IOT routine */

int32 rk (int32 IR, int32 AC)
{
int32 i;
UNIT *uptr;

switch (IR & 07) {                                      /* decode IR<9:11> */

    case 0:                                             /* unused */
        return (stop_inst << IOT_V_REASON) + AC;

    case 1:                                             /* DSKP */
        return (rk_sta & (RKS_DONE + RKS_ERR))?         /* skip on done, err */
            IOT_SKP + AC: AC;

    case 2:                                             /* DCLR */
        rk_sta = 0;                                     /* clear status */
        switch (AC & 03) {                              /* decode AC<10:11> */

        case RKX_CLS:                                   /* clear status */
            if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY;
        case RKX_CLSA:                                  /* clear status alt */
            break;

        case RKX_CLC:                                   /* clear control */
            rk_cmd = rk_busy = 0;                       /* clear registers */
            rk_ma = rk_da = 0;
            for (i = 0; i < RK_NUMDR; i++)
                sim_cancel (&rk_unit[i]);
            break;

        case RKX_CLD:                                   /* reset drive */
            if (rk_busy != 0)
                rk_sta = rk_sta | RKS_BUSY;
            else rk_go (RKC_SEEK, 0);                   /* seek to 0 */
            break;
            }                                           /* end switch AC */
        break;

    case 3:                                             /* DLAG */
        if (rk_busy != 0)
            rk_sta = rk_sta | RKS_BUSY;
        else {
            rk_da = AC;                                 /* load disk addr */
            rk_go (GET_FUNC (rk_cmd), GET_CYL (rk_cmd, rk_da));
            }
        break;

    case 4:                                             /* DLCA */
        if (rk_busy != 0)
            rk_sta = rk_sta | RKS_BUSY;
        else rk_ma = AC;                                /* load curr addr */
        break;

    case 5:                                             /* DRST */
        uptr = rk_dev.units + GET_DRIVE (rk_cmd);       /* selected unit */
        rk_sta = rk_sta & ~(RKS_HMOV + RKS_NRDY);       /* clear dynamic */
        if ((uptr->flags & UNIT_ATT) == 0)
            rk_sta = rk_sta | RKS_NRDY;
        if (sim_is_active (uptr))
            rk_sta = rk_sta | RKS_HMOV;
        return rk_sta;

    case 6:                                             /* DLDC */
        if (rk_busy != 0)
            rk_sta = rk_sta | RKS_BUSY;
        else {
            rk_cmd = AC;                                /* load command */
            rk_sta = 0;                                 /* clear status */
            }
        break;

    case 7:                                             /* DMAN */
        break;
        }                                               /* end case pulse */

RK_INT_UPDATE;                                          /* update int req */
return 0;                                               /* clear AC */
}

/* Initiate new function

   Called with function, cylinder, to allow recalibrate as well as
   load and go to be processed by this routine.

   Assumes that the controller is idle, and that updating of interrupt
   request will be done by the caller.
*/

void rk_go (int32 func, int32 cyl)
{
int32 t;
UNIT *uptr;

if (func == RKC_RALL)                                   /* all? use standard */
    func = RKC_READ;
if (func == RKC_WALL)
func = RKC_WRITE;
uptr = rk_dev.units + GET_DRIVE (rk_cmd);               /* selected unit */
if ((uptr->flags & UNIT_ATT) == 0) {                    /* not attached? */
    rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT;
    return;
    }
if (sim_is_active (uptr) || (cyl >= RK_NUMCY)) {        /* busy or bad cyl? */
    rk_sta = rk_sta | RKS_DONE | RKS_STAT;
    return;
    }
if ((func == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) {
    rk_sta = rk_sta | RKS_DONE | RKS_WLK;               /* write and locked? */
    return;
    }
if (func == RKC_WLK) {                                  /* write lock? */
    uptr->flags = uptr->flags | UNIT_SWLK;
    rk_sta = rk_sta | RKS_DONE;
    return;
    }
t = abs (cyl - uptr->CYL) * rk_swait;                   /* seek time */
if (func == RKC_SEEK) {                                 /* seek? */
    sim_activate (uptr, MAX (RK_MIN, t));               /* schedule */
    rk_sta = rk_sta | RKS_DONE;                         /* set done */
    }
else {
    sim_activate (uptr, t + rk_rwait);                  /* schedule */
    rk_busy = 1;                                        /* set busy */
    }
uptr->FUNC = func;                                      /* save func */
uptr->CYL = cyl;                                        /* put on cylinder */
return;
}

/* Unit service

   If seek, complete seek command
   Else complete data transfer command

   The unit control block contains the function and cylinder address for
   the current command.

   Note that memory addresses wrap around in the current field.
*/

static uint16 fill[RK_NUMWD/2] = { 0 };
t_stat rk_svc (UNIT *uptr)
{
int32 err, wc, wc1, awc, swc, pa, da;
UNIT *seluptr;

if (uptr->FUNC == RKC_SEEK) {                           /* seek? */
    seluptr = rk_dev.units + GET_DRIVE (rk_cmd);        /* see if selected */
    if ((uptr == seluptr) && ((rk_cmd & RKC_SKDN) != 0)) {
        rk_sta = rk_sta | RKS_DONE;
        RK_INT_UPDATE;
        }
    return SCPE_OK;
    }

if ((uptr->flags & UNIT_ATT) == 0) {                    /* not att? abort */
    rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT;
    rk_busy = 0;
    RK_INT_UPDATE;
    return IORETURN (rk_stopioe, SCPE_UNATT);
    }

if ((uptr->FUNC == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) {
    rk_sta = rk_sta | RKS_DONE | RKS_WLK;               /* write and locked? */
    rk_busy = 0;
    RK_INT_UPDATE;
    return SCPE_OK;
    }

pa = GET_MEX (rk_cmd) | rk_ma;                          /* phys address */
da = GET_DA (rk_cmd, rk_da) * RK_NUMWD * sizeof (int16);/* disk address */
swc = wc = (rk_cmd & RKC_HALF)? RK_NUMWD / 2: RK_NUMWD; /* get transfer size */
if ((wc1 = ((rk_ma + wc) - 010000)) > 0)                /* if wrap, limit */
    wc = wc - wc1;
err = fseek (uptr->fileref, da, SEEK_SET);              /* locate sector */

if ((uptr->FUNC == RKC_READ) && (err == 0) && MEM_ADDR_OK (pa)) { /* read? */
    awc = fxread (&M[pa], sizeof (int16), wc, uptr->fileref);
    for ( ; awc < wc; awc++)                            /* fill if eof */
        M[pa + awc] = 0;
    err = ferror (uptr->fileref);
    if ((wc1 > 0) && (err == 0))  {                     /* field wraparound? */
        pa = pa & 070000;                               /* wrap phys addr */
        awc = fxread (&M[pa], sizeof (int16), wc1, uptr->fileref);
        for ( ; awc < wc1; awc++)                       /* fill if eof */
            M[pa + awc] = 0;
        err = ferror (uptr->fileref);
        }
    }

if ((uptr->FUNC == RKC_WRITE) && (err == 0)) {          /* write? */
    fxwrite (&M[pa], sizeof (int16), wc, uptr->fileref);
    err = ferror (uptr->fileref);
    if ((wc1 > 0) && (err == 0)) {                      /* field wraparound? */
        pa = pa & 070000;                               /* wrap phys addr */
        fxwrite (&M[pa], sizeof (int16), wc1, uptr->fileref);
        err = ferror (uptr->fileref);
        }
    if ((rk_cmd & RKC_HALF) && (err == 0)) {            /* fill half sector */
        fxwrite (fill, sizeof (int16), RK_NUMWD/2, uptr->fileref);
        err = ferror (uptr->fileref);
        }
    }

rk_ma = (rk_ma + swc) & 07777;                          /* incr mem addr reg */
rk_sta = rk_sta | RKS_DONE;                             /* set done */
rk_busy = 0;
RK_INT_UPDATE;

if (err != 0) {
    sim_perror ("RK I/O error");
    clearerr (uptr->fileref);
    return SCPE_IOERR;
    }
return SCPE_OK;
}

/* Reset routine */

t_stat rk_reset (DEVICE *dptr)
{
int32 i;
UNIT *uptr;

rk_cmd = rk_ma = rk_da = rk_sta = rk_busy = 0;
int_req = int_req & ~INT_RK;                            /* clear interrupt */
for (i = 0; i < RK_NUMDR; i++) {                        /* stop all units */
    uptr = rk_dev.units + i;
    sim_cancel (uptr);
    uptr->flags = uptr->flags & ~UNIT_SWLK;
    uptr->CYL = uptr->FUNC = 0;
    }
return SCPE_OK;
}

/* Bootstrap routine */

#define BOOT_START 023
#define BOOT_UNIT 032
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))

static const uint16 boot_rom[] = {
    06007,                      /* 23, CAF */
    06744,                      /* 24, DLCA             ; addr = 0 */
    01032,                      /* 25, TAD UNIT         ; unit no */
    06746,                      /* 26, DLDC             ; command, unit */
    06743,                      /* 27, DLAG             ; disk addr, go */
    01032,                      /* 30, TAD UNIT         ; unit no, for OS */
    05031,                      /* 31, JMP . */
    00000                       /* UNIT, 0              ; in bits <9:10> */
    };

t_stat rk_boot (int32 unitno, DEVICE *dptr)
{
size_t i;

if (rk_dib.dev != DEV_RK)                               /* only std devno */
    return STOP_NOTSTD;
for (i = 0; i < BOOT_LEN; i++)
    M[BOOT_START + i] = boot_rom[i];
M[BOOT_UNIT] = (unitno & RK_M_NUMDR) << 1;
cpu_set_bootpc (BOOT_START);
return SCPE_OK;
}
Added src/PDP8/pdp8_rl.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_rl.c: RL8A cartridge disk simulator

   Copyright (c) 1993-2013, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   rl           RL8A cartridge disk

   17-Sep-13    RMS     Changed to use central set_bootpc routine
   25-Oct-05    RMS     Fixed IOT 61 decode bug (David Gesswein)
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   04-Jan-04    RMS     Changed attach routine to use sim_fsize
   25-Apr-03    RMS     Revised for extended file support
   04-Oct-02    RMS     Added DIB, device number support
   06-Jan-02    RMS     Changed enable/disable support
   30-Nov-01    RMS     Cloned from RL11

   The RL8A is a four drive cartridge disk subsystem.  An RL01 drive
   consists of 256 cylinders, each with 2 surfaces containing 40 sectors
   of 256 bytes.  An RL02 drive has 512 cylinders.

   The RL8A controller has several serious complications.
   - Seeking is relative to the current disk address; this requires
     keeping accurate track of the current cylinder.
   - The RL8A will not switch heads or cross cylinders during transfers.
   - The RL8A operates in 8b and 12b mode, like the RX8E; in 12b mode, it
     packs 2 12b words into 3 bytes, creating a 170 "word" sector with
     one wasted byte.  Multi-sector transfers in 12b mode don't work.
*/

#include "pdp8_defs.h"

/* Constants */

#define RL_NUMBY        256                             /* 8b bytes/sector */
#define RL_NUMSC        40                              /* sectors/surface */
#define RL_NUMSF        2                               /* surfaces/cylinder */
#define RL_NUMCY        256                             /* cylinders/drive */
#define RL_NUMDR        4                               /* drives/controller */
#define RL_MAXFR        (1 << 12)                       /* max transfer */
#define RL01_SIZE       (RL_NUMCY*RL_NUMSF*RL_NUMSC*RL_NUMBY)  /* words/drive */
#define RL02_SIZE       (RL01_SIZE * 2)                 /* words/drive */
#define RL_BBMAP        014                             /* sector for bblk map */
#define RL_BBID         0123                            /* ID for bblk map */

/* Flags in the unit flags word */

#define UNIT_V_WLK      (UNIT_V_UF + 0)                 /* write lock */
#define UNIT_V_RL02     (UNIT_V_UF + 1)                 /* RL01 vs RL02 */
#define UNIT_V_AUTO     (UNIT_V_UF + 2)                 /* autosize enable */
#define UNIT_V_DUMMY    (UNIT_V_UF + 3)                 /* dummy flag */
#define UNIT_DUMMY      (1u << UNIT_V_DUMMY)
#define UNIT_WLK        (1u << UNIT_V_WLK)
#define UNIT_RL02       (1u << UNIT_V_RL02)
#define UNIT_AUTO       (1u << UNIT_V_AUTO)
#define UNIT_WPRT       (UNIT_WLK | UNIT_RO)            /* write protect */

/* Parameters in the unit descriptor */

#define TRK             u3                              /* current cylinder */
#define STAT            u4                              /* status */

/* RLDS, NI = not implemented, * = kept in STAT, ^ = kept in TRK */

#define RLDS_LOAD       0                               /* no cartridge */
#define RLDS_LOCK       5                               /* lock on */
#define RLDS_BHO        0000010                         /* brushes home NI */
#define RLDS_HDO        0000020                         /* heads out NI */
#define RLDS_CVO        0000040                         /* cover open NI */
#define RLDS_HD         0000100                         /* head select ^ */
#define RLDS_RL02       0000200                         /* RL02 */
#define RLDS_DSE        0000400                         /* drv sel err NI */
#define RLDS_VCK        0001000                         /* vol check * */
#define RLDS_WGE        0002000                         /* wr gate err * */
#define RLDS_SPE        0004000                         /* spin err * */
#define RLDS_STO        0010000                         /* seek time out NI */
#define RLDS_WLK        0020000                         /* wr locked */
#define RLDS_HCE        0040000                         /* hd curr err NI */
#define RLDS_WDE        0100000                         /* wr data err NI */
#define RLDS_ATT        (RLDS_HDO+RLDS_BHO+RLDS_LOCK)   /* att status */
#define RLDS_UNATT      (RLDS_CVO+RLDS_LOAD)            /* unatt status */
#define RLDS_ERR        (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \
                         RLDS_VCK+RLDS_DSE)             /* errors bits */

/* RLCSA, seek = offset/rw = address (also uptr->TRK) */

#define RLCSA_DIR       04000                           /* direction */
#define RLCSA_HD        02000                           /* head select */
#define RLCSA_CYL       00777                           /* cyl offset */
#define GET_CYL(x)      ((x) & RLCSA_CYL)
#define GET_TRK(x)      ((((x) & RLCSA_CYL) * RL_NUMSF) + \
                        (((x) & RLCSA_HD)? 1: 0))
#define GET_DA(x)       ((GET_TRK(x) * RL_NUMSC) + rlsa)

/* RLCSB, function/unit select */

#define RLCSB_V_FUNC    0                               /* function */
#define RLCSB_M_FUNC    07
#define  RLCSB_MNT      0
#define  RLCSB_CLRD     1
#define  RLCSB_GSTA     2
#define  RLCSB_SEEK     3
#define  RLCSB_RHDR     4
#define  RLCSB_WRITE    5
#define  RLCSB_READ     6
#define  RLCSB_RNOHDR   7
#define RLCSB_V_MEX     3                               /* memory extension */
#define RLCSB_M_MEX     07
#define RLCSB_V_DRIVE   6                               /* drive */
#define RLCSB_M_DRIVE   03
#define RLCSB_V_IE      8                               /* int enable */
#define RLCSB_IE        (1u << RLCSB_V_IE)
#define RLCSB_8B        01000                           /* 12b/8b */
#define RCLS_MNT        02000                           /* maint NI */
#define RLCSB_RW        0001777                         /* read/write */
#define GET_FUNC(x)     (((x) >> RLCSB_V_FUNC) & RLCSB_M_FUNC)
#define GET_MEX(x)      (((x) >> RLCSB_V_MEX) & RLCSB_M_MEX)
#define GET_DRIVE(x)    (((x) >> RLCSB_V_DRIVE) & RLCSB_M_DRIVE)

/* RLSA, disk sector */

#define RLSA_V_SECT     6                               /* sector */
#define RLSA_M_SECT     077
#define GET_SECT(x)     (((x) >> RLSA_V_SECT) & RLSA_M_SECT)

/* RLER, error register */

#define RLER_DRDY       00001                           /* drive ready */
#define RLER_DRE        00002                           /* drive error */
#define RLER_HDE        01000                           /* header error */
#define RLER_INCMP      02000                           /* incomplete */
#define RLER_ICRC       04000                           /* CRC error */
#define RLER_MASK       07003

/* RLSI, silo register, used only in read header */

#define RLSI_V_TRK      6                               /* track */

extern uint16 M[];
extern int32 int_req;
extern UNIT cpu_unit;

uint8 *rlxb = NULL;                                     /* xfer buffer */
int32 rlcsa = 0;                                        /* control/status A */
int32 rlcsb = 0;                                        /* control/status B */
int32 rlma = 0;                                         /* memory address */
int32 rlwc = 0;                                         /* word count */
int32 rlsa = 0;                                         /* sector address */
int32 rler = 0;                                         /* error register */
int32 rlsi = 0, rlsi1 = 0, rlsi2 = 0;                   /* silo queue */
int32 rl_lft = 0;                                       /* silo left/right */
int32 rl_done = 0;                                      /* done flag */
int32 rl_erf = 0;                                       /* error flag */
int32 rl_swait = 10;                                    /* seek wait */
int32 rl_rwait = 10;                                    /* rotate wait */
int32 rl_stopioe = 1;                                   /* stop on error */

int32 rl60 (int32 IR, int32 AC);
int32 rl61 (int32 IR, int32 AC);
t_stat rl_svc (UNIT *uptr);
t_stat rl_reset (DEVICE *dptr);
void rl_set_done (int32 error);
t_stat rl_boot (int32 unitno, DEVICE *dptr);
t_stat rl_attach (UNIT *uptr, CONST char *cptr);
t_stat rl_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat rl_set_bad (UNIT *uptr, int32 val, CONST char *cptr, void *desc);

/* RL8A data structures

   rl_dev       RL device descriptor
   rl_unit      RL unit list
   rl_reg       RL register list
   rl_mod       RL modifier list
*/

DIB rl_dib = { DEV_RL, 2, { &rl60, &rl61 } };

UNIT rl_unit[] = {
    { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
             UNIT_ROABLE, RL01_SIZE) },
    { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
             UNIT_ROABLE, RL01_SIZE) },
    { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
             UNIT_ROABLE, RL01_SIZE) },
    { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
             UNIT_ROABLE, RL01_SIZE) }
    };

REG rl_reg[] = {
    { ORDATAD (RLCSA, rlcsa, 12, "control/status A") },
    { ORDATAD (RLCSB, rlcsb, 12, "control/status B") },
    { ORDATAD (RLMA, rlma, 12, "memory address") },
    { ORDATAD (RLWC, rlwc, 12, "word count") },
    { ORDATAD (RLSA, rlsa, 6, "sector address") },
    { ORDATAD (RLER, rler, 12, "error flags") },
    { ORDATAD (RLSI, rlsi, 16, "silo top word") },
    { ORDATAD (RLSI1, rlsi1, 16, "silo second word") },
    { ORDATAD (RLSI2, rlsi2, 16, "silo third word") },
    { FLDATAD (RLSIL, rl_lft, 0, "silo read left/right flag") },
    { FLDATAD (INT, int_req, INT_V_RL, "interrupt request") },
    { FLDATAD (DONE, rl_done, INT_V_RL, "done flag") },
    { FLDATA (IE, rlcsb, RLCSB_V_IE) },
    { FLDATAD (ERR, rl_erf, 0, "composite error flag") },
    { DRDATAD (STIME, rl_swait, 24, "seek time, per cylinder"), PV_LEFT },
    { DRDATAD (RTIME, rl_rwait, 24, "rotational delay"), PV_LEFT },
    { URDATA (CAPAC, rl_unit[0].capac, 10, T_ADDR_W, 0,
              RL_NUMDR, PV_LEFT + REG_HRO) },
    { FLDATAD (STOP_IOE, rl_stopioe, 0, "stop on I/O error") },
    { ORDATA (DEVNUM, rl_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB rl_mod[] = {
    { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
    { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
    { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad },
    { (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL },
    { (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL },
    { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL },
    { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL },
    { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL },
    { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL },
    { (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size },
    { (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { 0 }
    };

DEVICE rl_dev = {
    "RL", rl_unit, rl_reg, rl_mod,
    RL_NUMDR, 8, 24, 1, 8, 8,
    NULL, NULL, &rl_reset,
    &rl_boot, &rl_attach, NULL,
    &rl_dib, DEV_DISABLE | DEV_DIS
    };

/* IOT routines */

int32 rl60 (int32 IR, int32 AC)
{
int32 curr, offs, newc, maxc;
UNIT *uptr;

switch (IR & 07) {                                      /* case IR<9:11> */

    case 0:                                             /* RLDC */
        rl_reset (&rl_dev);                             /* reset device */
        break;

    case 1:                                             /* RLSD */
        if (rl_done)                                    /* skip if done */
            AC = IOT_SKP;
        else AC = 0;
        rl_done = 0;                                    /* clear done */
        int_req = int_req & ~INT_RL;                    /* clear intr */
        return AC;

    case 2:                                             /* RLMA */
        rlma = AC;
        break;

    case 3:                                             /* RLCA */
        rlcsa = AC;
        break;

    case 4:                                             /* RLCB */
        rlcsb = AC;
        rl_done = 0;                                    /* clear done */
        rler = rl_erf = 0;                              /* clear errors */
        int_req = int_req & ~INT_RL;                    /* clear intr */
        rl_lft = 0;                                     /* clear silo ptr */
        uptr = rl_dev.units + GET_DRIVE (rlcsb);        /* select unit */
        switch (GET_FUNC (rlcsb)) {                     /* case on func */

        case RLCSB_CLRD:                                /* clear drive */
            uptr->STAT = uptr->STAT & ~RLDS_ERR;        /* clear errors */
        case RLCSB_MNT:                                 /* mnt */
            rl_set_done (0);
            break;

        case RLCSB_SEEK:                                /* seek */
            curr = GET_CYL (uptr->TRK);                 /* current cylinder */
            offs = GET_CYL (rlcsa);                     /* offset */
            if (rlcsa & RLCSA_DIR) {                    /* in or out? */
                newc = curr + offs;                     /* out */
                maxc = (uptr->flags & UNIT_RL02)?
                        RL_NUMCY * 2: RL_NUMCY;
                if (newc >= maxc) newc = maxc - 1;
                }
            else {
                newc = curr - offs;                     /* in */
                if (newc < 0) newc = 0;
                }
            uptr->TRK = newc | (rlcsa & RLCSA_HD);
            sim_activate (uptr, rl_swait * abs (newc - curr));
            break;

        default:                                        /* data transfer */
            sim_activate (uptr, rl_swait);              /* activate unit */
            break;
            }                                           /* end switch func */
        break;

    case 5:                                             /* RLSA */
        rlsa = GET_SECT (AC);
        break;

    case 6:                                             /* spare */
        return 0;

    case 7:                                             /* RLWC */
        rlwc = AC;
        break;
        }                                               /* end switch pulse */

return 0;                                               /* clear AC */
}

int32 rl61 (int32 IR, int32 AC)
{
int32 dat;
UNIT *uptr;

switch (IR & 07) {                                      /* case IR<9:11> */

    case 0:                                             /* RRER */
        uptr = rl_dev.units + GET_DRIVE (rlcsb);        /* select unit */
        if (!sim_is_active (uptr) &&                    /* update drdy */
            (uptr->flags & UNIT_ATT))
            rler = rler | RLER_DRDY;
        else rler = rler & ~RLER_DRDY;
        dat = rler & RLER_MASK;
        break;

    case 1:                                             /* RRWC */
        dat = rlwc;
        break;

    case 2:                                             /* RRCA */
        dat = rlcsa;
        break;

    case 3:                                             /* RRCB */
        dat = rlcsb;
        break;

    case 4:                                             /* RRSA */
        dat = (rlsa << RLSA_V_SECT) & 07777;
        break;

    case 5:                                             /* RRSI */
        if (rl_lft) {                                   /* silo left? */
            dat = (rlsi >> 8) & 0377;                   /* get left 8b */
            rlsi = rlsi1;                               /* ripple */
            rlsi1 = rlsi2;
            }
        else dat = rlsi & 0377;                         /* get right 8b */
        rl_lft = rl_lft ^ 1;                            /* change side */
        break;

    case 6:                                             /* spare */
        return AC;

    case 7:                                             /* RLSE */
        if (rl_erf)                                     /* skip if err */
            dat = IOT_SKP | AC;
        else dat = AC;
        rl_erf = 0;
        break;
        }                                               /* end switch pulse */

return dat;
}

/* Service unit timeout

   If seek in progress, complete seek command
   Else complete data transfer command

   The unit control block contains the function and cylinder for
   the current command.
*/

t_stat rl_svc (UNIT *uptr)
{
int32 err, wc, maxc;
int32 i, j, func, da, bc, wbc;
uint32 ma;

func = GET_FUNC (rlcsb);                                /* get function */
if (func == RLCSB_GSTA) {                               /* get status? */
    rlsi = uptr->STAT | 
        ((uptr->TRK & RLCSA_HD)? RLDS_HD: 0) |
        ((uptr->flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT);
    if (uptr->flags & UNIT_RL02)
        rlsi = rlsi | RLDS_RL02;
    if (uptr->flags & UNIT_WPRT)
        rlsi = rlsi | RLDS_WLK;
    rlsi2 = rlsi1 = rlsi;
    rl_set_done (0);                                    /* done */
    return SCPE_OK;
    }

if ((uptr->flags & UNIT_ATT) == 0) {                    /* attached? */
    uptr->STAT = uptr->STAT | RLDS_SPE;                 /* spin error */
    rl_set_done (RLER_INCMP);                           /* flag error */
    return IORETURN (rl_stopioe, SCPE_UNATT);
    }

if ((func == RLCSB_WRITE) && (uptr->flags & UNIT_WPRT)) {
    uptr->STAT = uptr->STAT | RLDS_WGE;                 /* write and locked */
    rl_set_done (RLER_DRE);                             /* flag error */
    return SCPE_OK;
    }

if (func == RLCSB_SEEK) {                               /* seek? */
    rl_set_done (0);                                    /* done */
    return SCPE_OK;
    }

if (func == RLCSB_RHDR) {                               /* read header? */
    rlsi = (GET_TRK (uptr->TRK) << RLSI_V_TRK) | rlsa;
    rlsi1 = rlsi2 = 0;
    rl_set_done (0);                                    /* done */
    return SCPE_OK;
    }

if (((func != RLCSB_RNOHDR) && (GET_CYL (uptr->TRK) != GET_CYL (rlcsa)))
   || (rlsa >= RL_NUMSC)) {                             /* bad cyl or sector? */
    rl_set_done (RLER_HDE | RLER_INCMP);                /* flag error */
    return SCPE_OK;
    }
    
ma = (GET_MEX (rlcsb) << 12) | rlma;                    /* get mem addr */
da = GET_DA (rlcsa) * RL_NUMBY;                         /* get disk addr */
wc = 010000 - rlwc;                                     /* get true wc */
if (rlcsb & RLCSB_8B) {                                 /* 8b mode? */
    bc = wc;                                            /* bytes to xfr */
    maxc = (RL_NUMSC - rlsa) * RL_NUMBY;                /* max transfer */
    if (bc > maxc)                                      /* trk ovrun? limit */
        wc = bc = maxc;
    }
else {
    bc = ((wc * 3) + 1) / 2;                            /* 12b mode */
    if (bc > RL_NUMBY) {                                /* > 1 sector */
        bc = RL_NUMBY;                                  /* cap xfer */
        wc = (RL_NUMBY * 2) / 3;
        }
    }

err = fseek (uptr->fileref, da, SEEK_SET);

if ((func >= RLCSB_READ) && (err == 0) &&               /* read (no hdr)? */
    MEM_ADDR_OK (ma)) {                                 /* valid bank? */
    i = fxread (rlxb, sizeof (int8), bc, uptr->fileref);
    err = ferror (uptr->fileref);
    for ( ; i < bc; i++)                                /* fill buffer */
        rlxb[i] = 0;
    for (i = j = 0; i < wc; i++) {                      /* store buffer */
        if (rlcsb & RLCSB_8B)                           /* 8b mode? */
            M[ma] = rlxb[i] & 0377;                     /* store */
        else if (i & 1) {                               /* odd wd 12b? */
            M[ma] = ((rlxb[j + 1] >> 4) & 017) |
                (((uint16) rlxb[j + 2]) << 4);
            j = j + 3;
            }
        else M[ma] = rlxb[j] |                          /* even wd 12b */
            ((((uint16) rlxb[j + 1]) & 017) << 8);      
        ma = (ma & 070000) + ((ma + 1) & 07777);
        }                                               /* end for */
    }                                                   /* end if wr */

if ((func == RLCSB_WRITE) && (err == 0)) {              /* write? */
    for (i = j = 0; i < wc; i++) {                      /* fetch buffer */
        if (rlcsb & RLCSB_8B)                           /* 8b mode? */
            rlxb[i] = M[ma] & 0377;                     /* fetch */
        else if (i & 1) {                               /* odd wd 12b? */
            rlxb[j + 1] = rlxb[j + 1] | ((M[ma] & 017) << 4);
            rlxb[j + 2] = ((M[ma] >> 4) & 0377);
            j = j + 3;
            }
        else {                                          /* even wd 12b */
            rlxb[j] = M[ma] & 0377;
            rlxb[j + 1] = (M[ma] >> 8) & 017;
            }
        ma = (ma & 070000) + ((ma + 1) & 07777);
        }                                               /* end for */
    wbc = (bc + (RL_NUMBY - 1)) & ~(RL_NUMBY - 1);      /* clr to */
    for (i = bc; i < wbc; i++)                          /* end of blk */
        rlxb[i] = 0;
    fxwrite (rlxb, sizeof (int8), wbc, uptr->fileref);
    err = ferror (uptr->fileref);
    }                                                   /* end write */

rlwc = (rlwc + wc) & 07777;                             /* final word count */
if (rlwc != 0)                                          /* completed? */
    rler = rler | RLER_INCMP;
rlma = (rlma + wc) & 07777;                             /* final word addr */
rlsa = rlsa + ((bc + (RL_NUMBY - 1)) / RL_NUMBY);
rl_set_done (0);

if (err != 0) {                                         /* error? */
    sim_perror ("RL I/O error");
    clearerr (uptr->fileref);
    return SCPE_IOERR;
    }
return SCPE_OK;
}

/* Set done and possibly errors */

void rl_set_done (int32 status)
{
rl_done = 1;
rler = rler | status;
if (rler)
    rl_erf = 1;
if (rlcsb & RLCSB_IE)
    int_req = int_req | INT_RL;
else int_req = int_req & ~INT_RL;
return;
}

/* Device reset

   Note that the RL8A does NOT recalibrate its drives on RESET
*/

t_stat rl_reset (DEVICE *dptr)
{
int32 i;
UNIT *uptr;

rlcsa = rlcsb = rlsa = rler = 0;
rlma = rlwc = 0;
rlsi = rlsi1 = rlsi2 = 0;
rl_lft = 0;
rl_done = 0;
rl_erf = 0;
int_req = int_req & ~INT_RL;
for (i = 0; i < RL_NUMDR; i++) {
    uptr = rl_dev.units + i;
    sim_cancel (uptr);
    uptr->STAT = 0;
    }
if (rlxb == NULL)
    rlxb = (uint8 *) calloc (RL_MAXFR, sizeof (uint8));
if (rlxb == NULL)
    return SCPE_MEM;
return SCPE_OK;
}

/* Attach routine */

t_stat rl_attach (UNIT *uptr, CONST char *cptr)
{
uint32 p;
t_stat r;

uptr->capac = (uptr->flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE;
r = attach_unit (uptr, cptr);                           /* attach unit */
if (r != SCPE_OK)                                       /* error? */
    return r;
uptr->TRK = 0;                                          /* cyl 0 */
uptr->STAT = RLDS_VCK;                                  /* new volume */
if ((p = sim_fsize (uptr->fileref)) == 0) {             /* new disk image? */
    if (uptr->flags & UNIT_RO)
        return SCPE_OK;
    return rl_set_bad (uptr, 0, NULL, NULL);
    }
if ((uptr->flags & UNIT_AUTO) == 0)                     /* autosize? */
    return r;
if (p > (RL01_SIZE * sizeof (int16))) {
    uptr->flags = uptr->flags | UNIT_RL02;
    uptr->capac = RL02_SIZE;
    }
else {
    uptr->flags = uptr->flags & ~UNIT_RL02;
    uptr->capac = RL01_SIZE;
    }
return SCPE_OK;
}

/* Set size routine */

t_stat rl_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if (uptr->flags & UNIT_ATT)
    return SCPE_ALATT;
uptr->capac = (val & UNIT_RL02)? RL02_SIZE: RL01_SIZE;
return SCPE_OK;
}

/* Factory bad block table creation routine

   This routine writes the OS/8 specific bad block map in track 0, sector 014 (RL_BBMAP):

        words 0 magic number = 0123 (RL_BBID)
        words 1-n       block numbers
         :
        words n+1       end of table = 0

   Inputs:
        uptr    =       pointer to unit
        val     =       ignored
   Outputs:
        sta     =       status code
*/

t_stat rl_set_bad (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 i, da = RL_BBMAP * RL_NUMBY;

if ((uptr->flags & UNIT_ATT) == 0)
    return SCPE_UNATT;
if (uptr->flags & UNIT_RO)
    return SCPE_RO;
if (!get_yn ("Create bad block table? [N]", FALSE))
    return SCPE_OK;
if (fseek (uptr->fileref, da, SEEK_SET))
    return SCPE_IOERR;
rlxb[0] = RL_BBID;
for (i = 1; i < RL_NUMBY; i++)
    rlxb[i] = 0;
fxwrite (rlxb, sizeof (uint8), RL_NUMBY, uptr->fileref);
if (ferror (uptr->fileref))
    return SCPE_IOERR;
return SCPE_OK;
}

/* Bootstrap */

#define BOOT_START 1                                    /* start */
#define BOOT_UNIT 02006                                 /* unit number */
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))

static const uint16 boot_rom[] = {
    06600,                      /* BT, RLDC             ; reset */
    07201,                      /* 02, CLA IAC          ; clr drv = 1 */
    04027,                      /* 03, JMS GO           ; do io */
    01004,                      /* 04, TAD 4            ; rd hdr fnc */
    04027,                      /* 05, JMS GO           ; do io */
    06615,                      /* 06, RRSI             ; rd hdr lo */
    07002,                      /* 07, BSW              ; swap */
    07012,                      /* 10, RTR              ; lo cyl to L */
    06615,                      /* 11, RRSI             ; rd hdr hi */
    00025,                      /* 12, AND 25           ; mask = 377 */
    07004,                      /* 13, RTL              ; get cyl */
    06603,                      /* 14, RLCA             ; set addr */
    07325,                      /* 15, CLA STL IAC RAL  ; seek = 3 */
    04027,                      /* 16, JMS GO           ; do io */
    07332,                      /* 17, CLA STL RTR      ; dir in = 2000 */
    06605,                      /* 20, RLSA             ; sector */             
    01026,                      /* 21, TAD (-200)       ; one sector */
    06607,                      /* 22, RLWC             ; word cnt */
    07327,                      /* 23, CLA STL IAC RTL  ; read = 6*/
    04027,                      /* 24, JMS GO           ; do io */
    00377,                      /* 25, JMP 377          ; start */
    07600,                      /* 26, -200             ; word cnt */
    00000,                      /* GO, 0                ; subr */
    06604,                      /* 30, RLCB             ; load fnc */
    06601,                      /* 31, RLSD             ; wait */
    05031,                      /* 32, JMP .-1          ; */
    06617,                      /* 33, RLSE             ; error? */
    05427,                      /* 34, JMP I GO         ; no, ok */
    05001                       /* 35, JMP BT           ; restart */
    };


t_stat rl_boot (int32 unitno, DEVICE *dptr)
{
size_t i;

if (unitno)                                             /* only unit 0 */
    return SCPE_ARG;
if (rl_dib.dev != DEV_RL)                               /* only std devno */
    return STOP_NOTSTD;
rl_unit[unitno].TRK = 0;
for (i = 0; i < BOOT_LEN; i++)
    M[BOOT_START + i] = boot_rom[i];
cpu_set_bootpc (BOOT_START);
return SCPE_OK;
}
Added src/PDP8/pdp8_rx.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_rx.c: RX8E/RX01, RX28/RX02 floppy disk simulator

   Copyright (c) 1993-2013, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   rx           RX8E/RX01, RX28/RX02 floppy disk

   17-Sep-13    RMS     Changed to use central set_bootpc routine
   03-Sep-13    RMS     Added explicit void * cast
   15-May-06    RMS     Fixed bug in autosize attach (Dave Gesswein)
   04-Jan-04    RMS     Changed sim_fsize calling sequence
   05-Nov-03    RMS     Fixed bug in RX28 read status (Charles Dickman)
   26-Oct-03    RMS     Cleaned up buffer copy code, fixed double density write
   25-Apr-03    RMS     Revised for extended file support
   14-Mar-03    RMS     Fixed variable size interaction with save/restore
   03-Mar-03    RMS     Fixed autosizing
   08-Oct-02    RMS     Added DIB, device number support
                        Fixed reset to work with disabled device
   15-Sep-02    RMS     Added RX28/RX02 support
   06-Jan-02    RMS     Changed enable/disable support
   30-Nov-01    RMS     Added read only unit, extended SET/SHOW support
   24-Nov-01    RMS     Converted FLG to array
   17-Jul-01    RMS     Fixed warning from VC++ 6
   26-Apr-01    RMS     Added device enable/disable support
   13-Apr-01    RMS     Revised for register arrays
   14-Apr-99    RMS     Changed t_addr to unsigned
   15-Aug-96    RMS     Fixed bug in LCD

   An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B.
   An RX02 diskette consists of 77 tracks, each with 26 sectors of 128B
   (single density) or 256B (double density).  Tracks are numbered 0-76,
   sectors 1-26.  The RX8E (RX28) can store data in 8b mode or 12b mode.
   In 8b mode, the controller reads or writes 128 bytes (128B or 256B)
   per sector.  In 12b mode, it reads or writes 64 (64 or 128) 12b words
   per sector.  The 12b words are bit packed into the first 96 (192) bytes
   of the sector; the last 32 (64) bytes are zeroed on writes.
*/

#include "pdp8_defs.h"

#define RX_NUMTR        77                              /* tracks/disk */
#define RX_M_TRACK      0377
#define RX_NUMSC        26                              /* sectors/track */
#define RX_M_SECTOR     0177                            /* cf Jones!! */
#define RX_NUMBY        128                             /* bytes/sector */
#define RX2_NUMBY       256
#define RX_NUMWD        (RX_NUMBY / 2)                  /* words/sector */
#define RX2_NUMWD       (RX2_NUMBY / 2)
#define RX_SIZE         (RX_NUMTR * RX_NUMSC * RX_NUMBY)        /* bytes/disk */
#define RX2_SIZE        (RX_NUMTR * RX_NUMSC * RX2_NUMBY)
#define RX_NUMDR        2                               /* drives/controller */
#define RX_M_NUMDR      01
#define UNIT_V_WLK      (UNIT_V_UF + 0)                 /* write locked */
#define UNIT_V_DEN      (UNIT_V_UF + 1)                 /* double density */
#define UNIT_V_AUTO     (UNIT_V_UF + 2)                 /* autosize */
#define UNIT_WLK        (1u << UNIT_V_WLK)
#define UNIT_DEN        (1u << UNIT_V_DEN)
#define UNIT_AUTO       (1u << UNIT_V_AUTO)
#define UNIT_WPRT       (UNIT_WLK | UNIT_RO)            /* write protect */

#define IDLE            0                               /* idle state */
#define CMD8            1                               /* 8b cmd, ho next */
#define RWDS            2                               /* rw, sect next */
#define RWDT            3                               /* rw, track next */
#define RWXFR           4                               /* rw, transfer */
#define FILL            5                               /* fill buffer */
#define EMPTY           6                               /* empty buffer */
#define SDCNF           7                               /* set dens, conf next */
#define SDXFR           8                               /* set dens, transfer */
#define CMD_COMPLETE    9                               /* set done next */
#define INIT_COMPLETE   10                              /* init compl next */

#define RXCS_V_FUNC     1                               /* function */
#define RXCS_M_FUNC     7
#define  RXCS_FILL      0                               /* fill buffer */
#define  RXCS_EMPTY     1                               /* empty buffer */
#define  RXCS_WRITE     2                               /* write sector */
#define  RXCS_READ      3                               /* read sector */
#define  RXCS_SDEN      4                               /* set density (RX28) */
#define  RXCS_RXES      5                               /* read status */
#define  RXCS_WRDEL     6                               /* write del data */
#define  RXCS_ECODE     7                               /* read error code */
#define RXCS_DRV        0020                            /* drive */
#define RXCS_MODE       0100                            /* mode */
#define RXCS_MAINT      0200                            /* maintenance */
#define RXCS_DEN        0400                            /* density (RX28) */
#define RXCS_GETFNC(x)  (((x) >> RXCS_V_FUNC) & RXCS_M_FUNC)

#define RXES_CRC        0001                            /* CRC error NI */
#define RXES_ID         0004                            /* init done */
#define RXES_RX02       0010                            /* RX02 (RX28) */
#define RXES_DERR       0020                            /* density err (RX28) */
#define RXES_DEN        0040                            /* density (RX28) */
#define RXES_DD         0100                            /* deleted data */
#define RXES_DRDY       0200                            /* drive ready */

#define TRACK u3                                        /* current track */
#define READ_RXDBR      ((rx_csr & RXCS_MODE)? AC | (rx_dbr & 0377): rx_dbr)
#define CALC_DA(t,s,b)  (((t) * RX_NUMSC) + ((s) - 1)) * b

extern int32 int_req, int_enable, dev_done;

int32 rx_28 = 0;                                        /* controller type */
int32 rx_tr = 0;                                        /* xfer ready flag */
int32 rx_err = 0;                                       /* error flag */
int32 rx_csr = 0;                                       /* control/status */
int32 rx_dbr = 0;                                       /* data buffer */
int32 rx_esr = 0;                                       /* error status */
int32 rx_ecode = 0;                                     /* error code */
int32 rx_track = 0;                                     /* desired track */
int32 rx_sector = 0;                                    /* desired sector */
int32 rx_state = IDLE;                                  /* controller state */
int32 rx_cwait = 100;                                   /* command time */
int32 rx_swait = 10;                                    /* seek, per track */
int32 rx_xwait = 1;                                     /* tr set time */
int32 rx_stopioe = 0;                                   /* stop on error */
uint8 rx_buf[RX2_NUMBY] = { 0 };                        /* sector buffer */
int32 rx_bptr = 0;                                      /* buffer pointer */

int32 rx (int32 IR, int32 AC);
t_stat rx_svc (UNIT *uptr);
t_stat rx_reset (DEVICE *dptr);
t_stat rx_boot (int32 unitno, DEVICE *dptr);
t_stat rx_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat rx_attach (UNIT *uptr, CONST char *cptr);
void rx_cmd (void);
void rx_done (int32 esr_flags, int32 new_ecode);
t_stat rx_settype (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, CONST void *desc);

/* RX8E data structures

   rx_dev       RX device descriptor
   rx_unit      RX unit list
   rx_reg       RX register list
   rx_mod       RX modifier list
*/

DIB rx_dib = { DEV_RX, 1, { &rx } };

UNIT rx_unit[] = {
    { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+
             UNIT_ROABLE, RX_SIZE) },
    { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+
             UNIT_ROABLE, RX_SIZE) }
    };

REG rx_reg[] = {
    { ORDATAD (RXCS, rx_csr, 12, "status") },
    { ORDATAD (RXDB, rx_dbr, 12, "data buffer") },
    { ORDATAD (RXES, rx_esr, 12, "error status") },
    { ORDATA (RXERR, rx_ecode, 8) },
    { ORDATAD (RXTA, rx_track, 8, "current track") },
    { ORDATAD (RXSA, rx_sector, 8, "current sector") },
    { DRDATAD (STAPTR, rx_state, 4, "controller state"), REG_RO },
    { DRDATAD (BUFPTR, rx_bptr, 8, "buffer pointer")  },
    { FLDATAD (TR, rx_tr, 0, "transfer ready flag") },
    { FLDATAD (ERR, rx_err, 0, "error flag") },
    { FLDATAD (DONE, dev_done, INT_V_RX, "done flag") },
    { FLDATAD (ENABLE, int_enable, INT_V_RX, "interrupt enable flag") },
    { FLDATAD (INT, int_req, INT_V_RX, "interrupt pending flag") },
    { DRDATAD (CTIME, rx_cwait, 24, "command completion time"), PV_LEFT },
    { DRDATAD (STIME, rx_swait, 24, "seek time per track"), PV_LEFT },
    { DRDATAD (XTIME, rx_xwait, 24, "transfer ready delay"), PV_LEFT },
    { FLDATAD (STOP_IOE, rx_stopioe, 0, "stop on I/O error") },
    { BRDATAD (SBUF, rx_buf, 8, 8, RX2_NUMBY, "sector buffer array") },
    { FLDATA (RX28, rx_28, 0), REG_HRO },
    { URDATA (CAPAC, rx_unit[0].capac, 10, T_ADDR_W, 0,
              RX_NUMDR, REG_HRO | PV_LEFT) },
    { ORDATA (DEVNUM, rx_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB rx_mod[] = {
    { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
    { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
    { MTAB_XTD | MTAB_VDV, 1, NULL, "RX28", &rx_settype, NULL, NULL },
    { MTAB_XTD | MTAB_VDV, 0, NULL, "RX8E", &rx_settype, NULL, NULL },
    { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &rx_showtype, NULL },
    { (UNIT_DEN+UNIT_ATT), UNIT_ATT, "single density", NULL, NULL },
    { (UNIT_DEN+UNIT_ATT), (UNIT_DEN+UNIT_ATT), "double density", NULL, NULL },
    { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), 0, "single density", NULL, NULL },
    { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), UNIT_DEN, "double density", NULL, NULL },
    { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL },
    { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL },
    { (UNIT_AUTO+UNIT_DEN), 0, NULL, "SINGLE", &rx_set_size },
    { (UNIT_AUTO+UNIT_DEN), UNIT_DEN, NULL, "DOUBLE", &rx_set_size },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { 0 }
    };

DEVICE rx_dev = {
    "RX", rx_unit, rx_reg, rx_mod,
    RX_NUMDR, 8, 20, 1, 8, 8,
    NULL, NULL, &rx_reset,
    &rx_boot, &rx_attach, NULL,
    &rx_dib, DEV_DISABLE
    };

/* IOT routine */

int32 rx (int32 IR, int32 AC)
{
int32 drv = ((rx_csr & RXCS_DRV)? 1: 0);                /* get drive number */

switch (IR & 07) {                                      /* decode IR<9:11> */

    case 0:                                             /* unused */
        break;

    case 1:                                             /* LCD */
        if (rx_state != IDLE)                           /* ignore if busy */
            return AC;
        dev_done = dev_done & ~INT_RX;                  /* clear done, int */
        int_req = int_req & ~INT_RX;
        rx_tr = rx_err = 0;                             /* clear flags */
        rx_bptr = 0;                                    /* clear buf pointer */
        if (rx_28 && (AC & RXCS_MODE)) {                /* RX28 8b mode? */
            rx_dbr = rx_csr = AC & 0377;                /* save 8b */
            rx_tr = 1;                                  /* xfer is ready */
            rx_state = CMD8;                            /* wait for part 2 */
            }
        else {
            rx_dbr = rx_csr = AC;                       /* save new command */
            rx_cmd ();                                  /* issue command */
            }
        return 0;                                       /* clear AC */

    case 2:                                             /* XDR */
        switch (rx_state & 017) {                       /* case on state */

        case EMPTY:                                     /* emptying buffer */
            sim_activate (&rx_unit[drv], rx_xwait);     /* sched xfer */
            return READ_RXDBR;                          /* return data reg */

        case CMD8:                                      /* waiting for cmd */
            rx_dbr = AC & 0377;
            rx_csr = (rx_csr & 0377) | ((AC & 017) << 8);
            rx_cmd ();
            break;

        case RWDS:case RWDT:case FILL:case SDCNF:       /* waiting for data */
            rx_dbr = AC;                                /* save data */
            sim_activate (&rx_unit[drv], rx_xwait);     /* schedule */
            break;

        default:                                        /* default */
            return READ_RXDBR;                          /* return data reg */
            }
        break;

    case 3:                                             /* STR */
        if (rx_tr != 0) {
            rx_tr = 0;
            return IOT_SKP + AC;
            }
        break;

    case 4:                                             /* SER */
        if (rx_err != 0) {
            rx_err = 0;
            return IOT_SKP + AC;
            }
        break;

    case 5:                                             /* SDN */
        if ((dev_done & INT_RX) != 0) {
            dev_done = dev_done & ~INT_RX;
            int_req = int_req & ~INT_RX;
            return IOT_SKP + AC;
            }
        break;

    case 6:                                             /* INTR */
        if (AC & 1)
            int_enable = int_enable | INT_RX;
        else int_enable = int_enable & ~INT_RX;
        int_req = INT_UPDATE;
        break;

    case 7:                                             /* INIT */
        rx_reset (&rx_dev);                             /* reset device */
        break;
        }                                               /* end case pulse */

return AC;
}

void rx_cmd (void)
{
int32 drv = ((rx_csr & RXCS_DRV)? 1: 0);                /* get drive number */

switch (RXCS_GETFNC (rx_csr)) {                         /* decode command */

    case RXCS_FILL:
        rx_state = FILL;                                /* state = fill */
        rx_tr = 1;                                      /* xfer is ready */
        rx_esr = rx_esr & RXES_ID;                      /* clear errors */
        break;

    case RXCS_EMPTY:
        rx_state = EMPTY;                               /* state = empty */
        rx_esr = rx_esr & RXES_ID;                      /* clear errors */
        sim_activate (&rx_unit[drv], rx_xwait);         /* sched xfer */
        break;

    case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL:
        rx_state = RWDS;                                /* state = get sector */
        rx_tr = 1;                                      /* xfer is ready */
        rx_esr = rx_esr & RXES_ID;                      /* clear errors */
        break;

    case RXCS_SDEN:
        if (rx_28) {                                    /* RX28? */
            rx_state = SDCNF;                           /* state = get conf */
            rx_tr = 1;                                  /* xfer is ready */
            rx_esr = rx_esr & RXES_ID;                  /* clear errors */
            break;
            }                                           /* else fall thru */
    default:
        rx_state = CMD_COMPLETE;                        /* state = cmd compl */
        sim_activate (&rx_unit[drv], rx_cwait);         /* sched done */
        break;
        }                                               /* end switch func */

return;
}

/* Unit service; the action to be taken depends on the transfer state:

   IDLE         Should never get here
   RWDS         Save sector, set TR, set RWDT
   RWDT         Save track, set RWXFR
   RWXFR        Read/write buffer
   FILL         copy dbr to rx_buf[rx_bptr], advance ptr
                if rx_bptr > max, finish command, else set tr
   EMPTY        if rx_bptr > max, finish command, else
                copy rx_buf[rx_bptr] to dbr, advance ptr, set tr
   CMD_COMPLETE copy requested data to dbr, finish command
   INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command

   For RWDT and CMD_COMPLETE, the input argument is the selected drive;
   otherwise, it is drive 0.
*/

t_stat rx_svc (UNIT *uptr)
{
int32 i, func, byptr, bps, wps;
int8 *fbuf = (int8 *) uptr->filebuf;
uint32 da;
#define PTR12(x) (((x) + (x) + (x)) >> 1)

if (rx_28 && (uptr->flags & UNIT_DEN))                  /* RX28 and double density? */
    bps = RX2_NUMBY;                                    /* double bytes/sector */
else bps = RX_NUMBY;                                    /* RX8E, normal count */
wps = bps / 2;
func = RXCS_GETFNC (rx_csr);                            /* get function */
switch (rx_state) {                                     /* case on state */

    case IDLE:                                          /* idle */
        return SCPE_IERR;

    case EMPTY:                                         /* empty buffer */
        if (rx_csr & RXCS_MODE) {                       /* 8b xfer? */
            if (rx_bptr >= bps) {                       /* done? */
                rx_done (0, 0);                         /* set done */
                break;                                  /* and exit */
                }
            rx_dbr = rx_buf[rx_bptr];                   /* else get data */
            }
        else {
            byptr = PTR12 (rx_bptr);                    /* 12b xfer */
            if (rx_bptr >= wps) {                       /* done? */
                rx_done (0, 0);                         /* set done */
                break;                                  /* and exit */
                }
            rx_dbr = (rx_bptr & 1)?                     /* get data */
                ((rx_buf[byptr] & 017) << 8) | rx_buf[byptr + 1]:
                (rx_buf[byptr] << 4) | ((rx_buf[byptr + 1] >> 4) & 017);
            }
        rx_bptr = rx_bptr + 1;
        rx_tr = 1;
        break;

    case FILL:                                          /* fill buffer */
        if (rx_csr & RXCS_MODE) {                       /* 8b xfer? */
            rx_buf[rx_bptr] = rx_dbr;                   /* fill buffer */
            rx_bptr = rx_bptr + 1;
            if (rx_bptr < bps)                          /* if more, set xfer */
                rx_tr = 1;
            else rx_done (0, 0);                        /* else done */
            }
        else {
            byptr = PTR12 (rx_bptr);                    /* 12b xfer */
            if (rx_bptr & 1) {                          /* odd or even? */
                rx_buf[byptr] = (rx_buf[byptr] & 0360) | ((rx_dbr >> 8) & 017);
                rx_buf[byptr + 1] = rx_dbr & 0377;
                }
            else {
                rx_buf[byptr] = (rx_dbr >> 4) & 0377;
                rx_buf[byptr + 1] = (rx_dbr & 017) << 4;
                }
            rx_bptr = rx_bptr + 1;
            if (rx_bptr < wps)                          /* if more, set xfer */
                rx_tr = 1;
            else {
                for (i = PTR12 (wps); i < bps; i++)
                    rx_buf[i] = 0;                      /* else fill sector */
                rx_done (0, 0);                         /* set done */
                }
            }
        break;

    case RWDS:                                          /* wait for sector */
        rx_sector = rx_dbr & RX_M_SECTOR;               /* save sector */
        rx_tr = 1;                                      /* set xfer ready */
        rx_state = RWDT;                                /* advance state */
        return SCPE_OK;

    case RWDT:                                          /* wait for track */
        rx_track = rx_dbr & RX_M_TRACK;                 /* save track */
        rx_state = RWXFR;
        sim_activate (uptr,                             /* sched done */
            rx_swait * abs (rx_track - uptr->TRACK));
        return SCPE_OK;

    case RWXFR:                                         /* transfer */
        if ((uptr->flags & UNIT_BUF) == 0) {            /* not buffered? */
            rx_done (0, 0110);                          /* done, error */
            return IORETURN (rx_stopioe, SCPE_UNATT);
            }
        if (rx_track >= RX_NUMTR) {                     /* bad track? */
            rx_done (0, 0040);                          /* done, error */
            break;
            }
        uptr->TRACK = rx_track;                         /* now on track */
        if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) {       /* bad sect? */
            rx_done (0, 0070);                          /* done, error */
            break;
            }
        if (rx_28 &&                                    /* RX28? */
            (((uptr->flags & UNIT_DEN) != 0) ^
             ((rx_csr & RXCS_DEN) != 0))) {             /* densities agree? */
            rx_done (RXES_DERR, 0240);                  /* no, error */
            break;
            }
        da = CALC_DA (rx_track, rx_sector, bps);        /* get disk address */
        if (func == RXCS_WRDEL)                         /* del data? */
            rx_esr = rx_esr | RXES_DD;
        if (func == RXCS_READ) {                        /* read? */
            for (i = 0; i < bps; i++) rx_buf[i] = fbuf[da + i];
            }
        else {                                          /* write */
            if (uptr->flags & UNIT_WPRT) {              /* locked? */
                rx_done (0, 0100);                      /* done, error */
                break;
                }
            for (i = 0; i < bps; i++)
                fbuf[da + i] = rx_buf[i];
            da = da + bps;
            if (da > uptr->hwmark)
                uptr->hwmark = da;
            }
        rx_done (0, 0);                                 /* done */
        break;

    case SDCNF:                                         /* confirm set density */
        if ((rx_dbr & 0377) != 0111) {                  /* confirmed? */
            rx_done (0, 0250);                          /* no, error */
            break;
            }
        rx_state = SDXFR;                               /* next state */
        sim_activate (uptr, rx_cwait * 100);            /* schedule operation */
        break;

    case SDXFR:                                         /* erase disk */
        for (i = 0; i < (int32) uptr->capac; i++)
            fbuf[i] = 0;
        uptr->hwmark = uptr->capac;
        if (rx_csr & RXCS_DEN)
            uptr->flags = uptr->flags | UNIT_DEN;
        else uptr->flags = uptr->flags & ~UNIT_DEN;
        rx_done (0, 0);
        break;

    case CMD_COMPLETE:                                  /* command complete */
        if (func == RXCS_ECODE) {                       /* read ecode? */
            rx_dbr = rx_ecode;                          /* set dbr */
            rx_done (0, -1);                            /* don't update */
            }
        else if (rx_28) {                               /* no, read sta; RX28? */
            rx_esr = rx_esr & ~RXES_DERR;               /* assume dens match */
            if (((uptr->flags & UNIT_DEN) != 0) ^       /* densities mismatch? */
                ((rx_csr & RXCS_DEN) != 0))
                rx_done (RXES_DERR, 0240);              /* yes, error */
            else rx_done (0, 0);                        /* no, ok */
            }
        else rx_done (0, 0);                            /* RX8E status */
        break;

    case INIT_COMPLETE:                                 /* init complete */
        rx_unit[0].TRACK = 1;                           /* drive 0 to trk 1 */
        rx_unit[1].TRACK = 0;                           /* drive 1 to trk 0 */
        if ((rx_unit[0].flags & UNIT_BUF) == 0) {       /* not buffered? */
            rx_done (RXES_ID, 0010);                    /* init done, error */
            break;
            }
        da = CALC_DA (1, 1, bps);                       /* track 1, sector 1 */
        for (i = 0; i < bps; i++)                       /* read sector */
            rx_buf[i] = fbuf[da + i];
        rx_done (RXES_ID, 0);                           /* set done */
        if ((rx_unit[1].flags & UNIT_ATT) == 0)
            rx_ecode = 0020;
        break;
        }                                               /* end case state */

return SCPE_OK;
}

/* Command complete.  Set done and put final value in interface register,
   return to IDLE state.
*/

void rx_done (int32 esr_flags, int32 new_ecode)
{
int32 drv = (rx_csr & RXCS_DRV)? 1: 0;

rx_state = IDLE;                                        /* now idle */
dev_done = dev_done | INT_RX;                           /* set done */
int_req = INT_UPDATE;                                   /* update ints */
rx_esr = (rx_esr | esr_flags) & ~(RXES_DRDY|RXES_RX02|RXES_DEN);
if (rx_28)                                              /* RX28? */
    rx_esr = rx_esr | RXES_RX02;
if (rx_unit[drv].flags & UNIT_ATT) {                    /* update drv rdy */
    rx_esr = rx_esr | RXES_DRDY;
    if (rx_unit[drv].flags & UNIT_DEN)                  /* update density */
        rx_esr = rx_esr | RXES_DEN;
    }
if (new_ecode > 0)                                      /* test for error */
    rx_err = 1;
if (new_ecode < 0)                                      /* don't update? */
    return;
rx_ecode = new_ecode;                                   /* update ecode */
rx_dbr = rx_esr;                                        /* update RXDB */
return;
}

/* Reset routine.  The RX is one of the few devices that schedules
   an I/O transfer as part of its initialization */

t_stat rx_reset (DEVICE *dptr)
{
rx_dbr = rx_csr = 0;                                    /* 12b mode, drive 0 */
rx_esr = rx_ecode = 0;                                  /* clear error */
rx_tr = rx_err = 0;                                     /* clear flags */
rx_track = rx_sector = 0;                               /* clear address */
rx_state = IDLE;                                        /* ctrl idle */
dev_done = dev_done & ~INT_RX;                          /* clear done, int */
int_req = int_req & ~INT_RX;
int_enable = int_enable & ~INT_RX;
sim_cancel (&rx_unit[1]);                               /* cancel drive 1 */
if (dptr->flags & DEV_DIS)                              /* disabled? */
    sim_cancel (&rx_unit[0]);
else if (rx_unit[0].flags & UNIT_BUF)  {                /* attached? */
    rx_state = INIT_COMPLETE;                           /* yes, sched init */
    sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK));
    }
else rx_done (rx_esr | RXES_ID, 0010);                  /* no, error */
return SCPE_OK;
}

/* Attach routine */

t_stat rx_attach (UNIT *uptr, CONST char *cptr)
{
uint32 sz;

if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) {
    if (sz > RX_SIZE)
        uptr->flags = uptr->flags | UNIT_DEN;
    else uptr->flags = uptr->flags & ~UNIT_DEN;
    }
uptr->capac = (uptr->flags & UNIT_DEN)? RX2_SIZE: RX_SIZE;
return attach_unit (uptr, cptr);
}

/* Set size routine */

t_stat rx_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if (uptr->flags & UNIT_ATT)
    return SCPE_ALATT;
if ((rx_28 == 0) && val)                                /* not on RX8E */
    return SCPE_NOFNC;
uptr->capac = val? RX2_SIZE: RX_SIZE;
return SCPE_OK;
}

/* Set controller type */

t_stat rx_settype (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 i;

if ((val < 0) || (val > 1) || (cptr != NULL))
    return SCPE_ARG;
if (val == rx_28)
    return SCPE_OK;
for (i = 0; i < RX_NUMDR; i++) {
    if (rx_unit[i].flags & UNIT_ATT)
        return SCPE_ALATT;
    }
for (i = 0; i < RX_NUMDR; i++) {
    if (val)
        rx_unit[i].flags = rx_unit[i].flags | UNIT_DEN | UNIT_AUTO;
    else rx_unit[i].flags = rx_unit[i].flags & ~(UNIT_DEN | UNIT_AUTO);
    rx_unit[i].capac = val? RX2_SIZE: RX_SIZE;
    }
rx_28 = val;
return SCPE_OK;
}

/* Show controller type */

t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
if (rx_28) fprintf (st, "RX28");
else fprintf (st, "RX8E");
return SCPE_OK;
}

/* Bootstrap routine */

#define BOOT_START      022
#define BOOT_ENTRY      022
#define BOOT_INST       060
#define BOOT_LEN        (sizeof (boot_rom) / sizeof (int16))
#define BOOT2_START     020
#define BOOT2_ENTRY     033
#define BOOT2_LEN       (sizeof (boot2_rom) / sizeof (int16))

static const uint16 boot_rom[] = {
    06755,                      /* 22, SDN */
    05022,                      /* 23, JMP .-1 */
    07126,                      /* 24, CLL CML RTL      ; read command + */
    01060,                      /* 25, TAD UNIT         ; unit no */
    06751,                      /* 26, LCD              ; load read+unit */
    07201,                      /* 27, CLA IAC          ; AC = 1 */
    04053,                      /* 30, JMS LOAD         ; load sector */
    04053,                      /* 31, JMS LOAD         ; load track */
    07104,                      /* 32, CLL RAL          ; AC = 2 */
    06755,                      /* 33, SDN */
    05054,                      /* 34, JMP LOAD+1 */
    06754,                      /* 35, SER */
    07450,                      /* 36, SNA              ; more to do? */
    07610,                      /* 37, CLA SKP          ; error */
    05046,                      /* 40, JMP 46           ; go empty */
    07402,                      /* 41-45, HALT          ; error */
    07402,
    07402,
    07402,
    07402,
    06751,                      /* 46, LCD              ; load empty */
    04053,                      /* 47, JMS LOAD         ; get data */
    03002,                      /* 50, DCA 2            ; store */
    02050,                      /* 51, ISZ 50           ; incr store */
    05047,                      /* 52, JMP 47           ; loop until done */
    00000,                      /* LOAD, 0 */
    06753,                      /* 54, STR */
    05033,                      /* 55, JMP 33 */
    06752,                      /* 56, XDR */
    05453,                      /* 57, JMP I LOAD */
    07024,                      /* UNIT, CML RAL        ; for unit 1 */
    06030                       /* 61, KCC */
    };

static const uint16 boot2_rom[] = {
    01061,                      /* READ, TAD UNIT       ; next unit+den */
    01046,                      /* 21, TAD CON360       ; add in 360 */
    00060,                      /* 22, AND CON420       ; mask to 420 */
    03061,                      /* 23, DCA UNIT         ; 400,420,0,20... */
    07327,                      /* 24, STL CLA IAC RTL  ; AC = 6 = read */
    01061,                      /* 25, TAD UNIT         ; +unit+den */
    06751,                      /* 26, LCD              ; load cmd */
    07201,                      /* 27, CLA IAC;         ; AC = 1 = trksec */
    04053,                      /* 30, JMS LOAD         ; load trk */
    04053,                      /* 31, JMS LOAD         ; load sec */
    07004,                      /* CN7004, RAL          ; AC = 2 = empty */
    06755,                      /* START, SDN           ; done? */
    05054,                      /* 34, JMP LOAD+1       ; check xfr */
    06754,                      /* 35, SER              ; error? */
    07450,                      /* 36, SNA              ; AC=0 on start */
    05020,                      /* 37, JMP RD           ; try next den,un */
    01061,                      /* 40, TAD UNIT         ; +unit+den */
    06751,                      /* 41, LCD              ; load cmd */
    01061,                      /* 42, TAD UNIT         ; set 60 for sec boot */
    00046,                      /* 43, AND CON360       ; only density */
    01032,                      /* 44, TAD CN7004       ; magic */
    03060,                      /* 45, DCA 60 */
    00360,                      /* CON360, 360          ; NOP */
    04053,                      /* 47, JMS LOAD         ; get data */
    03002,                      /* 50, DCA 2            ; store */
    02050,                      /* 51, ISZ .-1          ; incr store */
    05047,                      /* 52, JMP .-3          ; loop until done */
    00000,                      /* LOAD, 0 */
    06753,                      /* 54, STR              ; xfr ready? */
    05033,                      /* 55, JMP 33           ; no, chk done */
    06752,                      /* 56, XDR              ; get word */
    05453,                      /* 57, JMP I 53         ; return */
    00420,                      /* CON420, 420          ; toggle */
    00020                       /* UNIT, 20             ; unit+density */
    };

t_stat rx_boot (int32 unitno, DEVICE *dptr)
{
size_t i;
extern uint16 M[];

if (rx_dib.dev != DEV_RX)                               /* only std devno */
    return STOP_NOTSTD;
if (rx_28) {
    for (i = 0; i < BOOT2_LEN; i++)
        M[BOOT2_START + i] = boot2_rom[i];
    cpu_set_bootpc (BOOT2_ENTRY);
    }
else {
    for (i = 0; i < BOOT_LEN; i++)
        M[BOOT_START + i] = boot_rom[i];
    M[BOOT_INST] = unitno? 07024: 07004;
    cpu_set_bootpc (BOOT_ENTRY);
    }
return SCPE_OK;
}
Added src/PDP8/pdp8_sys.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_sys.c: PDP-8 simulator interface

   Copyright (c) 1993-2016, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   15-Dec-16    RMS     Added PKSTF (Dave Gesswein)
   17-Sep-13    RMS     Fixed recognition of initial field change (Dave Gesswein)
   24-Mar-09    RMS     Added link to FPP
   24-Jun-08    RMS     Fixed bug in new rim loader (Don North)
   24-May-08    RMS     Fixed signed/unsigned declaration inconsistency
   03-Sep-07    RMS     Added FPP8 support
                        Rewrote rim and binary loaders
   15-Dec-06    RMS     Added TA8E support, IOT disambiguation
   30-Oct-06    RMS     Added infinite loop stop
   18-Oct-06    RMS     Re-ordered device list
   17-Oct-03    RMS     Added TSC8-75, TD8E support, DECtape off reel message
   25-Apr-03    RMS     Revised for extended file support
   30-Dec-01    RMS     Revised for new TTX
   26-Nov-01    RMS     Added RL8A support
   17-Sep-01    RMS     Removed multiconsole support
   16-Sep-01    RMS     Added TSS/8 packed char support, added KL8A support
   27-May-01    RMS     Added multiconsole support
   18-Mar-01    RMS     Added DF32 support
   14-Mar-01    RMS     Added extension detection of RIM binary tapes
   15-Feb-01    RMS     Added DECtape support
   30-Oct-00    RMS     Added support for examine to file
   27-Oct-98    RMS     V2.4 load interface
   10-Apr-98    RMS     Added RIM loader support
   17-Feb-97    RMS     Fixed bug in handling of bin loader fields
*/

#include "pdp8_defs.h"
#include <ctype.h>

extern DEVICE cpu_dev;
extern UNIT cpu_unit;
extern DEVICE tsc_dev;
extern DEVICE fpp_dev;
extern DEVICE ptr_dev, ptp_dev;
extern DEVICE tti_dev, tto_dev;
extern DEVICE clk_dev, lpt_dev;
extern DEVICE rk_dev, rl_dev;
extern DEVICE rx_dev;
extern DEVICE df_dev, rf_dev;
extern DEVICE dt_dev, td_dev;
extern DEVICE mt_dev, ct_dev;
extern DEVICE ttix_dev, ttox_dev;
extern REG cpu_reg[];
extern uint16 M[];

t_stat fprint_sym_fpp (FILE *of, t_value *val);
t_stat parse_sym_fpp (CONST char *cptr, t_value *val);
CONST char *parse_field (CONST char *cptr, uint32 max, uint32 *val, uint32 c);
CONST char *parse_fpp_xr (CONST char *cptr, uint32 *xr, t_bool inc);
int32 test_fpp_addr (uint32 ad, uint32 max);

/* SCP data structures and interface routines

   sim_name             simulator name string
   sim_PC               pointer to saved PC register descriptor
   sim_emax             maximum number of words for examine/deposit
   sim_devices          array of pointers to simulated devices
   sim_consoles         array of pointers to consoles (if more than one)
   sim_stop_messages    array of pointers to stop messages
   sim_load             binary loader
*/

char sim_name[] = "PDP-8";

REG *sim_PC = &cpu_reg[0];

int32 sim_emax = 4;

DEVICE *sim_devices[] = {
    &cpu_dev,
    &tsc_dev,
    &fpp_dev,
    &clk_dev,
    &ptr_dev,
    &ptp_dev,
    &tti_dev,
    &tto_dev,
    &ttix_dev,
    &ttox_dev,
    &lpt_dev,
    &rk_dev,
    &rl_dev,
    &rx_dev,
    &df_dev,
    &rf_dev,
    &dt_dev,
    &td_dev,
    &mt_dev,
    &ct_dev,
    NULL
    };

const char *sim_stop_messages[] = {
    "Unknown error",
    "Unimplemented instruction",
    "HALT instruction",
    "Breakpoint",
    "Opcode Breakpoint",
    "Non-standard device number",
    "DECtape off reel",
    "Infinite loop"
    };

/* Ambiguous device list - these devices have overlapped IOT codes */

DEVICE *amb_dev[] = {
    &rl_dev,
    &ct_dev,
    &td_dev,
    NULL
    };

#define AMB_RL      (1 << 12)
#define AMB_CT      (2 << 12)
#define AMB_TD      (3 << 12)

/* RIM loader format consists of alternating pairs of addresses and 12-bit
   words.  It can only operate in field 0 and is not checksummed.
*/

t_stat sim_load_rim (FILE *fi)
{
int32 origin, hi, lo, wd;

origin = 0200;
do {                                                    /* skip leader */
    if ((hi = getc (fi)) == EOF)
        return SCPE_FMT;
    } while ((hi == 0) || (hi >= 0200));
do {                                                    /* data block */
    if ((lo = getc (fi)) == EOF)
        return SCPE_FMT;
    wd = (hi << 6) | lo;
    if (wd > 07777)
        origin = wd & 07777;
    else M[origin++ & 07777] = wd;
    if ((hi = getc (fi)) == EOF)
        return SCPE_FMT;
    } while (hi < 0200);                                /* until trailer */
return SCPE_OK;
}

/* BIN loader format consists of a string of 12-bit words (made up from
   7-bit characters) between leader and trailer (200).  The last word on
   tape is the checksum.  A word with the "link" bit set is a new origin;
   a character > 0200 indicates a change of field.
*/

int32 sim_bin_getc (FILE *fi, uint32 *newf)
{
int32 c, rubout;

rubout = 0;                                             /* clear toggle */
while ((c = getc (fi)) != EOF) {                        /* read char */
    if (rubout)                                         /* toggle set? */
        rubout = 0;                                     /* clr, skip */
    else if (c == 0377)                                 /* rubout? */
        rubout = 1;                                     /* set, skip */
    else if (c > 0200)                                  /* channel 8 set? */
        *newf = (c & 070) << 9;                         /* change field */
    else return c;                                      /* otherwise ok */
    }
return EOF;
}

t_stat sim_load_bin (FILE *fi)
{
int32 hi, lo, wd, csum, t;
uint32 field, newf, origin;
int32 sections_read = 0;

for (;;) {
    csum = origin = field = newf = 0;                   /* init */
    do {                                                /* skip leader */
        if ((hi = sim_bin_getc (fi, &newf)) == EOF) {
            if (sections_read != 0) {
                sim_printf ("%d sections sucessfully read\n\r", sections_read);
                return SCPE_OK;
                } 
            else
                return SCPE_FMT;
            }
        } while ((hi == 0) || (hi >= 0200));
    for (;;) {                                          /* data blocks */
        if ((lo = sim_bin_getc (fi, &newf)) == EOF)     /* low char */
            return SCPE_FMT;
        wd = (hi << 6) | lo;                            /* form word */
        t = hi;                                         /* save for csum */
        if ((hi = sim_bin_getc (fi, &newf)) == EOF)     /* next char */
            return SCPE_FMT;
        if (hi == 0200) {                               /* end of tape? */
            if ((csum - wd) & 07777) {                  /* valid csum? */
                if (sections_read != 0)
                    sim_printf ("%d sections sucessfully read\n\r", sections_read);
                return SCPE_CSUM;
                }
            if (!(sim_switches & SWMASK ('A')))        /* Load all sections? */
                return SCPE_OK;
            sections_read++;
            break;
            }
        csum = csum + t + lo;                           /* add to csum */
        if (wd > 07777)                                 /* chan 7 set? */
            origin = wd & 07777;                        /* new origin */
        else {                                          /* no, data */
            if ((field | origin) >= MEMSIZE) 
                return SCPE_NXM;
            M[field | origin] = wd;
            origin = (origin + 1) & 07777;
            }
        field = newf;                                   /* update field */
        }
    }
return SCPE_IERR;
}

/* Binary loader
   Two loader formats are supported: RIM loader (-r) and BIN (-b) loader. */

t_stat sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
{
if ((*cptr != 0) || (flag != 0))
    return SCPE_ARG;
if ((sim_switches & SWMASK ('R')) ||                    /* RIM format? */
    (match_ext (fnam, "RIM") && !(sim_switches & SWMASK ('B'))))
    return sim_load_rim (fileref);
else return sim_load_bin (fileref);                     /* no, BIN */
}

/* Symbol tables */

#define I_V_FL          18                              /* flag start */
#define I_M_FL          07                              /* flag mask */
#define I_V_NPN         0                               /* no operand */
#define I_V_FLD         1                               /* field change */
#define I_V_MRF         2                               /* mem ref */
#define I_V_IOT         3                               /* general IOT */
#define I_V_OP1         4                               /* operate 1 */
#define I_V_OP2         5                               /* operate 2 */
#define I_V_OP3         6                               /* operate 3 */
#define I_V_IOA         7                               /* ambiguous IOT */
#define I_NPN           (I_V_NPN << I_V_FL)
#define I_FLD           (I_V_FLD << I_V_FL)
#define I_MRF           (I_V_MRF << I_V_FL)
#define I_IOT           (I_V_IOT << I_V_FL)
#define I_OP1           (I_V_OP1 << I_V_FL)
#define I_OP2           (I_V_OP2 << I_V_FL)
#define I_OP3           (I_V_OP3 << I_V_FL)
#define I_IOA           (I_V_IOA << I_V_FL)

static const int32 masks[] = {
    07777, 07707, 07000, 07000,
    07416, 07571, 017457, 077777,
    };

/* Ambiguous device mnemonics must precede default mnemonics */

static const char *opcode[] = {
 "SKON", "ION", "IOF", "SRQ",                           /* std IOTs */
 "GTF", "RTF", "SGT", "CAF",
 "RPE", "RSF", "RRB", "RFC", "RFC RRB",                 /* reader/punch */
 "PCE", "PSF", "PCF", "PPC", "PLS",
 "KCF", "KSF", "KCC", "KRS", "KIE", "KRB",              /* console */
 "TLF", "TSF", "TCF", "TPC", "SPI", "TLS",
 "SBE", "SPL", "CAL",                                   /* power fail */
 "CLEI", "CLDI", "CLSC", "CLLE", "CLCL", "CLSK",        /* clock */
 "CINT", "RDF", "RIF", "RIB",                           /* mem mmgt */
 "RMF", "SINT", "CUF", "SUF",
 "RLDC", "RLSD", "RLMA", "RLCA",                        /* RL - ambiguous */
 "RLCB", "RLSA", "RLWC",
 "RRER", "RRWC", "RRCA", "RRCB",
 "RRSA", "RRSI", "RLSE",
 "KCLR", "KSDR", "KSEN", "KSBF",                        /* CT - ambiguous */
 "KLSA", "KSAF", "KGOA", "KRSB",
 "SDSS", "SDST", "SDSQ",                                /* TD - ambiguous */
 "SDLC", "SDLD", "SDRC", "SDRD",
 "ADCL", "ADLM", "ADST", "ADRB",                        /* A/D */
 "ADSK", "ADSE", "ADLE", "ADRS",
 "DCMA", "DMAR", "DMAW",                                /* DF/RF */
 "DCIM", "DSAC", "DIML", "DIMA",
 "DCEA",         "DEAL", "DEAC",
 "DFSE", "DFSC", "DISK", "DMAC",
 "DCXA", "DXAL", "DXAC",
 "PKSTF", "PSKF", "PCLF", "PSKE",                                /* LPT */
 "PSTB", "PSIE", "PCLF PSTB", "PCIE",
 "LWCR", "CWCR", "LCAR",                                /* MT */
 "CCAR", "LCMR", "LFGR", "LDBR",
 "RWCR", "CLT", "RCAR",
 "RMSR", "RCMR", "RFSR", "RDBR",
 "SKEF", "SKCB", "SKJD", "SKTR", "CLF",
 "DSKP", "DCLR", "DLAG",                                /* RK */
 "DLCA", "DRST", "DLDC", "DMAN",
 "LCD", "XDR", "STR",                                   /* RX */
 "SER", "SDN", "INTR", "INIT",
 "DTRA", "DTCA", "DTXA", "DTLA",                        /* DT */
 "DTSF", "DTRB", "DTLB",
 "ETDS", "ESKP", "ECTF", "ECDF",                        /* TSC75 */
 "ERTB", "ESME", "ERIOT", "ETEN",
 "FFST", "FPINT", "FPICL", "FPCOM",                     /* FPP8 */
 "FPHLT", "FPST", "FPRST", "FPIST",
        "FMODE",        "FMRB",
 "FMRP", "FMDO",        "FPEP",

 "CDF", "CIF", "CIF CDF",
 "AND", "TAD", "ISZ", "DCA", "JMS", "JMP", "IOT",
 "NOP", "NOP2", "NOP3", "SWAB", "SWBA",
 "STL", "GLK", "STA", "LAS", "CIA",
 "BSW", "RAL", "RTL", "RAR", "RTR", "RAL RAR", "RTL RTR",
 "SKP", "SNL", "SZL",
 "SZA", "SNA", "SZA SNL", "SNA SZL",
 "SMA", "SPA", "SMA SNL", "SPA SZL",
 "SMA SZA", "SPA SNA", "SMA SZA SNL", "SPA SNA SZL",
 "SCL", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR",
 "SCA", "SCA SCL", "SCA MUY", "SCA DVI",
 "SCA NMI", "SCA SHL", "SCA ASR", "SCA LSR",
 "ACS", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR",
 "SCA", "DAD", "DST", "SWBA",
 "DPSZ", "DPIC", "DCIM", "SAM",
 "CLA", "CLL", "CMA", "CML", "IAC",                     /* encode only */
 "CLA", "OAS", "HLT",
 "CLA", "MQA", "MQL",
 NULL, NULL, NULL, NULL,                                /* decode only */
 NULL
 };
 
static const int32 opc_val[] = {
 06000+I_NPN, 06001+I_NPN, 06002+I_NPN, 06003+I_NPN,
 06004+I_NPN, 06005+I_NPN, 06006+I_NPN, 06007+I_NPN,
 06010+I_NPN, 06011+I_NPN, 06012+I_NPN, 06014+I_NPN, 06016+I_NPN,
 06020+I_NPN, 06021+I_NPN, 06022+I_NPN, 06024+I_NPN, 06026+I_NPN,
 06030+I_NPN, 06031+I_NPN, 06032+I_NPN, 06034+I_NPN, 06035+I_NPN, 06036+I_NPN,
 06040+I_NPN, 06041+I_NPN, 06042+I_NPN, 06044+I_NPN, 06045+I_NPN, 06046+I_NPN,
 06101+I_NPN, 06102+I_NPN, 06103+I_NPN,
 06131+I_NPN, 06132+I_NPN, 06133+I_NPN, 06135+I_NPN, 06136+I_NPN, 06137+I_NPN,
 06204+I_NPN, 06214+I_NPN, 06224+I_NPN, 06234+I_NPN,
 06244+I_NPN, 06254+I_NPN, 06264+I_NPN, 06274+I_NPN,
 06600+I_IOA+AMB_RL, 06601+I_IOA+AMB_RL, 06602+I_IOA+AMB_RL, 06603+I_IOA+AMB_RL,
 06604+I_IOA+AMB_RL, 06605+I_IOA+AMB_RL, 06607+I_IOA+AMB_RL,
 06610+I_IOA+AMB_RL, 06611+I_IOA+AMB_RL, 06612+I_IOA+AMB_RL, 06613+I_IOA+AMB_RL,
 06614+I_IOA+AMB_RL, 06615+I_IOA+AMB_RL, 06617+I_IOA+AMB_RL,
 06700+I_IOA+AMB_CT, 06701+I_IOA+AMB_CT, 06702+I_IOA+AMB_CT, 06703+I_IOA+AMB_CT,
 06704+I_IOA+AMB_CT, 06705+I_IOA+AMB_CT, 06706+I_IOA+AMB_CT, 06707+I_IOA+AMB_CT,
 06771+I_IOA+AMB_TD, 06772+I_IOA+AMB_TD, 06773+I_IOA+AMB_TD,
 06774+I_IOA+AMB_TD, 06775+I_IOA+AMB_TD, 06776+I_IOA+AMB_TD, 06777+I_IOA+AMB_TD,
 06530+I_NPN, 06531+I_NPN, 06532+I_NPN, 06533+I_NPN,    /* AD */
 06534+I_NPN, 06535+I_NPN, 06536+I_NPN, 06537+I_NPN,
 06660+I_NPN, 06601+I_NPN, 06603+I_NPN, 06605+I_NPN,                 /* DF/RF */
 06611+I_NPN, 06612+I_NPN, 06615+I_NPN, 06616+I_NPN,
 06611+I_NPN,              06615+I_NPN, 06616+I_NPN,
 06621+I_NPN, 06622+I_NPN, 06623+I_NPN, 06626+I_NPN,
 06641+I_NPN, 06643+I_NPN, 06645+I_NPN,
 06661+I_NPN, 06662+I_NPN, 06663+I_NPN,                 /* LPT */
 06664+I_NPN, 06665+I_NPN, 06666+I_NPN, 06667+I_NPN,
 06701+I_NPN, 06702+I_NPN, 06703+I_NPN,                 /* MT */
 06704+I_NPN, 06705+I_NPN, 06706+I_NPN, 06707+I_NPN,
 06711+I_NPN, 06712+I_NPN, 06713+I_NPN,
 06714+I_NPN, 06715+I_NPN, 06716+I_NPN, 06717+I_NPN,
 06721+I_NPN, 06722+I_NPN, 06723+I_NPN, 06724+I_NPN, 06725+I_NPN,
 06741+I_NPN, 06742+I_NPN, 06743+I_NPN,                 /* RK */
 06744+I_NPN, 06745+I_NPN, 06746+I_NPN, 06747+I_NPN,
 06751+I_NPN, 06752+I_NPN, 06753+I_NPN,                 /* RX */
 06754+I_NPN, 06755+I_NPN, 06756+I_NPN, 06757+I_NPN,
 06761+I_NPN, 06762+I_NPN, 06764+I_NPN, 06766+I_NPN,    /* DT */
 06771+I_NPN, 06772+I_NPN, 06774+I_NPN,
 06360+I_NPN, 06361+I_NPN, 06362+I_NPN, 06363+I_NPN,    /* TSC */
 06364+I_NPN, 06365+I_NPN, 06366+I_NPN, 06367+I_NPN,
 06550+I_NPN, 06551+I_NPN, 06552+I_NPN, 06553+I_NPN,    /* FPP8 */
 06554+I_NPN, 06555+I_NPN, 06556+I_NPN, 06557+I_NPN,
              06561+I_NPN,              06563+I_NPN,
 06564+I_NPN, 06565+I_NPN,              06567+I_NPN,

 06201+I_FLD, 06202+I_FLD, 06203+I_FLD,
 00000+I_MRF, 01000+I_MRF, 02000+I_MRF, 03000+I_MRF,
 04000+I_MRF, 05000+I_MRF, 06000+I_IOT,
 07000+I_NPN, 07400+I_NPN, 07401+I_NPN, 07431+I_NPN, 07447+I_NPN,
 07120+I_NPN, 07204+I_NPN, 07240+I_NPN, 07604+I_NPN, 07041+I_NPN,
 07002+I_OP1, 07004+I_OP1, 07006+I_OP1,
 07010+I_OP1, 07012+I_OP1, 07014+I_OP1, 07016+I_OP1,
 07410+I_OP2, 07420+I_OP2, 07430+I_OP2,
 07440+I_OP2, 07450+I_OP2, 07460+I_OP2, 07470+I_OP2,
 07500+I_OP2, 07510+I_OP2, 07520+I_OP2, 07530+I_OP2,
 07540+I_OP2, 07550+I_OP2, 07560+I_OP2, 07570+I_OP2,
 07403+I_OP3, 07405+I_OP3, 07407+I_OP3,
 07411+I_OP3, 07413+I_OP3, 07415+I_OP3, 07417+I_OP3,
 07441+I_OP3, 07443+I_OP3, 07445+I_OP3, 07447+I_OP3,
 07451+I_OP3, 07453+I_OP3, 07455+I_OP3, 07457+I_OP3,
 017403+I_OP3, 017405+I_OP3, 0174017+I_OP3,
 017411+I_OP3, 017413+I_OP3, 017415+I_OP3, 017417+I_OP3,
 017441+I_OP3, 017443+I_OP3, 017445+I_OP3, 017447+I_OP3,
 017451+I_OP3, 017453+I_OP3, 017455+I_OP3, 017457+I_OP3,
 07200+I_OP1, 07100+I_OP1, 07040+I_OP1, 07020+I_OP1, 07001+I_OP1,
 07600+I_OP2, 07404+I_OP2, 07402+I_OP2,
 07601+I_OP3, 07501+I_OP3, 07421+I_OP3,
 07000+I_OP1, 07400+I_OP2, 07401+I_OP3, 017401+I_OP3,
 -1
 };

/* Symbol tables for FPP-8 */

#define F_V_FL          18                              /* flag start */
#define F_M_FL          017                             /* flag mask */
#define F_V_NOP12       0                               /* no opnd 12b */
#define F_V_NOP9        1                               /* no opnd 9b */
#define F_V_AD15        2                               /* 15b dir addr */
#define F_V_AD15X       3                               /* 15b dir addr indx */
#define F_V_IMMX        4                               /* 12b immm indx */
#define F_V_X           5                               /* index */
#define F_V_MRI         6                               /* mem ref ind */
#define F_V_MR1D        7                               /* mem ref dir 1 word */
#define F_V_MR2D        8                               /* mem ref dir 2 word */
#define F_V_LEMU        9                               /* LEA/IMUL */
#define F_V_LEMUI       10                              /* LEAI/IMULI */
#define F_V_LTR         11                              /* LTR */
#define F_V_MRD         12                              /* mem ref direct (enc) */
#define F_NOP12         (F_V_NOP12 << F_V_FL)
#define F_NOP9          (F_V_NOP9 << F_V_FL)
#define F_AD15          (F_V_AD15 << F_V_FL)
#define F_AD15X         (F_V_AD15X << F_V_FL)
#define F_IMMX          (F_V_IMMX << F_V_FL)
#define F_X             (F_V_X << F_V_FL)
#define F_MRI           (F_V_MRI << F_V_FL)
#define F_MR1D          (F_V_MR1D << F_V_FL)
#define F_MR2D          (F_V_MR2D << F_V_FL)
#define F_LEMU          (F_V_LEMU << F_V_FL)
#define F_LEMUI         (F_V_LEMUI << F_V_FL)
#define F_LTR           (F_V_LTR << F_V_FL)
#define F_MRD           (F_V_MRD << F_V_FL)

static const uint32 fmasks[] = {
    07777, 07770, 07770, 07600,
    07770, 07770, 07600, 07600,
    07600, 017600, 017600, 07670,
    07777
    };

/* Memory references are encode dir / decode 1D / decode 2D / indirect */

static const char *fopcode[] = {
    "FEXIT",    "FPAUSE",   "FCLA",     "FNEG",
    "FNORM",    "STARTF",   "STARTD",   "JAC",
                "ALN",      "ATX",      "XTA",
    "FNOP",     "STARTE",
    "LDX",      "ADDX",
    "FLDA",     "FLDA",     "FLDA",     "FLDAI",
    "JEQ",      "JGE",      "JLE",      "JA",
    "JNE",      "JLT",      "JGT",      "JAL",
    "SETX",     "SETB",     "JSA",      "JSR",
    "FADD",     "FADD",     "FADD",     "FADDI",
    "JNX",
    "FSUB",     "FSUB",     "FSUB",     "FSUBI",
    "TRAP3",
    "FDIV",     "FDIV",     "FDIV",     "FDIVI",
    "TRAP4",
    "FMUL",     "FMUL",     "FMUL",     "FMULI",
    "LTREQ",    "LTRGE",    "LTRLE",    "LTRA",
    "LTRNE",    "LTRLT",    "LTRGT",    "LTRAL",
    "FADDM",    "FADDM",    "FADDM",    "FADDMI",
    "IMUL",     "LEA",
    "FSTA",     "FSTA",     "FSTA",     "FSTAI",
    "IMULI",    "LEAI",
    "FMULM",    "FMULM",    "FMULM",    "FMULMI",
    NULL
    };

static const int32 fop_val[] = {
    00000+F_NOP12,  00001+F_NOP12,  00002+F_NOP12,  00003+F_NOP12,
    00004+F_NOP12,  00005+F_NOP12,  00006+F_NOP12,  00007+F_NOP12,
                    00010+F_X,      00020+F_X,      00030+F_X,
    00040+F_NOP9,   00050+F_NOP9,
    00100+F_IMMX,   00110+F_IMMX,
    00000+F_MRD,    00200+F_MR1D,   00400+F_MR2D,   00600+F_MRI,
    01000+F_AD15,   01010+F_AD15,   01020+F_AD15,   01030+F_AD15,
    01040+F_AD15,   01050+F_AD15,   01060+F_AD15,   01070+F_AD15,
    01100+F_AD15,   01110+F_AD15,   01120+F_AD15,   01130+F_AD15,
    01000+F_MRD,    01200+F_MR1D,   01400+F_MR2D,   01600+F_MRI,
    02000+F_AD15X,
    02000+F_MRD,    02200+F_MR1D,   02400+F_MR2D,   02600+F_MRI,
    03000+F_AD15,
    03000+F_MRD,    03200+F_MR1D,   03400+F_MR2D,   03600+F_MRI,
    04000+F_AD15,
    04000+F_MRD,    04200+F_MR1D,   04400+F_MR2D,   04600+F_MRI,
    05000+F_LTR,    05010+F_LTR,    05020+F_LTR,    05030+F_LTR,
    05040+F_LTR,    05050+F_LTR,    05060+F_LTR,    05070+F_LTR,
    05000+F_MRD,    05200+F_MR1D,   05400+F_MR2D,   05600+F_MRI,
    016000+F_LEMU,  006000+F_LEMU,
    06000+F_MRD,    06200+F_MR1D,   06400+F_MR2D,   06600+F_MRI,
    017000+F_LEMUI, 007000+F_LEMUI,
    07000+F_MRD,    07200+F_MR1D,   07400+F_MR2D,   07600+F_MRI,
    -1
    };

/* Operate decode

   Inputs:
        *of     =       output stream
        inst    =       mask bits
        Class   =       instruction class code
        sp      =       space needed?
   Outputs:
        status  =       space needed
*/

int32 fprint_opr (FILE *of, int32 inst, int32 Class, int32 sp)
{
int32 i, j;

for (i = 0; opc_val[i] >= 0; i++) {                     /* loop thru ops */
    j = (opc_val[i] >> I_V_FL) & I_M_FL;                /* get class */
    if ((j == Class) && (opc_val[i] & inst)) {          /* same class? */
        inst = inst & ~opc_val[i];                      /* mask bit set? */
        fprintf (of, (sp? " %s": "%s"), opcode[i]);
        sp = 1;
        }
    }
return sp;
}

/* Symbolic decode

   Inputs:
        *of     =       output stream
        addr    =       current PC
        *val    =       pointer to data
        *uptr   =       pointer to unit 
        sw      =       switches
   Outputs:
        return  =       status code
*/

#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x)
#define SIXTOASC(x) (((x) >= 040)? (x): (x) + 0100)
#define TSSTOASC(x) ((x) + 040)

t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
    UNIT *uptr, int32 sw)
{
int32 cflag, i, j, sp, inst, disp, opc;
extern int32 emode;
t_stat r;

cflag = (uptr == NULL) || (uptr == &cpu_unit);
inst = val[0];
if (sw & SWMASK ('A')) {                                /* ASCII? */
    if (inst > 0377)
        return SCPE_ARG;
    fprintf (of, FMTASC (inst & 0177));
    return SCPE_OK;
    }
if (sw & SWMASK ('C')) {                                /* characters? */
    fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077));
    fprintf (of, "%c", SIXTOASC (inst & 077));
    return SCPE_OK;
    }
if (sw & SWMASK ('T')) {                                /* TSS8 packed? */
    fprintf (of, "%c", TSSTOASC ((inst >> 6) & 077));
    fprintf (of, "%c", TSSTOASC (inst & 077));
    return SCPE_OK;
    }
if ((sw & SWMASK ('F')) &&                              /* FPP8? */
    ((r = fprint_sym_fpp (of, val)) != SCPE_ARG))
    return r;
if (!(sw & SWMASK ('M')))
    return SCPE_ARG;

/* Instruction decode */

opc = (inst >> 9) & 07;                                 /* get major opcode */
if (opc == 07)                                          /* operate? */
    inst = inst | ((emode & 1) << 12);                  /* include EAE mode */
if (opc == 06) {                                        /* IOT? */
    DEVICE *dptr;
    DIB *dibp;
    uint32 dno = (inst >> 3) & 077;
    for (i = 0; (dptr = amb_dev[i]) != NULL; i++) {     /* check amb devices */
        if ((dptr->ctxt == NULL) ||                     /* no DIB or */
            (dptr->flags & DEV_DIS)) continue;          /* disabled? skip */
        dibp = (DIB *) dptr->ctxt;                      /* get DIB */
        if ((dno >= dibp->dev) ||                       /* IOT for this dev? */
            (dno < (dibp->dev + dibp->num))) {
            inst = inst | ((i + 1) << 12);              /* disambiguate */
            break;                                      /* done */
            }
        }
    }
        
for (i = 0; opc_val[i] >= 0; i++) {                     /* loop thru ops */
    j = (opc_val[i] >> I_V_FL) & I_M_FL;                /* get class */
    if ((opc_val[i] & 077777) == (inst & masks[j])) {   /* match? */

        switch (j) {                                    /* case on class */

        case I_V_NPN: case I_V_IOA:                     /* no operands */
            fprintf (of, "%s", opcode[i]);              /* opcode */
            break;

        case I_V_FLD:                                   /* field change */
            fprintf (of, "%s %-o", opcode[i], (inst >> 3) & 07);
            break;

        case I_V_MRF:                                   /* mem ref */
            disp = inst & 0177;                         /* displacement */
            fprintf (of, "%s%s", opcode[i], ((inst & 00400)? " I ": " "));
            if (inst & 0200) {                          /* current page? */
                if (cflag)
                    fprintf (of, "%-o", (addr & 07600) | disp);
                else fprintf (of, "C %-o", disp);
                }
            else fprintf (of, "%-o", disp);             /* page zero */
            break;

        case I_V_IOT:                                   /* IOT */
            fprintf (of, "%s %-o", opcode[i], inst & 0777);
            break;

        case I_V_OP1:                                   /* operate group 1 */
            sp = fprint_opr (of, inst & 0361, j, 0);
            if (opcode[i])
                fprintf (of, (sp? " %s": "%s"), opcode[i]);
            break;

        case I_V_OP2:                                   /* operate group 2 */
            if (opcode[i])
                fprintf (of, "%s", opcode[i]); /* skips */
            fprint_opr (of, inst & 0206, j, opcode[i] != NULL);
            break;      

        case I_V_OP3:                                   /* operate group 3 */
            sp = fprint_opr (of, inst & 0320, j, 0);
            if (opcode[i])
                fprintf (of, (sp? " %s": "%s"), opcode[i]);
            break;
            }                                           /* end case */

        return SCPE_OK;
        }                                               /* end if */
    }                                                   /* end for */
return SCPE_ARG;
}

/* Symbolic input

   Inputs:
        *cptr   =       pointer to input string
        addr    =       current PC
        *uptr   =       pointer to unit
        *val    =       pointer to output values
        sw      =       switches
   Outputs:
        status  =       error status
*/

t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
{
uint32 cflag, d, i, j, k;
t_stat r;
char gbuf[CBUFSIZE];

cflag = (uptr == NULL) || (uptr == &cpu_unit);
while (isspace (*cptr)) cptr++;                         /* absorb spaces */
if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */
    if (cptr[0] == 0)                                   /* must have 1 char */
        return SCPE_ARG;
    val[0] = (t_value) cptr[0] | 0200;
    return SCPE_OK;
    }
if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */
    if (cptr[0] == 0)                                   /* must have 1 char */
        return SCPE_ARG;
    val[0] = (((t_value) cptr[0] & 077) << 6) |
              ((t_value) cptr[1] & 077);
    return SCPE_OK;
    }
if ((sw & SWMASK ('T')) || ((*cptr == '"') && cptr++)) { /* TSS8 string? */
    if (cptr[0] == 0)                                   /* must have 1 char */
        return SCPE_ARG;
    val[0] = (((t_value) (cptr[0] - 040) & 077) << 6) |
              ((t_value) (cptr[1] - 040) & 077);
    return SCPE_OK;
    }
if ((r = parse_sym_fpp (cptr, val)) != SCPE_ARG)        /* FPP8 inst? */
    return r;

/* Instruction parse */

cptr = get_glyph (cptr, gbuf, 0);                       /* get opcode */
for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ;
if (opcode[i] == NULL)
    return SCPE_ARG;
val[0] = opc_val[i] & 07777;                            /* get value */
j = (opc_val[i] >> I_V_FL) & I_M_FL;                    /* get class */

switch (j) {                                            /* case on class */

    case I_V_IOT:                                       /* IOT */
        if ((cptr = parse_field (cptr, 0777, &d, 0)) == NULL)
            return SCPE_ARG;                            /* get dev+pulse */
        val[0] = val[0] | d;
        break;

    case I_V_FLD:                                       /* field */
        for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0;
            cptr = get_glyph (cptr, gbuf, 0)) {
            for (i = 0; (opcode[i] != NULL) &&
                        (strcmp (opcode[i], gbuf) != 0) ; i++) ;
            if (opcode[i] != NULL) {
                k = (opc_val[i] >> I_V_FL) & I_M_FL;
                if (k != j)
                    return SCPE_ARG;
                val[0] = val[0] | (opc_val[i] & 07777);
                }
            else {
                d = get_uint (gbuf, 8, 07, &r);
                if (r != SCPE_OK)
                    return SCPE_ARG;
                val[0] = val[0] | (d << 3);
                break;
                }
            }
        break;

    case I_V_MRF:                                       /* mem ref */
        cptr = get_glyph (cptr, gbuf, 0);               /* get next field */
        if (strcmp (gbuf, "I") == 0) {                  /* indirect? */
            val[0] = val[0] | 0400;
            cptr = get_glyph (cptr, gbuf, 0);
            }
        if ((k = (strcmp (gbuf, "C") == 0)) || (strcmp (gbuf, "Z") == 0)) {
            if ((cptr = parse_field (cptr, 0177, &d, 0)) == NULL)
                return SCPE_ARG;
            val[0] = val[0] | d | (k? 0200: 0);
            }
        else {
            d = get_uint (gbuf, 8, 07777, &r);
            if (r != SCPE_OK)
                return SCPE_ARG;
            if (d <= 0177)
                val[0] = val[0] | d;
            else if (cflag && (((addr ^ d) & 07600) == 0))
                val[0] = val[0] | (d & 0177) | 0200;
            else return SCPE_ARG;
            }
        break;

    case I_V_OP1: case I_V_OP2: case I_V_OP3:           /* operates */
    case I_V_NPN: case I_V_IOA:
        for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0;
            cptr = get_glyph (cptr, gbuf, 0)) {
            for (i = 0; (opcode[i] != NULL) &&
                        (strcmp (opcode[i], gbuf) != 0) ; i++) ;
            k = opc_val[i] & 07777;
            if ((opcode[i] == NULL) || (((k ^ val[0]) & 07000) != 0))
                return SCPE_ARG;
            val[0] = val[0] | k;
            }
        break;
        }                                               /* end case */

if (*cptr != 0) return SCPE_ARG;                        /* junk at end? */
return SCPE_OK;
}

/* FPP8 instruction decode */

t_stat fprint_sym_fpp (FILE *of, t_value *val)
{
uint32 wd1, wd2, xr4b, xr3b, ad15;
uint32 i, j;
extern uint32 fpp_bra, fpp_cmd;

wd1 = (uint32) val[0] | ((fpp_cmd & 04000) << 1);
wd2 = (uint32) val[1];
xr4b = (wd1 >> 3) & 017;
xr3b = wd1 & 07;
ad15 = (xr3b << 12) | wd2;

for (i = 0; fop_val[i] >= 0; i++) {                     /* loop thru ops */
    j = (fop_val[i] >> F_V_FL) & F_M_FL;                /* get class */
    if ((fop_val[i] & 017777) == (wd1 & fmasks[j])) {   /* match? */

        switch (j) {                                    /* case on class */
        case F_V_NOP12:
        case F_V_NOP9:
        case F_V_LTR:                                   /* no operands */
            fprintf (of, "%s", fopcode[i]);
            break;

        case F_V_X:                                     /* index */
            fprintf (of, "%s %o", fopcode[i], xr3b);
            break;

        case F_V_IMMX:                                  /* index imm */
            fprintf (of, "%s %-o,%o", fopcode[i], wd2, xr3b);
            return -1;                                  /* extra word */

        case F_V_AD15:                                  /* 15b address */
            fprintf (of, "%s %-o", fopcode[i], ad15);
            return -1;                                  /* extra word */

        case F_V_AD15X:                                 /* 15b addr, indx */
            fprintf (of, "%s %-o", fopcode[i], ad15);
            if (xr4b >= 010)
                fprintf (of, ",%o+", xr4b & 7);
            else fprintf (of, ",%o", xr4b);
            return -1;                                  /* extra word */

        case F_V_MR1D:                                  /* 1 word direct */
            ad15 = (fpp_bra + (3 * (wd1 & 0177))) & ADDRMASK;
            fprintf (of, "%s %-o", fopcode[i], ad15);
            break;

        case F_V_LEMU:
        case F_V_MR2D:                                  /* 2 word direct */
            fprintf (of, "%s %-o", fopcode[i], ad15);
            if (xr4b >= 010)
                fprintf (of, ",%o+", xr4b & 7);
            else if (xr4b != 0)
                fprintf (of, ",%o", xr4b);
            return -1;                                  /* extra word */

        case F_V_LEMUI:
        case F_V_MRI:                                   /* indirect */
            ad15 = (fpp_bra + (3 * xr3b)) & ADDRMASK;
            fprintf (of, "%s %-o", fopcode[i], ad15);
            if (xr4b >= 010)
                fprintf (of, ",%o+", xr4b & 7);
            else if (xr4b != 0)
                fprintf (of, ",%o", xr4b);
            break;

        case F_V_MRD:                                   /* encode only */
            return SCPE_IERR;
            }

        return SCPE_OK;
        }                                               /* end if */
    }                                                   /* end for */
return SCPE_ARG;
}

/* FPP8 instruction parse */

t_stat parse_sym_fpp (CONST char *cptr, t_value *val)
{
uint32 i, j, ad, xr;
int32 broff, nwd;
char gbuf[CBUFSIZE];

cptr = get_glyph (cptr, gbuf, 0);                       /* get opcode */
for (i = 0; (fopcode[i] != NULL) && (strcmp (fopcode[i], gbuf) != 0) ; i++) ;
if (fopcode[i] == NULL) return SCPE_ARG;
val[0] = fop_val[i] & 07777;                            /* get value */
j = (fop_val[i] >> F_V_FL) & F_M_FL;                    /* get class */
xr = 0;
nwd = 0;

switch (j) {                                            /* case on class */

    case F_V_NOP12:
    case F_V_NOP9:
    case F_V_LTR:                                       /* no operands */
        break;

    case F_V_X:                                         /* 3b XR */
        if ((cptr = parse_field (cptr, 07, &xr, 0)) == NULL)
            return SCPE_ARG;
        val[0] |= xr;
        break;

    case F_V_IMMX:                                      /* 12b, XR */
        if ((cptr = parse_field (cptr, 07777, &ad, ',')) == NULL)
            return SCPE_ARG;
        if ((*cptr == 0) ||
            ((cptr = parse_fpp_xr (cptr, &xr, FALSE)) == NULL))
            return SCPE_ARG;
        val[0] |= xr;
        val[++nwd] = ad;
        break;

    case F_V_AD15:                                      /* 15b addr */
        if ((cptr = parse_field (cptr, 077777, &ad, 0)) == NULL)
            return SCPE_ARG;
        val[0] |= (ad >> 12) & 07;
        val[++nwd] = ad & 07777;
        break;

    case F_V_AD15X:                                     /* 15b addr, idx */
        if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL)
            return SCPE_ARG;
        if ((*cptr == 0) ||
            ((cptr = parse_fpp_xr (cptr, &xr, FALSE)) == NULL))
            return SCPE_ARG;
        val[0] |= ((xr << 3) | ((ad >> 12) & 07));
        val[++nwd] = ad & 07777;
        break;

    case F_V_LEMUI:
    case F_V_MRI:                                       /* indirect */
        if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL)
            return SCPE_ARG;
        if ((*cptr != 0) &&
            ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL))
            return SCPE_ARG;
        if ((broff = test_fpp_addr (ad, 07)) < 0)
            return SCPE_ARG;
        val[0] |= ((xr << 3) | broff);
        break;

    case F_V_MRD:                                       /* direct */
        if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL)
            return SCPE_ARG;
        if (((broff = test_fpp_addr (ad, 0177)) < 0) ||
            (*cptr != 0)) {
            if ((*cptr != 0) &&
                ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL))
                return SCPE_ARG;
            val[0] |= (00400 | (xr << 3) | ((ad >> 12) & 07));
            val[++nwd] = ad & 07777;
            }
        else val[0] |= (00200 | broff);
        break;

    case F_V_LEMU:
        if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL)
            return SCPE_ARG;
        if ((*cptr != 0) &&
            ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL))
            return SCPE_ARG;
        val[0] |= ((xr << 3) | ((ad >> 12) & 07));
        val[++nwd] = ad & 07777;
        break;

    case F_V_MR1D:
    case F_V_MR2D:
        return SCPE_IERR;          
        }                                               /* end case */

if (*cptr != 0) return SCPE_ARG;                        /* junk at end? */
return -nwd;
}

/* Parse field */

CONST char *parse_field (CONST char *cptr, uint32 max, uint32 *val, uint32 c)
{
char gbuf[CBUFSIZE];
t_stat r;

cptr = get_glyph (cptr, gbuf, c);                       /* get field */
*val = get_uint (gbuf, 8, max, &r);
if (r != SCPE_OK)
    return NULL;
return cptr;
}

/* Parse index register */

CONST char *parse_fpp_xr (CONST char *cptr, uint32 *xr, t_bool inc)
{
char gbuf[CBUFSIZE];
uint32 len;
t_stat r;

cptr = get_glyph (cptr, gbuf, 0);                      /* get field */
len = strlen (gbuf);
if (gbuf[len - 1] == '+') {
    if (!inc)
        return NULL;
    gbuf[len - 1] = 0;
    *xr = 010;
    }
else *xr = 0;
*xr += get_uint (gbuf, 8, 7, &r);
if (r != SCPE_OK)
    return NULL;
return cptr;
}

/* Test address in range of base register */

int32 test_fpp_addr (uint32 ad, uint32 max)
{
uint32 off;
extern uint32 fpp_bra;

off = ad - fpp_bra;
if (((off % 3) != 0) ||
    (off > (max * 3)))
    return -1;
return ((int32) off / 3);
}
Added src/PDP8/pdp8_td.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_td.c: PDP-8 simple DECtape controller (TD8E) simulator

   Copyright (c) 1993-2013, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   This module was inspired by Gerold Pauler's TD8E simulator for Doug Jones'
   PDP8 simulator but tracks the hardware implementation more closely.

   td           TD8E/TU56 DECtape

   17-Sep-13    RMS     Changed to use central set_bootpc routine
   23-Mar-11    RMS     Fixed SDLC to clear AC (from Dave Gesswein)
   23-Jun-06    RMS     Fixed switch conflict in ATTACH
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   09-Jan-04    RMS     Changed sim_fsize calling sequence, added STOP_OFFR

   PDP-8 DECtapes are represented in memory by fixed length buffer of 12b words.
   Three file formats are supported:

        18b/36b                 256 words per block [256 x 18b]
        16b                     256 words per block [256 x 16b]
        12b                     129 words per block [129 x 12b]

   When a 16b or 18/36b DECtape file is read in, it is converted to 12b format.

   DECtape motion is measured in 3b lines.  Time between lines is 33.33us.
   Tape density is nominally 300 lines per inch.  The format of a DECtape (as
   taken from the TD8E formatter) is:

        reverse end zone        8192 reverse end zone codes ~ 10 feet
        reverse buffer          200 interblock codes
        block 0
         :
        block n
        forward buffer          200 interblock codes
        forward end zone        8192 forward end zone codes ~ 10 feet

   A block consists of five 18b header words, a tape-specific number of data
   words, and five 18b trailer words.  All systems except the PDP-8 use a
   standard block length of 256 words; the PDP-8 uses a standard block length
   of 86 words (x 18b = 129 words x 12b).

   Because a DECtape file only contains data, the simulator cannot support
   write timing and mark track and can only do a limited implementation
   of non-data words.  Read assumes that the tape has been conventionally
   written forward:

        header word 0           0
        header word 1           block number (for forward reads)
        header words 2,3        0
        header word 4           checksum (for reverse reads)
        :
        trailer word 4          checksum (for forward reads)
        trailer words 3,2       0
        trailer word 1          block number (for reverse reads)
        trailer word 0          0

   Write modifies only the data words and dumps the non-data words in the
   bit bucket.
*/

#include "pdp8_defs.h"

#define DT_NUMDR        2                               /* #drives */
#define UNIT_V_WLK      (UNIT_V_UF + 0)                 /* write locked */
#define UNIT_V_8FMT     (UNIT_V_UF + 1)                 /* 12b format */
#define UNIT_V_11FMT    (UNIT_V_UF + 2)                 /* 16b format */
#define UNIT_WLK        (1 << UNIT_V_WLK)
#define UNIT_8FMT       (1 << UNIT_V_8FMT)
#define UNIT_11FMT      (1 << UNIT_V_11FMT)
#define STATE           u3                              /* unit state */
#define LASTT           u4                              /* last time update */
#define WRITTEN         u5                              /* device buffer is dirty and needs flushing */
#define UNIT_WPRT       (UNIT_WLK | UNIT_RO)            /* write protect */

/* System independent DECtape constants */

#define DT_LPERMC       6                               /* lines per mark track */
#define DT_EZLIN        (8192 * DT_LPERMC)              /* end zone length */
#define DT_BFLIN        (200 * DT_LPERMC)               /* end zone buffer */
#define DT_HTLIN        (5 * DT_LPERMC)                 /* lines per hdr/trlr */

/* 16b, 18b, 36b DECtape constants */

#define D18_WSIZE       6                               /* word size in lines */
#define D18_BSIZE       384                             /* block size in 12b */
#define D18_TSIZE       578                             /* tape size */
#define D18_LPERB       (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)
#define D18_FWDEZ       (DT_EZLIN + (D18_LPERB * D18_TSIZE))
#define D18_CAPAC       (D18_TSIZE * D18_BSIZE)         /* tape capacity */

#define D18_NBSIZE      ((D18_BSIZE * D8_WSIZE) / D18_WSIZE)
#define D18_FILSIZ      (D18_NBSIZE * D18_TSIZE * sizeof (int32))
#define D11_FILSIZ      (D18_NBSIZE * D18_TSIZE * sizeof (int16))

/* 12b DECtape constants */

#define D8_WSIZE        4                               /* word size in lines */
#define D8_BSIZE        129                             /* block size in 12b */
#define D8_TSIZE        1474                            /* tape size */
#define D8_LPERB        (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)
#define D8_FWDEZ        (DT_EZLIN + (D8_LPERB * D8_TSIZE))
#define D8_CAPAC        (D8_TSIZE * D8_BSIZE)           /* tape capacity */
#define D8_FILSIZ       (D8_CAPAC * sizeof (int16))

/* This controller */

#define DT_CAPAC        D8_CAPAC                        /* default */
#define DT_WSIZE        D8_WSIZE

/* Calculated constants, per unit */

#define DTU_BSIZE(u)    (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)
#define DTU_TSIZE(u)    (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)
#define DTU_LPERB(u)    (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)
#define DTU_FWDEZ(u)    (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)
#define DTU_CAPAC(u)    (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)

#define DT_LIN2BL(p,u)  (((p) - DT_EZLIN) / DTU_LPERB (u))
#define DT_LIN2OF(p,u)  (((p) - DT_EZLIN) % DTU_LPERB (u))

/* Command register */

#define TDC_UNIT        04000                           /* unit select */
#define TDC_FWDRV       02000                           /* fwd/rev */
#define TDC_STPGO       01000                           /* stop/go */
#define TDC_RW          00400                           /* read/write */
#define TDC_MASK        07400                           /* implemented */
#define TDC_GETUNIT(x)  (((x) & TDC_UNIT)? 1: 0)

/* Status register */

#define TDS_WLO         00200                           /* write lock */
#define TDS_TME         00100                           /* timing/sel err */

/* Mark track register and codes */

#define MTK_MASK        077
#define MTK_REV_END     055                             /* rev end zone */
#define MTK_INTER       025                             /* interblock */
#define MTK_FWD_BLK     026                             /* fwd block */
#define MTK_REV_GRD     032                             /* reverse guard */
#define MTK_FWD_PRE     010                             /* lock, etc */
#define MTK_DATA        070                             /* data */
#define MTK_REV_PRE     073                             /* lock, etc */
#define MTK_FWD_GRD     051                             /* fwd guard */
#define MTK_REV_BLK     045                             /* rev block */
#define MTK_FWD_END     022                             /* fwd end zone */

/* DECtape state */

#define STA_STOP        0                               /* stopped */
#define STA_DEC         2                               /* decelerating */
#define STA_ACC         4                               /* accelerating */
#define STA_UTS         6                               /* up to speed */
#define STA_DIR         1                               /* fwd/rev */

#define ABS(x)          (((x) < 0)? (-(x)): (x))
#define MTK_BIT(c,p)    (((c) >> (DT_LPERMC - 1 - ((p) % DT_LPERMC))) & 1)

/* State and declarations */

int32 td_cmd = 0;                                       /* command */
int32 td_dat = 0;                                       /* data */
int32 td_mtk = 0;                                       /* mark track */
int32 td_slf = 0;                                       /* single line flag */
int32 td_qlf = 0;                                       /* quad line flag */
int32 td_tme = 0;                                       /* timing error flag */
int32 td_csum = 0;                                      /* save check sum */
int32 td_qlctr = 0;                                     /* quad line ctr */
int32 td_ltime = 20;                                    /* interline time */
int32 td_dctime = 40000;                                /* decel time */
int32 td_stopoffr = 0;
static uint8 tdb_mtk[DT_NUMDR][D18_LPERB];              /* mark track bits */

int32 td77 (int32 IR, int32 AC);
t_stat td_svc (UNIT *uptr);
t_stat td_reset (DEVICE *dptr);
t_stat td_attach (UNIT *uptr, CONST char *cptr);
void td_flush (UNIT *uptr);
t_stat td_detach (UNIT *uptr);
t_stat td_boot (int32 unitno, DEVICE *dptr);
t_bool td_newsa (int32 newf);
t_bool td_setpos (UNIT *uptr);
int32 td_header (UNIT *uptr, int32 blk, int32 line);
int32 td_trailer (UNIT *uptr, int32 blk, int32 line);
int32 td_read (UNIT *uptr, int32 blk, int32 line);
void td_write (UNIT *uptr, int32 blk, int32 line, int32 datb);
int32 td_set_mtk (int32 code, int32 u, int32 k);
t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, CONST void *desc);

extern uint16 M[];

/* TD data structures

   td_dev       DT device descriptor
   td_unit      DT unit list
   td_reg       DT register list
   td_mod       DT modifier list
*/

DIB td_dib = { DEV_TD8E, 1, { &td77 } };

UNIT td_unit[] = {
    { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
    { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
             UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }
    };

REG td_reg[] = {
    { GRDATAD (TDCMD, td_cmd, 8, 4, 8, "command register") },
    { ORDATAD (TDDAT, td_dat, 12, "data register") },
    { ORDATAD (TDMTK, td_mtk, 6, "mark track register") },
    { FLDATAD (TDSLF, td_slf, 0, "single line flag") },
    { FLDATAD (TDQLF, td_qlf, 0, "quad line flag") },
    { FLDATAD (TDTME, td_tme, 0, "timing error flag") },
    { ORDATAD (TDQL, td_qlctr, 2, "quad line counter") },
    { ORDATA (TDCSUM, td_csum, 6), REG_RO },
    { DRDATAD (LTIME, td_ltime, 31, "time between lines"), REG_NZ | PV_LEFT },
    { DRDATAD (DCTIME, td_dctime, 31, "time to decelerate to a full stop"), REG_NZ | PV_LEFT },
    { URDATAD (POS, td_unit[0].pos, 10, T_ADDR_W, 0,
              DT_NUMDR, PV_LEFT | REG_RO, "positions, in lines, units 0 and 1") },
    { URDATAD (STATT, td_unit[0].STATE, 8, 18, 0,
              DT_NUMDR, REG_RO, "unit state, units 0 and 1") },
    { URDATA (LASTT, td_unit[0].LASTT, 10, 32, 0,
              DT_NUMDR, REG_HRO) },
    { FLDATAD (STOP_OFFR, td_stopoffr, 0, "stop on off-reel error") },
    { ORDATA (DEVNUM, td_dib.dev, 6), REG_HRO },
    { NULL }
    };

MTAB td_mod[] = {
    { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
    { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, 
    { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL },
    { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL },
    { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
      &set_dev, &show_dev, NULL },
    { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "POSITION", NULL, NULL, &td_show_pos },
    { 0 }
    };

DEVICE td_dev = {
    "TD", td_unit, td_reg, td_mod,
    DT_NUMDR, 8, 24, 1, 8, 12,
    NULL, NULL, &td_reset,
    &td_boot, &td_attach, &td_detach,
    &td_dib, DEV_DISABLE | DEV_DIS
    };

/* IOT routines */

int32 td77 (int32 IR, int32 AC)
{
int32 pulse = IR & 07;
int32 u = TDC_GETUNIT (td_cmd);                         /* get unit */
int32 diff, t;

switch (pulse) {

    case 01:                                            /* SDSS */
        if (td_slf)
            return AC | IOT_SKP;
        break;

    case 02:                                            /* SDST */
        if (td_tme)
            return AC | IOT_SKP;
        break;

    case 03:                                            /* SDSQ */
        if (td_qlf)
            return AC | IOT_SKP;
        break;

    case 04:                                            /* SDLC */
        td_tme = 0;                                     /* clear tim err */
        diff = (td_cmd ^ AC) & TDC_MASK;                /* cmd changes */
        td_cmd = AC & TDC_MASK;                         /* update cmd */
        if ((diff != 0) && (diff != TDC_RW)) {          /* signif change? */
            if (td_newsa (td_cmd))                      /* new command */
                return AC | (IORETURN (td_stopoffr, STOP_DTOFF) << IOT_V_REASON);
            }
        AC = 0;
        break;

    case 05:                                            /* SDLD */
        td_slf = 0;                                     /* clear flags */
        td_qlf = 0;
        td_qlctr = 0;
        td_dat = AC;                                    /* load data reg */
        break;

    case 06:                                            /* SDRC */
        td_slf = 0;                                     /* clear flags */
        td_qlf = 0;
        td_qlctr = 0;
        t = td_cmd | td_mtk;                            /* form status */
        if (td_tme || !(td_unit[u].flags & UNIT_ATT))   /* tim/sel err? */
            t = t | TDS_TME;
        if (td_unit[u].flags & UNIT_WPRT)               /* write locked? */
            t = t | TDS_WLO;
        return t;                                       /* return status */

    case 07:                                            /* SDRD */
        td_slf = 0;                                     /* clear flags */
        td_qlf = 0;
        td_qlctr = 0;
        return td_dat;                                  /* return data */
        }

return AC;
}

/* Command register change (start/stop, forward/reverse, new unit)

   1. If change in motion, stop to start
        - schedule up to speed
        - set function as next state
   2. If change in motion, start to stop, or change in direction
        - schedule stop
*/

t_bool td_newsa (int32 newf)
{
int32 prev_mving, new_mving, prev_dir, new_dir;
UNIT *uptr;

uptr = td_dev.units + TDC_GETUNIT (newf);               /* new unit */
if ((uptr->flags & UNIT_ATT) == 0)                      /* new unit attached? */
    return FALSE;

new_mving = ((newf & TDC_STPGO) != 0);                  /* new moving? */
prev_mving = (uptr->STATE != STA_STOP);                 /* previous moving? */
new_dir = ((newf & TDC_FWDRV) != 0);                    /* new dir? */
prev_dir = ((uptr->STATE & STA_DIR) != 0);              /* previous dir? */

td_mtk = 0;                                             /* mark trk reg cleared */

if (!prev_mving && !new_mving)                          /* stop from stop? */
    return FALSE;

if (new_mving && !prev_mving) {                         /* start from stop? */
    if (td_setpos (uptr))                               /* update pos */
        return TRUE;
    sim_cancel (uptr);                                  /* stop current */
    sim_activate (uptr, td_dctime - (td_dctime >> 2));  /* sched accel */
    uptr->STATE = STA_ACC | new_dir;                    /* set status */
    td_slf = td_qlf = td_qlctr = 0;                     /* clear state */
    return FALSE;
    }

if ((prev_mving && !new_mving) ||                       /* stop from moving? */
    (prev_dir != new_dir)) {                            /* dir chg while moving? */
    if (uptr->STATE >= STA_ACC) {                       /* not stopping? */
        if (td_setpos (uptr))                           /* update pos */
            return TRUE;
        sim_cancel (uptr);                              /* stop current */
        sim_activate (uptr, td_dctime);                 /* schedule decel */
        uptr->STATE = STA_DEC | prev_dir;               /* set status */
        td_slf = td_qlf = td_qlctr = 0;                 /* clear state */
        }
    return FALSE;
    }

return FALSE;   
}

/* Update DECtape position

   DECtape motion is modeled as a constant velocity, with linear
   acceleration and deceleration.  The motion equations are as follows:

        t       =       time since operation started
        tmax    =       time for operation (accel, decel only)
        v       =       at speed velocity in lines (= 1/td_ltime)

   Then:
        at speed dist = t * v
        accel dist = (t^2 * v) / (2 * tmax)
        decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)

   This routine uses the relative (integer) time, rather than the absolute
   (floating point) time, to allow save and restore of the start times.
*/

t_bool td_setpos (UNIT *uptr)
{
uint32 new_time, ut, ulin, udelt;
int32 delta;

new_time = sim_grtime ();                               /* current time */
ut = new_time - uptr->LASTT;                            /* elapsed time */
if (ut == 0)                                            /* no time gone? exit */
    return FALSE;
uptr->LASTT = new_time;                                 /* update last time */
switch (uptr->STATE & ~STA_DIR) {                       /* case on motion */

    case STA_STOP:                                      /* stop */
        delta = 0;
        break;

    case STA_DEC:                                       /* slowing */
        ulin = ut / (uint32) td_ltime;
        udelt = td_dctime / td_ltime;
        delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);
        break;

    case STA_ACC:                                       /* accelerating */
        ulin = ut / (uint32) td_ltime;
        udelt = (td_dctime - (td_dctime >> 2)) / td_ltime;
        delta = (ulin * ulin) / (2 * udelt);
        break;

    case STA_UTS:                                       /* at speed */
        delta = ut / (uint32) td_ltime;
        break;
        }

if (uptr->STATE & STA_DIR)                              /* update pos */
    uptr->pos = uptr->pos - delta;
else uptr->pos = uptr->pos + delta;
if (((int32) uptr->pos < 0) ||
    ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) {
    detach_unit (uptr);                                 /* off reel */
    sim_cancel (uptr);                                  /* no timing pulses */
    return TRUE;
    }
return FALSE;
}

/* Unit service - unit is either changing speed, or it is up to speed */

t_stat td_svc (UNIT *uptr)
{
int32 mot = uptr->STATE & ~STA_DIR;
int32 dir = uptr->STATE & STA_DIR;
int32 unum = uptr - td_dev.units;
int32 su = TDC_GETUNIT (td_cmd);
int32 mtkb, datb;

/* Motion cases

   Decelerating - if go, next state must be accel as specified by td_cmd
   Accelerating - next state must be up to speed, fall through
   Up to speed - process line */

if (mot == STA_STOP)                                    /* stopped? done */
    return SCPE_OK;
if ((uptr->flags & UNIT_ATT) == 0) {                    /* not attached? */
    uptr->STATE = uptr->pos = 0;                        /* also done */
    return SCPE_UNATT;
    }

switch (mot) {                                          /* case on motion */

    case STA_DEC:                                       /* deceleration */
        if (td_setpos (uptr))                           /* upd pos; off reel? */
            return IORETURN (td_stopoffr, STOP_DTOFF);
        if ((unum != su) || !(td_cmd & TDC_STPGO))      /* not sel or stop? */
            uptr->STATE = 0;                            /* stop */
        else {                                          /* selected and go */
            uptr->STATE = STA_ACC |                     /* accelerating */
                ((td_cmd & TDC_FWDRV)? STA_DIR: 0);     /* in new dir */
            sim_activate (uptr, td_dctime - (td_dctime >> 2));
            }
        return SCPE_OK;

    case STA_ACC:                                       /* accelerating */
        if (td_setpos (uptr))                           /* upd pos; off reel? */
            return IORETURN (td_stopoffr, STOP_DTOFF);
        uptr->STATE = STA_UTS | dir;                    /* set up to speed */
        break;

    case STA_UTS:                                       /* up to speed */
        if (dir)                                        /* adjust position */
            uptr->pos = uptr->pos - 1;
        else uptr->pos = uptr->pos + 1;
        uptr->LASTT = sim_grtime ();                    /* save time */
        if (((int32) uptr->pos < 0) ||                  /* off reel? */
           (uptr->pos >= (((uint32) DTU_FWDEZ (uptr)) + DT_EZLIN))) {
            detach_unit (uptr);
            return IORETURN (td_stopoffr, STOP_DTOFF);
            }
        break;                                          /* check function */
        }

/* At speed - process the current line

   Once the TD8E is running at speed, it operates line by line.  If reading,
   the current mark track bit is shifted into the mark track register, and
   the current data nibble (3b) is shifted into the data register.  If
   writing, the current mark track bit is shifted into the mark track
   register, the top nibble from the data register is written to tape, and
   the data register is shifted up.  The complexity here comes from 
   synthesizing the mark track, based on tape position, and the header data. */

sim_activate (uptr, td_ltime);                          /* sched next line */
if (unum != su)                                         /* not sel? done */
    return SCPE_OK;
td_slf = 1;                                             /* set single */
td_qlctr = (td_qlctr + 1) % DT_WSIZE;                   /* count words */
if (td_qlctr == 0) {                                    /* lines mod 4? */
    if (td_qlf) {                                       /* quad line set? */
        td_tme = 1;                                     /* timing error */
        td_cmd = td_cmd & ~TDC_RW;                      /* clear write */
        }
    else td_qlf = 1;                                    /* no, set quad */
    }

datb = 0;                                               /* assume no data */
if (uptr->pos < (DT_EZLIN - DT_BFLIN))                  /* rev end zone? */
    mtkb = MTK_BIT (MTK_REV_END, uptr->pos);
else if (uptr->pos < DT_EZLIN)                          /* rev buffer? */
    mtkb = MTK_BIT (MTK_INTER, uptr->pos);
else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) {     /* data zone? */
    int32 blkno = DT_LIN2BL (uptr->pos, uptr);          /* block # */
    int32 lineno = DT_LIN2OF (uptr->pos, uptr);         /* line # within block */
    if (lineno < DT_HTLIN) {                            /* header? */
        if ((td_cmd & TDC_RW) == 0)                     /* read? */                     
            datb = td_header (uptr, blkno, lineno);     /* get nibble */
        }
    else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) {  /* data? */
        if (td_cmd & TDC_RW)                            /* write? */
            td_write (uptr, blkno,                      /* write data nibble */
                      lineno - DT_HTLIN,                /* data rel line num */
                      (td_dat >> 9) & 07);
        else datb = td_read (uptr, blkno,               /* no, read */
                             lineno - DT_HTLIN);
        }
    else if ((td_cmd & TDC_RW) == 0)                    /* trailer; read? */
        datb = td_trailer (uptr, blkno, lineno -        /* get trlr nibble */
                           (DTU_LPERB (uptr) - DT_HTLIN));
    mtkb = tdb_mtk[unum][lineno];
    }
else if (uptr->pos < (((uint32) DTU_FWDEZ (uptr)) + DT_BFLIN))
    mtkb = MTK_BIT (MTK_INTER, uptr->pos);              /* fwd buffer? */
else mtkb = MTK_BIT (MTK_FWD_END, uptr->pos);           /* fwd end zone */

if (dir) {                                              /* reverse? */
    mtkb = mtkb ^ 01;                                   /* complement mark bit, */
    datb = datb ^ 07;                                   /* data bits */
    }
td_mtk = ((td_mtk << 1) | mtkb) & MTK_MASK;             /* shift mark reg */
td_dat = ((td_dat << 3) | datb) & 07777;                /* shift data reg */
return SCPE_OK;
}

/* Header read - reads out 18b words in 3b increments

        word    lines           contents
        0       0-5             0
        1       6-11            block number
        2       12-17           0
        3       18-23           0
        4       24-29           reverse checksum (0777777)
*/

int32 td_header (UNIT *uptr, int32 blk, int32 line)
{
int32 nibp;

switch (line) {

    case 8: case 9: case 10: case 11:                   /* block num */
        nibp = 3 * (DT_LPERMC - 1 - (line % DT_LPERMC));
        return (blk >> nibp) & 07;

    case 24: case 25: case 26: case 27: case 28: case 29: /* rev csum */
        return 07;                                      /* 777777 */

    default:
        return 0;
        }
}

/* Trailer read - reads out 18b words in 3b increments
   Checksum is stored to avoid double calculation

   word         lines           contents
   0            0-5             forward checksum (lines 0-1, rest 0)
   1            6-11            0
   2            12-17           0
   3            18-23           reverse block mark
   4            24-29           0

   Note that the reverse block mark (when read forward) appears
   as the complement obverse (3b nibbles swapped end for end and
   complemented).
*/

int32 td_trailer (UNIT *uptr, int32 blk, int32 line)
{
int32 nibp, i, ba;
int16 *fbuf= (int16 *) uptr->filebuf;

switch (line) {

    case 0:
        td_csum = 07777;                                /* init csum */
        ba = blk * DTU_BSIZE (uptr);
        for (i = 0; i < DTU_BSIZE (uptr); i++)          /* loop thru buf */
            td_csum = (td_csum ^ ~fbuf[ba + i]) & 07777;
        td_csum = ((td_csum >> 6) ^ td_csum) & 077;
        return (td_csum >> 3) & 07;

    case 1:
        return (td_csum & 07);

    case 18: case 19: case 20: case 21:
        nibp = 3 * (line % DT_LPERMC);
        return ((blk >> nibp) & 07) ^ 07;

    default:
        return 0;
        }
}

/* Data read - convert block number/data line # to offset in data array */

int32 td_read (UNIT *uptr, int32 blk, int32 line)
{
int16 *fbuf = (int16 *) uptr->filebuf;                  /* buffer */
uint32 ba = blk * DTU_BSIZE (uptr);                     /* block base */
int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE));    /* nibble pos */

ba = ba + (line / DT_WSIZE);                            /* block addr */
return (fbuf[ba] >> nibp) & 07;                         /* get data nibble */
}

/* Data write - convert block number/data line # to offset in data array */

void td_write (UNIT *uptr, int32 blk, int32 line, int32 dat)
{
int16 *fbuf = (int16 *) uptr->filebuf;                  /* buffer */
uint32 ba = blk * DTU_BSIZE (uptr);                     /* block base */
int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE));    /* nibble pos */

ba = ba + (line / DT_WSIZE);                            /* block addr */
fbuf[ba] = (fbuf[ba] & ~(07 << nibp)) | (dat << nibp);  /* upd data nibble */
uptr->WRITTEN = TRUE;
if (ba >= uptr->hwmark)                                 /* upd length */
    uptr->hwmark = ba + 1;
return;
}

/* Reset routine */

t_stat td_reset (DEVICE *dptr)
{
int32 i;
UNIT *uptr;

for (i = 0; i < DT_NUMDR; i++) {                        /* stop all activity */
    uptr = td_dev.units + i;
    if (sim_is_running) {                               /* CAF? */
        if (uptr->STATE >= STA_ACC) {                   /* accel or uts? */
            if (td_setpos (uptr))                       /* update pos */
                continue;
            sim_cancel (uptr);
            sim_activate (uptr, td_dctime);             /* sched decel */
            uptr->STATE = STA_DEC | (uptr->STATE & STA_DIR);
            }
         }
    else {
        sim_cancel (uptr);                              /* sim reset */
        uptr->STATE = 0;  
        uptr->LASTT = sim_grtime ();
        }
    }
td_slf = td_qlf = td_qlctr = 0;                         /* clear state */
td_cmd = td_dat = td_mtk = 0;
td_csum = 0;
return SCPE_OK;
}

/* Bootstrap routine - OS/8 only 

   1) Read reverse until reverse end zone (mark track is complement obverse)
   2) Read forward until mark track code 031.  This is a composite code from
      the last 4b of the forward block number and the first two bits of the
      reverse guard (01 -0110 01- 1010).  There are 16 lines before the first
      data word.
   3) Store data words from 7354 to end of page.  This includes header and
      trailer words.
   4) Continue at location 7400.
*/

#define BOOT_START      07300
#define BOOT_LEN        (sizeof (boot_rom) / sizeof (int16))

static const uint16 boot_rom[] = {
    01312,                      /* ST,  TAD L4MT        ;=2000, reverse */
    04312,                      /*      JMS L4MT        ; rev lk for 022 */
    04312,                      /*      JMS L4MT        ; fwd lk for 031 */
    06773,                      /* DAT, SDSQ            ; wait for 12b */
    05303,                      /*      JMP .-1 */
    06777,                      /*      SDRD            ; read word */
    03726,                      /*      DCA I BUF       ; store */
    02326,                      /*      ISZ BUF         ; incr ptr */
    05303,                      /*      JMP DAT         ; if not 0, cont */
    05732,                      /*      JMP I SCB       ; jump to boot */
    02000,                      /* L4MT,2000            ; overwritten */
    01300,                      /*      TAD ST          ; =1312, go */
    06774,                      /*      SDLC            ; new command */
    06771,                      /* MTK, SDSS            ; wait for mark */
    05315,                      /*      JMP .-1 */
    06776,                      /*      SDRC            ; get mark code */
    00331,                      /*      AND K77         ; mask to 6b */
    01327,                      /* CMP, TAD MCD         ; got target code? */
    07640,                      /*      SZA CLA         ; skip if yes */
    05315,                      /*      JMP MTK         ; wait for mark */
    02321,                      /*      ISZ CMP         ; next target */
    05712,                      /*      JMP I L4MT      ; exit */
    07354,                      /* BUF, 7354            ; loading point */
    07756,                      /* MCD, -22             ; target 1 */
    07747,                      /*      -31             ; target 2 */
    00077,                      /*      77              ; mask */
    07400                       /* SCB, 7400            ; secondary boot */
    };

t_stat td_boot (int32 unitno, DEVICE *dptr)
{
size_t i;

if (unitno)
    return SCPE_ARG;                                    /* only unit 0 */
if (td_dib.dev != DEV_TD8E)
    return STOP_NOTSTD;                                 /* only std devno */
td_unit[unitno].pos = DT_EZLIN;
for (i = 0; i < BOOT_LEN; i++)
    M[BOOT_START + i] = boot_rom[i];
cpu_set_bootpc (BOOT_START);
return SCPE_OK;
}

/* Attach routine

   Determine 12b, 16b, or 18b/36b format
   Allocate buffer
   If 16b or 18b, read 16b or 18b format and convert to 12b in buffer
   If 12b, read data into buffer
   Set up mark track bit array
*/

t_stat td_attach (UNIT *uptr, CONST char *cptr)
{
uint32 pdp18b[D18_NBSIZE];
uint16 pdp11b[D18_NBSIZE], *fbuf;
int32 i, k, mtkpb;
int32 u = uptr - td_dev.units;
t_stat r;
uint32 ba, sz;

r = attach_unit (uptr, cptr);                           /* attach */
if (r != SCPE_OK)                                       /* fail? */
    return r;
if ((sim_switches & SIM_SW_REST) == 0) {                /* not from rest? */
    uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;
    if (sim_switches & SWMASK ('F'))                    /* att 18b? */
        uptr->flags = uptr->flags & ~UNIT_8FMT;
    else if (sim_switches & SWMASK ('S'))               /* att 16b? */
        uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
    else if (!(sim_switches & SWMASK ('A')) &&          /* autosize? */
        (sz = sim_fsize (uptr->fileref))) {
        if (sz == D11_FILSIZ)
            uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
        else if (sz > D8_FILSIZ)
            uptr->flags = uptr->flags & ~UNIT_8FMT;
        }
    }
uptr->capac = DTU_CAPAC (uptr);                         /* set capacity */
uptr->filebuf = calloc (uptr->capac, sizeof (int16));
if (uptr->filebuf == NULL) {                            /* can't alloc? */
    detach_unit (uptr);
    return SCPE_MEM;
    }
fbuf = (uint16 *) uptr->filebuf;                        /* file buffer */
sim_printf ("%s%d: ", sim_dname (&td_dev), u);
if (uptr->flags & UNIT_8FMT)
    sim_printf ("12b format");
else if (uptr->flags & UNIT_11FMT)
    sim_printf ("16b format");
else sim_printf ("18b/36b format");
sim_printf (", buffering file in memory\n");
uptr->io_flush = td_flush;
if (uptr->flags & UNIT_8FMT)                            /* 12b? */
    uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16),
            uptr->capac, uptr->fileref);
else {                                                  /* 16b/18b */
    for (ba = 0; ba < uptr->capac; ) {                  /* loop thru file */
        if (uptr->flags & UNIT_11FMT) {
            k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref);
            for (i = 0; i < k; i++)
                pdp18b[i] = pdp11b[i];
            }
        else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref);
        if (k == 0)
            break;
        for ( ; k < D18_NBSIZE; k++)
            pdp18b[k] = 0;
        for (k = 0; k < D18_NBSIZE; k = k + 2) {        /* loop thru blk */
            fbuf[ba] = (pdp18b[k] >> 6) & 07777;
            fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) |
                ((pdp18b[k + 1] >> 12) & 077);
            fbuf[ba + 2] = pdp18b[k + 1] & 07777;
            ba = ba + 3;
            }                                           /* end blk loop */
        }                                               /* end file loop */
    uptr->hwmark = ba;
    }                                                   /* end else */
uptr->flags = uptr->flags | UNIT_BUF;                   /* set buf flag */
uptr->pos = DT_EZLIN;                                   /* beyond leader */
uptr->LASTT = sim_grtime ();                            /* last pos update */
uptr->STATE = STA_STOP;                                 /* stopped */

mtkpb = (DTU_BSIZE (uptr) * DT_WSIZE) / DT_LPERMC;      /* mtk codes per blk */
k = td_set_mtk (MTK_INTER, u, 0);                       /* fill mark track */
k = td_set_mtk (MTK_FWD_BLK, u, k);                     /* bit array */
k = td_set_mtk (MTK_REV_GRD, u, k);
for (i = 0; i < 4; i++)
    k = td_set_mtk (MTK_FWD_PRE, u, k);
for (i = 0; i < (mtkpb - 4); i++)
    k = td_set_mtk (MTK_DATA, u, k);
for (i = 0; i < 4; i++)
    k = td_set_mtk (MTK_REV_PRE, u, k);
k = td_set_mtk (MTK_FWD_GRD, u, k);
k = td_set_mtk (MTK_REV_BLK, u, k);
k = td_set_mtk (MTK_INTER, u, k);
return SCPE_OK;
}

/* Detach routine

   If 12b, write buffer to file
   If 16b or 18b, convert 12b buffer to 16b or 18b and write to file
   Deallocate buffer
*/

void td_flush (UNIT* uptr)
{
uint32 pdp18b[D18_NBSIZE];
uint16 pdp11b[D18_NBSIZE], *fbuf;
int32 i, k;
uint32 ba;

if (uptr->WRITTEN && uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) {    /* any data? */
    rewind (uptr->fileref);                             /* start of file */
    fbuf = (uint16 *) uptr->filebuf;                    /* file buffer */
    if (uptr->flags & UNIT_8FMT)                        /* PDP8? */
        fxwrite (uptr->filebuf, sizeof (uint16),        /* write file */
            uptr->hwmark, uptr->fileref);
    else {                                              /* 16b/18b */
        for (ba = 0; ba < uptr->hwmark; ) {             /* loop thru buf */
            for (k = 0; k < D18_NBSIZE; k = k + 2) {
                pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) |
                    ((uint32) (fbuf[ba + 1] >> 6) & 077);
                pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) |
                    ((uint32) (fbuf[ba + 2] & 07777));
                ba = ba + 3;
                }                                       /* end loop blk */
            if (uptr->flags & UNIT_11FMT) {             /* 16b? */
                for (i = 0; i < D18_NBSIZE; i++)
                    pdp11b[i] = pdp18b[i];
                fxwrite (pdp11b, sizeof (uint16),
                    D18_NBSIZE, uptr->fileref);
                }
            else fxwrite (pdp18b, sizeof (uint32),
                D18_NBSIZE, uptr->fileref);
            }                                           /* end loop buf */
        }                                               /* end else */
    if (ferror (uptr->fileref))
        sim_perror ("I/O error");
    }
uptr->WRITTEN = FALSE;                                  /* no longer dirty */
}

t_stat td_detach (UNIT* uptr)
{
int u = (int)(uptr - td_dev.units);

if (!(uptr->flags & UNIT_ATT))
    return SCPE_OK;
if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) {    /* any data? */
    sim_printf ("%s%d: writing buffer to file\n", sim_dname (&td_dev), u);
    td_flush (uptr);
    }                                                   /* end if hwmark */
free (uptr->filebuf);                                   /* release buf */
uptr->flags = uptr->flags & ~UNIT_BUF;                  /* clear buf flag */
uptr->filebuf = NULL;                                   /* clear buf ptr */
uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;  /* default fmt */
uptr->capac = DT_CAPAC;                                 /* default size */
uptr->pos = uptr->STATE = 0;
sim_cancel (uptr);                                      /* no more pulses */
return detach_unit (uptr);
}

/* Set mark track code into bit array */

int32 td_set_mtk (int32 code, int32 u, int32 k)
{
int32 i;

for (i = 5; i >= 0; i--)
    tdb_mtk[u][k++] = (code >> i) & 1;
return k;
}

/* Show position */

t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;
if (uptr->pos < DT_EZLIN)                               /* rev end zone? */
    fprintf (st, "Reverse end zone\n");
else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) {     /* data zone? */
    int32 blkno = DT_LIN2BL (uptr->pos, uptr);          /* block # */
    int32 lineno = DT_LIN2OF (uptr->pos, uptr);         /* line # within block */
    fprintf (st, "Block %d, line %d, ", blkno, lineno);
    if (lineno < DT_HTLIN)                              /* header? */
        fprintf (st, "header cell %d, nibble %d\n",
            lineno / DT_LPERMC, lineno % DT_LPERMC);
    else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN))    /* data? */
        fprintf (st, "data word %d, nibble %d\n",
            (lineno - DT_HTLIN) / DT_WSIZE, (lineno - DT_HTLIN) % DT_WSIZE);
    else fprintf (st, "trailer cell %d, nibble %d\n",
        (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) / DT_LPERMC,
        (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) % DT_LPERMC);
    }
else fprintf (st, "Forward end zone\n");                /* fwd end zone */
return SCPE_OK;
}

Added src/PDP8/pdp8_tsc.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_tsc.c: PDP-8 ETOS timesharing option board (TSC8-75)

   Copyright (c) 2003-2011, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   This module is based on Bernhard Baehr's PDP-8/E simulator

        PDP-8/E Simulator Source Code

        Copyright ) 2001-2003 Bernhard Baehr

        TSC8iots.c - IOTs for the TSC8-75 Board plugin

        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 2 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, write to the Free Software
        Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

   tsc          TSC8-75 option board
*/

#include "pdp8_defs.h"

extern int32 int_req;
extern int32 SF;
extern int32 tsc_ir;                                    /* "ERIOT" */
extern int32 tsc_pc;                                    /* "ERTB" */
extern int32 tsc_cdf;                                   /* "ECDF" */
extern int32 tsc_enb;                                   /* enable */

#define UNIT_V_SN699    (UNIT_V_UF + 0)                 /* SN 699 or above */
#define UNIT_SN699      (1 << UNIT_V_SN699)

int32 tsc (int32 IR, int32 AC);
t_stat tsc_reset (DEVICE *dptr);

/* TSC data structures

   tsc_dev      TSC device descriptor
   tsc_unit     TSC unit descriptor
   tsc_reg      TSC register list
*/

DIB tsc_dib = { DEV_TSC, 1, { &tsc } };

UNIT tsc_unit = { UDATA (NULL, UNIT_SN699, 0) };

REG tsc_reg[] = {
    { ORDATAD (IR, tsc_ir, 12, "most recently trapped instruction") },
    { ORDATAD (PC, tsc_pc, 12, "PC of most recently trapped instruction") },
    { FLDATAD (CDF, tsc_cdf, 0, "1 if trapped instruction is CDF, 0 otherwise") },
    { FLDATAD (ENB, tsc_enb, 0, "interrupt enable flag") },
    { FLDATAD (INT, int_req, INT_V_TSC, "interrupt pending flag") },
    { NULL }
    };

MTAB tsc_mod[] = {
    { UNIT_SN699, UNIT_SN699, "ESME", "ESME", NULL },
    { UNIT_SN699, 0, "no ESME", "NOESME", NULL },
    { 0 }
    };

DEVICE tsc_dev = {
    "TSC", &tsc_unit, tsc_reg, tsc_mod,
    1, 10, 31, 1, 8, 8,
    NULL, NULL, &tsc_reset,
    NULL, NULL, NULL,
    &tsc_dib, DEV_DISABLE | DEV_DIS
    };

/* IOT routine */

int32 tsc (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 0:                                             /* ETDS */
        tsc_enb = 0;                                    /* disable int req */
        int_req = int_req & ~INT_TSC;                   /* clear flag */
        break;

    case 1:                                             /* ESKP */
        return (int_req & INT_TSC)? IOT_SKP + AC: AC;   /* skip on int req */

    case 2:                                             /* ECTF */
        int_req = int_req & ~INT_TSC;                   /* clear int req */
        break;

    case 3:                                             /* ECDF */
        AC = AC | ((tsc_ir >> 3) & 07);                 /* read "ERIOT"<6:8> */
        if (tsc_cdf)                                    /* if cdf, skip */
            AC = AC | IOT_SKP;
        tsc_cdf = 0;
        break;

    case 4:                                             /* ERTB */
        return tsc_pc;

    case 5:                                             /* ESME */
        if (tsc_unit.flags & UNIT_SN699) {              /* enabled? */
            if (tsc_cdf && ((tsc_ir & 070) >> 3) == (SF & 07)) {
                AC = AC | IOT_SKP;
                tsc_cdf = 0;
                }
            }
        break;

    case 6:                                             /* ERIOT */
        return tsc_ir;

    case 7:                                             /* ETEN */
        tsc_enb = 1;
        break;
        }                                               /* end switch */

return AC;
}

/* Reset routine */

t_stat tsc_reset (DEVICE *dptr)
{
tsc_ir = 0;
tsc_pc = 0;
tsc_cdf = 0;
tsc_enb = 0;
int_req = int_req & ~INT_TSC;
return SCPE_OK;
}
Added src/PDP8/pdp8_tt.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_tt.c: PDP-8 console terminal simulator

   Copyright (c) 1993-2016, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   tti,tto      KL8E terminal input/output

   18-Apr-12    RMS     Revised to use clock coscheduling
   18-Jun-07    RMS     Added UNIT_IDLE flag to console input
   18-Oct-06    RMS     Synced keyboard to clock
   30-Sep-06    RMS     Fixed handling of non-printable characters in KSR mode
   22-Nov-05    RMS     Revised for new terminal processing routines
   28-May-04    RMS     Removed SET TTI CTRL-C
   29-Dec-03    RMS     Added console output backpressure support
   25-Apr-03    RMS     Revised for extended file support
   02-Mar-02    RMS     Added SET TTI CTRL-C
   22-Dec-02    RMS     Added break support
   01-Nov-02    RMS     Added 7B/8B support
   04-Oct-02    RMS     Added DIBs, device number support
   30-May-02    RMS     Widened POS to 32b
   07-Sep-01    RMS     Moved function prototypes
*/

#include "pdp8_defs.h"
#include "sim_tmxr.h"
#include <ctype.h>

extern int32 int_req, int_enable, dev_done, stop_inst;
extern int32 tmxr_poll;

int32 tti (int32 IR, int32 AC);
int32 tto (int32 IR, int32 AC);
t_stat tti_svc (UNIT *uptr);
t_stat tto_svc (UNIT *uptr);
t_stat tti_reset (DEVICE *dptr);
t_stat tto_reset (DEVICE *dptr);
t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc);

/* TTI data structures

   tti_dev      TTI device descriptor
   tti_unit     TTI unit descriptor
   tti_reg      TTI register list
   tti_mod      TTI modifiers list
*/

DIB tti_dib = { DEV_TTI, 1, { &tti } };

UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE|TT_MODE_KSR, 0), SERIAL_IN_WAIT };

REG tti_reg[] = {
    { ORDATAD (BUF, tti_unit.buf, 8, "last data item processed") },
    { FLDATAD (DONE, dev_done, INT_V_TTI, "device done flag") },
    { FLDATAD (ENABLE, int_enable, INT_V_TTI, "interrupt enable flag") },
    { FLDATAD (INT, int_req, INT_V_TTI, "interrupt pending flag") },
    { DRDATAD (POS, tti_unit.pos, T_ADDR_W, "number of characters input"), PV_LEFT },
    { DRDATAD (TIME, tti_unit.wait, 24, "input polling interval (if 0, the keyboard is polled synchronously with the clock)"), PV_LEFT+REG_NZ },
    { NULL }
    };

MTAB tti_mod[] = {
    { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode },
    { TT_MODE, TT_MODE_7B,  "7b",  "7B",  &tty_set_mode },
    { TT_MODE, TT_MODE_8B,  "8b",  "8B",  &tty_set_mode },
    { TT_MODE, TT_MODE_7P,  "7b",  NULL,  NULL },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev, NULL },
    { 0 }
    };

DEVICE tti_dev = {
    "TTI", &tti_unit, tti_reg, tti_mod,
    1, 10, 31, 1, 8, 8,
    NULL, NULL, &tti_reset,
    NULL, NULL, NULL,
    &tti_dib, 0
    };

uint32 tti_buftime;                                     /* time input character arrived */

/* TTO data structures

   tto_dev      TTO device descriptor
   tto_unit     TTO unit descriptor
   tto_reg      TTO register list
*/

DIB tto_dib = { DEV_TTO, 1, { &tto } };

UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT };

REG tto_reg[] = {
    { ORDATAD (BUF, tto_unit.buf, 8, "last date item processed") },
    { FLDATAD (DONE, dev_done, INT_V_TTO, "device done flag") },
    { FLDATAD (ENABLE, int_enable, INT_V_TTO, "interrupt enable flag") },
    { FLDATAD (INT, int_req, INT_V_TTO, "interrupt pending flag") },
    { DRDATAD (POS, tto_unit.pos, T_ADDR_W, "number of characters output"), PV_LEFT },
    { DRDATAD (TIME, tto_unit.wait, 24, "time form I/O initiation to interrupt"), PV_LEFT },
    { NULL }
    };

MTAB tto_mod[] = {
    { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode },
    { TT_MODE, TT_MODE_7B,  "7b",  "7B",  &tty_set_mode },
    { TT_MODE, TT_MODE_8B,  "8b",  "8B",  &tty_set_mode },
    { TT_MODE, TT_MODE_7P,  "7p",  "7P",  &tty_set_mode },
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev },
    { 0 }
    };

DEVICE tto_dev = {
    "TTO", &tto_unit, tto_reg, tto_mod,
    1, 10, 31, 1, 8, 8,
    NULL, NULL, &tto_reset, 
    NULL, NULL, NULL,
    &tto_dib, 0
    };

/* Terminal input: IOT routine */

int32 tti (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */
    case 0:                                             /* KCF */
        dev_done = dev_done & ~INT_TTI;                 /* clear flag */
        int_req = int_req & ~INT_TTI;
        return AC;

    case 1:                                             /* KSF */
        return (dev_done & INT_TTI)? IOT_SKP + AC: AC;

    case 2:                                             /* KCC */
        dev_done = dev_done & ~INT_TTI;                 /* clear flag */
        int_req = int_req & ~INT_TTI;
        return 0;                                       /* clear AC */

    case 4:                                             /* KRS */
        return (AC | tti_unit.buf);                     /* return buffer */

    case 5:                                             /* KIE */
        if (AC & 1)
            int_enable = int_enable | (INT_TTI+INT_TTO);
        else int_enable = int_enable & ~(INT_TTI+INT_TTO);
        int_req = INT_UPDATE;                           /* update interrupts */
        return AC;

    case 6:                                             /* KRB */
        dev_done = dev_done & ~INT_TTI;                 /* clear flag */
        int_req = int_req & ~INT_TTI;
        sim_activate_abs (&tti_unit, tti_unit.wait);    /* check soon for more input */
        return (tti_unit.buf);                          /* return buffer */

    default:
        return (stop_inst << IOT_V_REASON) + AC;
        }                                               /* end switch */
}

/* Unit service */

t_stat tti_svc (UNIT *uptr)
{
int32 c;

sim_clock_coschedule (uptr, tmxr_poll);                 /* continue poll */
if ((dev_done & INT_TTI) &&                             /* prior character still pending and < 500ms? */
    ((sim_os_msec () - tti_buftime) < 500))
    return SCPE_OK;
if ((c = sim_poll_kbd ()) < SCPE_KFLAG)                 /* no char or error? */
    return c;
if (c & SCPE_BREAK)                                     /* break? */
    uptr->buf = 0;
else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR);
tti_buftime = sim_os_msec ();
uptr->pos = uptr->pos + 1;
dev_done = dev_done | INT_TTI;                          /* set done */
int_req = INT_UPDATE;                                   /* update interrupts */
return SCPE_OK;
}

/* Reset routine */

t_stat tti_reset (DEVICE *dptr)
{
tmxr_set_console_units (&tti_unit, &tto_unit);
tti_unit.buf = 0;
dev_done = dev_done & ~INT_TTI;                         /* clear done, int */
int_req = int_req & ~INT_TTI;
int_enable = int_enable | INT_TTI;                      /* set enable */
if (!sim_is_running)                                    /* RESET (not CAF)? */
    sim_activate (&tti_unit, KBD_WAIT (tti_unit.wait, tmxr_poll));
return SCPE_OK;
}

/* Terminal output: IOT routine */

int32 tto (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 0:                                             /* TLF */
        dev_done = dev_done | INT_TTO;                  /* set flag */
        int_req = INT_UPDATE;                           /* update interrupts */
        return AC;

    case 1:                                             /* TSF */
        return (dev_done & INT_TTO)? IOT_SKP + AC: AC;

    case 2:                                             /* TCF */
        dev_done = dev_done & ~INT_TTO;                 /* clear flag */
        int_req = int_req & ~INT_TTO;                   /* clear int req */
        return AC;

    case 5:                                             /* SPI */
        return (int_req & (INT_TTI+INT_TTO))? IOT_SKP + AC: AC;

    case 6:                                             /* TLS */
        dev_done = dev_done & ~INT_TTO;                 /* clear flag */
        int_req = int_req & ~INT_TTO;                   /* clear int req */
    case 4:                                             /* TPC */
        sim_activate (&tto_unit, tto_unit.wait);        /* activate unit */
        tto_unit.buf = AC;                              /* load buffer */
        return AC;

    default:
        return (stop_inst << IOT_V_REASON) + AC;
        }                                               /* end switch */
}

/* Unit service */

t_stat tto_svc (UNIT *uptr)
{
int32 c;
t_stat r;

c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR);
if (c >= 0) {
    if ((r = sim_putchar_s (c)) != SCPE_OK) {           /* output char; error? */
        sim_activate (uptr, uptr->wait);                /* try again */
        return ((r == SCPE_STALL)? SCPE_OK: r);         /* if !stall, report */
        }
    }
dev_done = dev_done | INT_TTO;                          /* set done */
int_req = INT_UPDATE;                                   /* update interrupts */
uptr->pos = uptr->pos + 1;
return SCPE_OK;
}

/* Reset routine */

t_stat tto_reset (DEVICE *dptr)
{
tto_unit.buf = 0;
dev_done = dev_done & ~INT_TTO;                         /* clear done, int */
int_req = int_req & ~INT_TTO;
int_enable = int_enable | INT_TTO;                      /* set enable */
sim_cancel (&tto_unit);                                 /* deactivate unit */
return SCPE_OK;
}

t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val;
tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val;
return SCPE_OK;
}
Added src/PDP8/pdp8_ttx.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pdp8_ttx.c: PDP-8 additional terminals simulator

   Copyright (c) 1993-2016, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   ttix,ttox    PT08/KL8JA terminal input/output

   18-Sep-16    RMS     Expanded support to 16 terminals
   11-Oct-13    RMS     Poll TTIX immediately to pick up initial connect (Mark Pizzolato)
   18-Apr-12    RMS     Revised to use clock coscheduling
   19-Nov-08    RMS     Revised for common TMXR show routines
   07-Jun-06    RMS     Added UNIT_IDLE flag
   06-Jul-06    RMS     Fixed bug in DETACH routine
   22-Nov-05    RMS     Revised for new terminal processing routines
   29-Jun-05    RMS     Added SET TTOXn DISCONNECT
                        Fixed bug in SET LOG/NOLOG
   21-Jun-05    RMS     Fixed bug in SHOW CONN/STATS
   05-Jan-04    RMS     Revised for tmxr library changes
   09-May-03    RMS     Added network device flag
   25-Apr-03    RMS     Revised for extended file support
   22-Dec-02    RMS     Added break support
   02-Nov-02    RMS     Added 7B/8B support
   04-Oct-02    RMS     Added DIB, device number support
   22-Aug-02    RMS     Updated for changes to sim_tmxr.c
   06-Jan-02    RMS     Added device enable/disable support
   30-Dec-01    RMS     Complete rebuild
   30-Nov-01    RMS     Added extended SET/SHOW support

   This module implements 1-16 individual serial interfaces similar in function
   to the console.  These interfaces are mapped to Telnet based connections as
   though they were the four lines of a terminal multiplexor.  The connection
   polling mechanism is superimposed onto the keyboard of the first interface.

   The done and enable flags are maintained locally, and only a master interrupt
   request is maintained in global register dev_done. Because this is actually
   an interrupt request flag, the corresponding bit in int_enable must always
   be set to 1.
*/

#include "pdp8_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.h"
#include <ctype.h>

#define TTX_MAXL        16
#define TTX_INIL        4

#define TTX_GETLN(x)    (((x) >> 4) & TTX_MASK)

extern int32 int_req, int_enable, dev_done, stop_inst;
extern int32 tmxr_poll;

uint32 ttix_done = 0;                                   /* input ready flags */
uint32 ttox_done = 0;                                   /* output ready flags */
uint32 ttx_enbl = 0;                                    /* intr enable flags */
uint8 ttix_buf[TTX_MAXL] = { 0 };                       /* input buffers */
uint8 ttox_buf[TTX_MAXL] = { 0 };                       /* output buffers */
TMLN ttx_ldsc[TTX_MAXL] = { {0} };                      /* line descriptors */
TMXR ttx_desc = { TTX_INIL, 0, 0, ttx_ldsc };           /* mux descriptor */
#define ttx_lines	ttx_desc.lines

int32 ttix (int32 IR, int32 AC);
int32 ttox (int32 IR, int32 AC);
t_stat ttix_svc (UNIT *uptr);
t_stat ttox_svc (UNIT *uptr);
int32 ttx_getln (int32 inst);
void ttx_new_flags (uint32 newi, uint32 newo, uint32 newe);
t_stat ttx_reset (DEVICE *dptr);
t_stat ttx_attach (UNIT *uptr, CONST char *cptr);
t_stat ttx_detach (UNIT *uptr);
void ttx_reset_ln (int32 i);
t_stat ttx_vlines (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat ttx_show_devno (FILE *st, UNIT *uptr, int32 val, CONST void *desc);

#define TTIX_SET_DONE(ln)       ttx_new_flags (ttix_done | (1u << (ln)), ttox_done, ttx_enbl)
#define TTIX_CLR_DONE(ln)       ttx_new_flags (ttix_done & ~(1u << (ln)), ttox_done, ttx_enbl)
#define TTIX_TST_DONE(ln)       ((ttix_done & (1u << (ln))) != 0)
#define TTOX_SET_DONE(ln)       ttx_new_flags (ttix_done, ttox_done | (1u << (ln)), ttx_enbl)
#define TTOX_CLR_DONE(ln)       ttx_new_flags (ttix_done, ttox_done & ~(1u << (ln)), ttx_enbl)
#define TTOX_TST_DONE(ln)       ((ttox_done & (1u << (ln))) != 0)
#define TTX_SET_ENBL(ln)        ttx_new_flags (ttix_done, ttox_done, ttx_enbl | (1u << (ln)))
#define TTX_CLR_ENBL(ln)        ttx_new_flags (ttix_done, ttox_done, ttx_enbl & ~(1u << (ln)))
#define TTX_TST_ENBL(ln)        ((ttx_enbl & (1u << (ln))) != 0)

/* TTIx data structures

   ttix_dev     TTIx device descriptor
   ttix_unit    TTIx unit descriptor
   ttix_reg     TTIx register list
   ttix_mod     TTIx modifiers list
*/

DIB_DSP ttx_dsp[TTX_MAXL * 2] = {
    { DEV_TTI1,  &ttix }, { DEV_TTO1,  &ttox },
    { DEV_TTI2,  &ttix }, { DEV_TTO2,  &ttox },
    { DEV_TTI3,  &ttix }, { DEV_TTO3,  &ttox },
    { DEV_TTI4,  &ttix }, { DEV_TTO4,  &ttox },
    { DEV_TTI5,  &ttix }, { DEV_TTO5,  &ttox },
    { DEV_TTI6,  &ttix }, { DEV_TTO6,  &ttox },
    { DEV_TTI7,  &ttix }, { DEV_TTO7,  &ttox },
    { DEV_TTI8,  &ttix }, { DEV_TTO8,  &ttox },
    { DEV_TTI9,  &ttix }, { DEV_TTO9,  &ttox },
    { DEV_TTI10, &ttix }, { DEV_TTO10, &ttox },
    { DEV_TTI11, &ttix }, { DEV_TTO11, &ttox },
    { DEV_TTI12, &ttix }, { DEV_TTO12, &ttox },
    { DEV_TTI13, &ttix }, { DEV_TTO13, &ttox },
    { DEV_TTI14, &ttix }, { DEV_TTO14, &ttox },
    { DEV_TTI15, &ttix }, { DEV_TTO15, &ttox },
    { DEV_TTI16, &ttix }, { DEV_TTO16, &ttox }
    };

DIB ttx_dib = { DEV_TTI1, TTX_INIL * 2, { &ttix, &ttox }, ttx_dsp };

UNIT ttix_unit = { UDATA (&ttix_svc, UNIT_IDLE|UNIT_ATTABLE, 0), SERIAL_IN_WAIT };

REG ttix_reg[] = {
    { BRDATAD (BUF, ttix_buf, 8, 8, TTX_MAXL, "input buffer, lines 0 to 15") },
    { ORDATAD (DONE, ttix_done, TTX_MAXL, "device done flag (line 0 rightmost)") },
    { ORDATAD (ENABLE, ttx_enbl, TTX_MAXL, "interrupt enable flag") },
    { FLDATA  (SUMDONE, dev_done, INT_V_TTI1), REG_HRO },
    { FLDATA  (SUMENABLE, int_enable, INT_V_TTI1), REG_HRO },
    { DRDATAD (TIME, ttix_unit.wait, 24, "initial polling interval"), REG_NZ + PV_LEFT },
    { DRDATA  (LINES, ttx_desc.lines, 6), REG_HRO },
    { NULL }
    };

MTAB ttix_mod[] = {
    { MTAB_VDV,            0,       "LINES",      "LINES", &ttx_vlines,  &tmxr_show_lines, (void *) &ttx_desc },
    { MTAB_VDV,            0,      "DEVNO",          NULL, NULL,         &ttx_show_devno, (void *) &ttx_desc },
    { UNIT_ATT,     UNIT_ATT,     "SUMMARY",         NULL, NULL,         &tmxr_show_summ,  (void *) &ttx_desc },
    { MTAB_VDV,            1,          NULL, "DISCONNECT", &tmxr_dscln,  NULL,             (void *) &ttx_desc },
    { MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS",         NULL, NULL,         &tmxr_show_cstat, (void *) &ttx_desc },
    { MTAB_VDV | MTAB_NMO, 0, "STATISTICS",          NULL, NULL,         &tmxr_show_cstat, (void *) &ttx_desc },
    { 0 }
    };

/* debugging bitmaps */
#define DBG_XMT  TMXR_DBG_XMT                           /* display Transmitted Data */
#define DBG_RCV  TMXR_DBG_RCV                           /* display Received Data */
#define DBG_RET  TMXR_DBG_RET                           /* display Returned Received Data */
#define DBG_CON  TMXR_DBG_CON                           /* display connection activities */
#define DBG_TRC  TMXR_DBG_TRC                           /* display trace routine calls */

DEBTAB ttx_debug[] = {
  {"XMT",    DBG_XMT, "Transmitted Data"},
  {"RCV",    DBG_RCV, "Received Data"},
  {"RET",    DBG_RET, "Returned Received Data"},
  {"CON",    DBG_CON, "connection activities"},
  {"TRC",    DBG_TRC, "trace routine calls"},
  {0}
};

DEVICE ttix_dev = {
    "TTIX", &ttix_unit, ttix_reg, ttix_mod,
    1, 10, 31, 1, 8, 8,
    &tmxr_ex, &tmxr_dep, &ttx_reset,
    NULL, &ttx_attach, &ttx_detach,
    &ttx_dib, DEV_MUX | DEV_DISABLE | DEV_DEBUG,
    0, ttx_debug
    };

/* TTOx data structures

   ttox_dev     TTOx device descriptor
   ttox_unit    TTOx unit descriptor
   ttox_reg     TTOx register list
*/

UNIT ttox_unit[] = {
    { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT },
    { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }
    };

REG ttox_reg[] = {
    { BRDATAD (BUF, ttox_buf, 8, 8, TTX_MAXL, "last data item processed, lines 0 to 3") },
    { ORDATAD  (DONE, ttox_done, TTX_MAXL, "device done flag (line 0 rightmost)") },
    { ORDATAD  (ENABLE, ttx_enbl, TTX_MAXL, "interrupt enable flag") },
    { FLDATA  (SUMDONE, dev_done, INT_V_TTO1), REG_HRO },
    { FLDATA  (SUMENABLE, int_enable, INT_V_TTO1), REG_HRO },
    { URDATAD (TIME, ttox_unit[0].wait, 10, 24, 0,
              TTX_MAXL, PV_LEFT, "line from I/O initiation to interrupt, lines 0 to 3") },
    { NULL }
    };

MTAB ttox_mod[] = {
    { TT_MODE, TT_MODE_UC, "UC", "UC", NULL },
    { TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
    { TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
    { TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
    { MTAB_VDV, 0, "DEVNO", NULL, NULL, &ttx_show_devno, &ttx_desc },
    { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT",
      &tmxr_dscln, NULL, &ttx_desc },
    { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG",
      &tmxr_set_log, &tmxr_show_log, &ttx_desc },
    { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG",
      &tmxr_set_nolog, NULL, &ttx_desc },
    { 0 }
    };

DEVICE ttox_dev = {
    "TTOX", ttox_unit, ttox_reg, ttox_mod,
    TTX_MAXL, 10, 31, 1, 8, 8,
    NULL, NULL, &ttx_reset, 
    NULL, NULL, NULL,
    NULL, DEV_DISABLE | DEV_DEBUG,
    0, ttx_debug
    };

/* Terminal input: IOT routine */

int32 ttix (int32 inst, int32 AC)
{
int32 pulse = inst & 07;                                /* IOT pulse */
int32 ln = ttx_getln (inst);                            /* line # */

if (ln < 0)                                             /* bad line #? */
    return (SCPE_IERR << IOT_V_REASON) | AC;

switch (pulse) {                                        /* case IR<9:11> */

    case 0:                                             /* KCF */
        TTIX_CLR_DONE (ln);                             /* clear flag */
        break;

    case 1:                                             /* KSF */
        return (TTIX_TST_DONE (ln))? IOT_SKP | AC: AC;

    case 2:                                             /* KCC */
        TTIX_CLR_DONE (ln);                             /* clear flag */
        sim_activate_abs (&ttix_unit, ttix_unit.wait);  /* check soon for more input */
        return 0;                                       /* clear AC */

    case 4:                                             /* KRS */
        return (AC | ttix_buf[ln]);                     /* return buf */

    case 5:                                             /* KIE */
        if (AC & 1)
            TTX_SET_ENBL (ln);
        else TTX_CLR_ENBL (ln);
        break;

    case 6:                                             /* KRB */
        TTIX_CLR_DONE (ln);                             /* clear flag */
        sim_activate_abs (&ttix_unit, ttix_unit.wait);  /* check soon for more input */
        return ttix_buf[ln];                            /* return buf */

    default:
        return (stop_inst << IOT_V_REASON) | AC;
        }                                               /* end switch */

return AC;
}

/* Unit service */

t_stat ttix_svc (UNIT *uptr)
{
int32 ln, c, temp;

if ((uptr->flags & UNIT_ATT) == 0)                      /* attached? */
    return SCPE_OK;
sim_clock_coschedule (uptr, tmxr_poll);                 /* continue poll */
ln = tmxr_poll_conn (&ttx_desc);                        /* look for connect */
if (ln >= 0)                                            /* got one? */
    ttx_ldsc[ln].rcve = 1;                              /* set rcv enable */
tmxr_poll_rx (&ttx_desc);                               /* poll for input */
for (ln = 0; ln < ttx_lines; ln++) {                    /* loop thru lines */
    if (ttx_ldsc[ln].conn) {                            /* connected? */
        if (TTIX_TST_DONE (ln))                         /* last char still pending? */
            continue;
        if ((temp = tmxr_getc_ln (&ttx_ldsc[ln]))) {    /* get char */
            if (temp & SCPE_BREAK)                      /* break? */
                c = 0;
            else c = sim_tt_inpcvt (temp, TT_GET_MODE (ttox_unit[ln].flags));
            ttix_buf[ln] = c;
            TTIX_SET_DONE (ln);                         /* set flag */
            }
        }
    }
return SCPE_OK;
}

/* Terminal output: IOT routine */

int32 ttox (int32 inst, int32 AC)
{
int32 pulse = inst & 07;                                /* pulse */
int32 ln = ttx_getln (inst);                            /* line # */

if (ln < 0)                                             /* bad line #? */
    return (SCPE_IERR << IOT_V_REASON) | AC;

switch (pulse) {                                        /* case IR<9:11> */

    case 0:                                             /* TLF */
        TTOX_SET_DONE (ln);                             /* set flag */
        break;

    case 1:                                             /* TSF */
        return (TTOX_TST_DONE (ln))? IOT_SKP | AC: AC;

    case 2:                                             /* TCF */
        TTOX_CLR_DONE (ln);                             /* clear flag */
        break;

    case 5:                                             /* SPI */
        if ((TTIX_TST_DONE (ln) || TTOX_TST_DONE (ln))  /* either done set */
            && TTX_TST_ENBL (ln))                       /* and enabled? */
            return IOT_SKP | AC;
        return AC;

    case 6:                                             /* TLS */
        TTOX_CLR_DONE (ln);                             /* clear flag */
    case 4:                                             /* TPC */
        sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); /* activate */
        ttox_buf[ln] = AC & 0377;                       /* load buffer */
        break;

   default:
        return (stop_inst << IOT_V_REASON) | AC;
        }                                               /* end switch */

return AC;
}

/* Unit service */

t_stat ttox_svc (UNIT *uptr)
{
int32 c, ln = uptr - ttox_unit;                         /* line # */

if (ttx_ldsc[ln].conn) {                                /* connected? */
    if (ttx_ldsc[ln].xmte) {                            /* tx enabled? */
        TMLN *lp = &ttx_ldsc[ln];                       /* get line */
        c = sim_tt_outcvt (ttox_buf[ln], TT_GET_MODE (ttox_unit[ln].flags));
        if (c >= 0)                                     /* output char */
            tmxr_putc_ln (lp, c);
        tmxr_poll_tx (&ttx_desc);                       /* poll xmt */
        }
    else {
        tmxr_poll_tx (&ttx_desc);                       /* poll xmt */
        sim_activate (uptr, ttox_unit[ln].wait);        /* wait */
        return SCPE_OK;
        }
    }
TTOX_SET_DONE (ln);                                     /* set done */
return SCPE_OK;
}

/* Flag routine

   Global dev_done is used as a master interrupt; therefore, global
   int_enable must always be set
*/

void ttx_new_flags (uint32 newidone, uint32 newodone, uint32 newenbl)
{
ttix_done = newidone;
ttox_done = newodone;
ttx_enbl = newenbl;
if ((ttix_done & ttx_enbl) != 0)
    dev_done |= INT_TTI1;
else dev_done &= ~INT_TTI1;
if ((ttox_done & ttx_enbl) != 0)
    dev_done |= INT_TTO1;
else dev_done &= ~INT_TTO1;
int_enable |= (INT_TTI1 | INT_TTO1);
int_req = INT_UPDATE;
return;
}

/* Compute relative line number, based on table of device numbers */

int32 ttx_getln (int32 inst)
{
int32 i;
int32 device = (inst >> 3) & 077;                       /* device = IR<3:8> */

for (i = 0; i < (ttx_lines * 2); i++) {                 /* loop thru disp tbl */
    if (device == ttx_dsp[i].dev)                       /* dev # match? */
        return (i >> 1);                                /* return line # */
    }
return -1;
}

/* Reset routine */

t_stat ttx_reset (DEVICE *dptr)
{
int32 ln;

if (dptr->flags & DEV_DIS) {                            /* sync enables */
    ttix_dev.flags |= DEV_DIS;
    ttox_dev.flags |= DEV_DIS;
    }
else {
    ttix_dev.flags &= ~DEV_DIS;
    ttox_dev.flags &= ~DEV_DIS;
    }
if (ttix_unit.flags & UNIT_ATT)                         /* if attached, */
    sim_activate (&ttix_unit, tmxr_poll);               /* activate */
else sim_cancel (&ttix_unit);                           /* else stop */
for (ln = 0; ln < TTX_MAXL; ln++)                       /* for all lines */
    ttx_reset_ln (ln);                                  /* reset line */
int_enable |= (INT_TTI1 | INT_TTO1);                    /* set master enable */
return SCPE_OK;
}

/* Reset line n */

void ttx_reset_ln (int32 ln)
{
uint32 mask = (1u << ln);

ttix_buf[ln] = 0;                                       /* clr buf */
ttox_buf[ln] = 0;                                       /* clr done, set enbl */
ttx_new_flags (ttix_done & ~mask, ttox_done & ~mask, ttx_enbl | mask);
sim_cancel (&ttox_unit[ln]);                            /* stop output */
return;
}

/* Attach master unit */

t_stat ttx_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;

r = tmxr_attach (&ttx_desc, uptr, cptr);                /* attach */
if (r != SCPE_OK)                                       /* error */
    return r;
sim_activate (uptr, 0);                                 /* start poll at once */
return SCPE_OK;
}

/* Detach master unit */

t_stat ttx_detach (UNIT *uptr)
{
int32 i;
t_stat r;

r = tmxr_detach (&ttx_desc, uptr);                      /* detach */
for (i = 0; i < TTX_MAXL; i++)                         /* all lines, */
    ttx_ldsc[i].rcve = 0;                               /* disable rcv */
sim_cancel (uptr);                                      /* stop poll */
return r;
}

/* Change number of lines */

t_stat ttx_vlines (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 newln, i, t;
t_stat r;

if (cptr == NULL)
    return SCPE_ARG;
newln = get_uint (cptr, 10, TTX_MAXL, &r);
if ((r != SCPE_OK) || (newln == ttx_lines))
    return r;
if (newln == 0)
    return SCPE_ARG;
if (newln < ttx_lines) {
    for (i = newln, t = 0; i < ttx_lines; i++)
        t = t | ttx_ldsc[i].conn;
    if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE))
        return SCPE_OK;
    for (i = newln; i < ttx_lines; i++) {
        if (ttx_ldsc[i].conn) {
            tmxr_linemsg (&ttx_ldsc[i], "\r\nOperator disconnected line\r\n");
            tmxr_reset_ln (&ttx_ldsc[i]);               /* reset line */
            }
        ttox_unit[i].flags |= UNIT_DIS;
        ttx_reset_ln (i);
        }
    }
else {
    for (i = ttx_lines; i < newln; i++) {
        ttox_unit[i].flags &= ~UNIT_DIS;
        ttx_reset_ln (i);
        }
    }
ttx_lines = newln;
ttx_dib.num = newln * 2;
return SCPE_OK;
}

/* Show device numbers */
t_stat ttx_show_devno (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 i, dev_offset;
DEVICE *dptr;

if (uptr == NULL)
    return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL)
    return SCPE_IERR;
/* Select correct devno entry for Input or Output device */
if (dptr->name[2] == 'O')
    dev_offset = 1;
else
    dev_offset = 0;

fprintf(st, "devno=");
for (i = 0; i < ttx_lines; i++) {
    fprintf(st, "%02o%s", ttx_dsp[i*2+dev_offset].dev, i < ttx_lines-1 ? 
         "," : "");
}
return SCPE_OK;
}

Added src/PDP8/pidp8i.c.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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pidp8i.c: PiDP-8/I additions to the PDP-8 simulator

   Copyright © 2015-2017 by Oscar Vermeulen, Ian Schofield, 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.
*/

#include "pidp8i.h"

#include "../gpio-common.h"

#include <dirent.h> // for USB stick searching


//// EXPORTED GLOBALS //////////////////////////////////////////////////

// truly terrible even for me - break out of sim and start new script in scp.c
int awfulHackFlag = 0;


//// INTERNAL GLOBALS AND CONSTANTS ////////////////////////////////////

// Single-shot flag for the STOP switch.  It's global because several of
// the functions below need to examine or modify it, since more than
// just the STOP switch handler can put us into STOP mode.
static int swStop = 0;


//// 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, pidp8i_led_state_t eTT)
{
    // First time thru, allocate temp working space so we can flash the
	// LED values over instantaneously, avoiding the need for cross-
	// thread locking to get a coherent display update.
	//
	// Allocated dynamically only because C doesn't let us say
	// temp_led_status[nledrows] on the stack, because nledrows
	// isn't const-enough for C.  (It is for C++; grrr.)
    #define LS_BYTES (nledrows * sizeof(uint16))
	static uint16* temp_led_status = 0;
	if (temp_led_status == 0) temp_led_status = malloc(LS_BYTES);

    // Groups 0-4, easy cases: single-register LED strings
    temp_led_status[0] = (uint32) sPC;
    temp_led_status[1] = (uint32) sMA;
    temp_led_status[2] = (uint32) sMB;
    temp_led_status[3] = (uint32) sLAC;
    temp_led_status[4] = (uint32) sMQ;

    // Group 5a: instruction type column, decoded from current instruction
    // in the IR register
    uint32 tempLeds = 0;
    switch ((sIR & 0xE00) >> 9) {
        case 0: tempLeds |= (1 << 11); break;       // 000 AND
        case 1: tempLeds |= (1 << 10); break;       // 001 TAD
        case 2: tempLeds |= (1 <<  9); break;       // 010 DCA
        case 3: tempLeds |= (1 <<  8); break;       // 011 ISZ
        case 4: tempLeds |= (1 <<  7); break;       // 100 JMS
        case 5: tempLeds |= (1 <<  6); break;       // 101 JMP
        case 6: tempLeds |= (1 <<  5); break;       // 110 IOT
        case 7: tempLeds |= (1 <<  4); break;       // 111-0 and 111-1 OPR group 1 & 2
    }

    // Group 5b: first three LEDs at the top of the next column over
    if ((((sIR & 0xE00) >> 9) <= 5) &&  // is it a memory reference instruction?
            ((sIR & 0x100) != 0)) {     // is it indirect addressing?
        tempLeds += (1 << 1);           // then set Defer LED
    }
    if (eTT == pls_execute) tempLeds |= (1 << 2); // set Execute LED
    if (eTT == pls_fetch)   tempLeds |= (1 << 3); // set Fetch LED

    // Group 5 is done: set LEDs all at once
    temp_led_status[5] = tempLeds;

    // Group 6: remaining LEDs in upper right block plus step count LEDs.
    // Some of these are handled in the main instruction decoding loop.
    tempLeds = 0;
    if (eTT == pls_pause)  tempLeds |= (1 << 8); // set Pause LED
    if (int_req & INT_ION) tempLeds |= (1 << 9); // set ION LED
    if (swStop == 0)       tempLeds |= (1 << 7); // set Run LED
    tempLeds |= ((uint32)(sSC) & 0x1f) << 2;     // set Step Count LEDs
    temp_led_status[6] = tempLeds;

    // Group 7: DF, IF, and Link.
    //
    // DF and IF are shifted up 12 bits for the simulator's convenience so
    // they can simply be OR'd with PC to construct 15-bit addresses, so
    // shift these values down to their positions in the LED string.
    //
    // Then OR in the Link bit, which SIMH keeps in bit 12 (0-based) of
    // the 13-bit LAC register.
    tempLeds  = (uint32) (sDF >> 3) | (sIF >> 6);
    tempLeds |= (uint32) ((sLAC & 010000) >> 7);
    temp_led_status[7] = tempLeds;

    // Set all the LED values instantaneously.  (If the compiler here
    // doesn't implement this atomically, it's near-to; it may actually
    // be a single CPU instruction.)
    memcpy(ledstatus, temp_led_status, LS_BYTES);
}


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

static void mount_usb_stick_file (int devNo, char *devCode)
{
    char    sFoundFile[CBUFSIZE] = { '\0' };
    char    sUSBPath[CBUFSIZE];     // will be "/media/usb0" 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
    fileExtension[2] = '\0';                    // chop off device number

    // Forget the prior file attached to this PDP-8 device.  The only reason
    // 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 (strstr (pDirent->d_name, fileExtension)) {
                    snprintf (sFoundFile, sizeof (sFoundFile), "%s/%s",
                            sUSBPath, pDirent->d_name);
                    for (j = 0; j < 7; ++j) {
                        if (strncmp (mountedFiles[j], sFoundFile, CBUFSIZE) == 0) {
                            break;  // already mounted; skip next
                        }
                    }
                    if (j == 7) {
                        // Media image file is not already mounted, so leave while
                        // loop with path set to mount it
                        break;
                    }
                    else {
                        // It's mounted, so forget its path, else we will stop
                        // searching the other USB mount points
                        sFoundFile[0] = '\0';
                    }
                }
            }

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

    if (sFoundFile[0]) {            // no file found, exit
        if (access (sFoundFile, R_OK) == 0) {
            if (attach_cmd ((int32) 0, sFoundFile) == SCPE_OK) {   // issue ATTACH command
                // add file to mount list
                strncpy (mountedFiles[devNo], sFoundFile, CBUFSIZE);
                printf ("\r\nMounted %s %s\r\n", devCode, mountedFiles[devNo]);
            }
            else {
                printf ("\r\nSIMH error mounting %s\r\n", devCode);
            }
        }
        else {
            printf ("\r\nCannot read medium image %s from USB: %s\r\n",
                    sFoundFile, strerror (errno));
        }
    }
    else {
        printf ("\r\nNo unmounted %s file found\r\n", devCode);
    }
}


//// handle_sing_step //////////////////////////////////////////////////
// Handle SING_STEP combinations as nonstandard functions with respect
// to a real PDP-8, since SIMH doesn't try to emulate the PDP-8's
// single-stepping mode — not to be confused with single-instruction
// mode, which SIMH *does* emulate — so the SING_STEP switch is free
// for our nonstandard uses.
//
// This is separate from handle_flow_control_switches() only because
// there are so many cases here that it would obscure the overall flow
// of our calling function to do all this there.

static pidp8i_flow_t handle_sing_step(int closed)
{
    // If SING_STEP is open, we do nothing here except reset the single-shot
    // flag if it was set.
    static int single_shot = 0;
    if (!closed) {
        single_shot = 0;
        return pft_normal;
    }

    // There are two sets of SING_STEP combos: first up are those where the
    // other switches involved have to be set already, and the function is
    // triggered as soon as SING_STEP closes.  These are functions we don't
    // want re-executing repeatedly while SING_STEP remains closed.
    if (single_shot == 0) {
        // SING_STEP switch was open last we knew, and now it's closed, so
        // set the single-shot flag.
        single_shot = 1;

        // 1. Convert DF switch values to a device number, which
        // we will map to a PDP-8 device type, then attempt to
        // ATTACH some unmounted medium from USB to that device
        //
        // We treat DF == 0 as nothing to mount, since we use
        // SING_STEP for other things, so we need a way to
        // decide which meaning of SING_STEP to take here.
        //
        // The shift by 9 is how many non-DF bits are below
        // DF in switchstatus[1]
        //
        // The bit complement is because closed DF switches show
        // as 0, because they're dragging the pull-up down, but
        // we want those treated as 1s, and vice versa.
        uint16_t css1 = ~switchstatus[1]; 
        int swDevice = (css1 & SS1_DF_ALL) >> 9;
        if (swDevice) {
            char swDevCode[4] = { '\0' };
            switch (swDevice) {
                case 1: strcpy(swDevCode, "ptr"); break; // PTR paper tape reader
                case 2: strcpy(swDevCode, "ptp"); break; // High speed paper tape punch
                case 3: strcpy(swDevCode, "dt0"); break; // TC08 DECtape (#8 is first!)
                case 4: strcpy(swDevCode, "dt1"); break;
                case 5: strcpy(swDevCode, "rx0"); break; // RX8E (8/e peripheral!)
                case 6: strcpy(swDevCode, "rx1"); break;
                case 7: strcpy(swDevCode, "rl0"); break; // RL8A
            }
            if (swDevCode[0]) mount_usb_stick_file(swDevice, swDevCode);
        }

        // 2. Do the same with IF, except that the switch value
        // is used to decide which boot script to restart with via
        // SIMH's DO command.
        //
        // The shift value of 6 is because the IF switches are 3
        // down from the DF switches above.
        int swScript = (css1 & SS1_IF_ALL) >> 6;
        if (swScript) {
            // build filename from IF value
            char sScript[256];
            snprintf(sScript, sizeof(sScript), "@BOOTDIR@/%d.script", swScript);
            printf("\r\n\nRebooting %s\r\n\r\n", sScript);
            awfulHackFlag = swScript;   // this triggers a do command after leaving the simulator run.
            return pft_halt;
        }
    } // end if single-shot flag clear
    else {
        // Now handle the second set of SING_STEP special-function
        // combos, being those where the switches can be pressed in any
        // order, so that we take action when the last one of the set
        // closes, no matter which one that is.  These immediately exit
        // the SIMH instruction interpreter, so they won't re-execute
        // merely because the human isn't fast enough to lift his finger
        // by the time the next iteration of that loop starts.

        // 3. Scan for host poweroff command (Sing_Step + Sing_Inst + Stop)
        if ((switchstatus[2] & (SS2_S_INST | SS2_STOP)) == 0) {
            printf("\r\nShutdown\r\n\r\n");
            awfulHackFlag = 8;  // this triggers an exit command after leaving the simulator run.
            if (spawn_cmd (0, "sudo /bin/systemctl poweroff") != SCPE_OK) {
                printf("\r\n\r\npoweroff failed\r\n\r\n");
            }
            return pft_halt;
        }

        // 4. Scan for host reboot command (Sing_Step + Sing_Inst + Start)
        if ((switchstatus[2] & (SS2_S_INST | SS2_START)) == 0) {
            printf("\r\nReboot\r\n\r\n");
            awfulHackFlag = 8;      // this triggers an exit command after leaving the simulator run.
            if (spawn_cmd (0, "sudo /bin/systemctl reboot") != SCPE_OK) {
                printf("\r\n\r\nreboot failed\r\n\r\n");
            }
            return pft_halt;
        }

        #if 0
        // These combos once meant something, but no longer do.  If you
        // reassign them, think carefully whether they should continue to
        // be handled here and not above in the "if" branch.  If nothing
        // prevents your function from being re-executed while SING_STEP
        // remains closed and re-execution would be bad, move the test
        // under the aegis of the single_shot flag.

        // 5. Sing_Step + Sing_Inst + Load Add
        if ((switchstatus[2] & (SS2_S_INST | SS2_L_ADD)) == 0) { }

        // 6. Sing_Step + Sing_Inst + Deposit
        if ((switchstatus[2] & (SS2_S_INST | SS2_DEP)) == 0) { }
        #endif
    }

    return pft_normal;
}


//// handle_flow_control_switches //////////////////////////////////////
// Process all of the PiDP-8/I front panel switches that can affect the
// flow path of the PDP-8 simulator's instruction interpretation loop,
// returning a code telling the simulator our decision.
//
// The simulator passes in pointers to PDP-8 registers we may modify as
// a side effect of handling these switches.

pidp8i_flow_t handle_flow_control_switches(uint16* pM,
    uint32 *pPC, uint32 *pMA, int32 *pMB, int32 *pLAC, int32 *pIF,
    int32 *pDF, int32* pint_req)
{
    // Exit early if the blink() thread has not attached itself to the GPIO
    // peripheral in the Pi, since that means we cannot safely interpret the
    // data in the switchstatus array.  This is especially important on
    // non-Pi hosts, since switchstatus will remain zeroed, which we would
    // interpret as "all switches are pressed!", causing havoc.
    //
    // It would be cheaper for our caller to check this for us and skip the
    // call, but there's no good reason to expose such implementations
    // details to it.  We're trying to keep the PDP-8 simulator's CPU core
    // as free of PiDP-8/I details as is practical.
    if (!pidp8i_gpio_present) return pft_normal;

    // Handle the nonstandard SING_STEP + X combos, some of which halt
    // the processor.
    if (handle_sing_step ((switchstatus[2] & SS2_S_STEP) == 0) == pft_halt) {
        return pft_halt;
    }

    // Check for SING_INST switch close...
    static int swSingInst = 0;
    if (((switchstatus[2] & SS2_S_INST) == 0) && (swSingInst == 0)) {
        // Put the processor in stop mode until we get a CONT or START
        // switch closure.  Technically this is wrong according to DEC's
        // docs: we're supposed to finish executing the next instruction
        // before we "clear the RUN flip-flop" in DEC terms, whereas
        // we're testing these switches before we fetch the next
        // instruction.  Show me how it matters, and I'll fix it. :)
        swSingInst = 1;
        swStop = 1;
    }

    // ...and SING_INST switch open
    if (swSingInst && (switchstatus[2] & SS2_S_INST)) {
        swSingInst = 0;
    }

    // Check for START switch press...
    static int swStart = 0;
    if (((switchstatus[2] & SS2_START) == 0) && (swStart == 0)) {
        *pint_req = *pint_req & ~INT_ION; // disable ION. says so in handbook, true?
        *pLAC = 0;                        // clear L and AC;
        *pMB = 0;                         // clear MB.
        *pMA = *pPC & 07777;              // transfer PC into MA (FIXME: does IR make this unnecessary?)
        swStop = 0;                       // START cancels STOP mode
        swSingInst = 0;                   // allow SING INST mode re-entry
        swStart = 1;                      // make it single-shot
    }

    // ...and START switch release
    if (swStart && (switchstatus[2] & SS2_START)) {
        swStart = 0;
    }

    // Check for CONT switch press...
    static int swCont = 0;
    if (((switchstatus[2] & SS2_CONT) == 0) && (swCont < 2)) {
        if (swCont == 0) {
            // On the initial CONT press, release stop mode regardless
            // of how it was enabled to execute the next instruction.
            //
            // FIXME: Are we handling MB correctly? [973271ae36]
            swStop = 0;
            swCont = 1;                 // make it single-shot
        }
        else if (swCont == 1) {
            // The second time we come back in here while the CONT
            // switch is still down -- the human is too slow to
            // release it between iterations -- we stop paying attention
            // to it until the switch opens; check below.  Re-enter stop
            // mode if SING_INST is still closed, else leave stop mode
            // because we must have stopped via STOP or HLT.
            swStop = !!swSingInst;
            swCont = 2;
        }
    }

    // ...and CONT switch release
    if (swCont && (switchstatus[2] & SS2_CONT)) {
        swCont = 0;
    }

    // Check for LOAD_ADD switch press.  (No "and release" beacuse it's
    // harmless if this keeps happening until the slow human releases the
    // switch.  This function is idempotent.)
    if ((switchstatus[2] & SS2_L_ADD) == 0) {
        // Copy SR into PC.  We're XORing instead of masking and copying
        // because the switchstatus bits are opposite what we want here: we
        // get a 0 from the GPIO peripheral when the switch is closed, which
        // is the switch's "1" position.
        *pPC = switchstatus[0] ^ 07777;
                               
        // Copy DF switch settings to DF register
        //
        // The shift is because the DF positions inside the switchstatus[1]
        // register happen to be 3 bit positions off of where we want them
        // in DF here: we want to be able to logically OR PC and DF to make
        // 15-bit data access addresses.
        //
        // We complement the bits here for the same reason we XOR'd the PC
        // value above.
        uint16_t css1 = ~switchstatus[1]; 
        *pDF = (css1 & SS1_DF_ALL) << 3;

        // Do the same for IF.  The only difference comes from the fact that
        // IF is the next 3 bits down in switchstatus[1].
        *pIF = (css1 & SS1_IF_ALL) << 6;
    }

    // Check for DEP switch press...
    static int swDep = 0;
    if (((switchstatus[2] & SS2_DEP) == 0) && (swDep == 0)) {
        pM[*pPC] = switchstatus[0] ^ 07777;    // XOR rationale above
        /* ??? in 66 handbook: strictly speaking, SR goes into AC,
           then AC into MB. Does it clear AC afterwards? If not, needs fix */
        *pMB = pM[*pPC];
        *pMA = *pPC & 07777;          // MA trails PC on FP
        *pPC = (*pPC + 1) & 07777;    // increment PC
        swDep = 1;                    // make it single-shot
    }

    // ...and DEP switch release
    if (swDep && (switchstatus[2] & SS2_DEP)) {
        swDep = 0;
    }

    // Check for EXAM switch press...
    static int swExam = 0;
    if (((switchstatus[2] & SS2_EXAM) == 0) && (swExam == 0)) {
        *pMB = pM[*pPC];
        *pMA = *pPC & 07777;          // MA trails PC on FP
        *pPC = (*pPC + 1) & 07777;    // increment PC
        swExam = 1;                   // make it single-shot
    }

    // ...and EXAM switch release
    if (swExam && (switchstatus[2] & SS2_EXAM)) {
        swExam = 0;
    }

    // Check for STOP switch press.  No "and release" because we get out of
    // STOP mode with START or CONT, not by releasing STOP, and while in
    // STOP mode, this switch's function is idempotent.
    if ((switchstatus[2] & SS2_STOP) == 0) {
        swStop = 1;
    }

    // If any of the above put us into STOP mode, go no further.  In
    // particular, fetch no more instructions, and do not touch PC!
    if (swStop == 1) return pft_stop;

    return pft_normal;
}


//// get_switch_register ///////////////////////////////////////////////
// Return the current contents of the switch register

int32 get_switch_register(void)
{
    return switchstatus[0] ^ 07777;
}


//// set_stop_mode /////////////////////////////////////////////////////
// Set the STOP mode flag.  This is a wrapper around a module-global
// variable that the CPU simulator core currently needs to set.

void set_stop_mode(void)
{
    swStop = 1;
}
Added src/PDP8/pidp8i.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* pidp8i.h: Interface between PiDP-8/I additions and the stock SIMH PDP-8
   simulator

   Copyright © 2015-2017 by Oscar Vermeulen 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.
*/

#if !defined(PIDP8I_H)
#define PIDP8I_H

#include "pdp8_defs.h"

typedef enum {
    pft_normal,
    pft_halt,
    pft_stop,
} pidp8i_flow_t;

typedef enum {
	pls_fetch,
	pls_execute,
	pls_pause,
} pidp8i_led_state_t;

extern int awfulHackFlag;

extern int32 get_switch_register(void);

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 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, pidp8i_led_state_t eTT);
extern void set_stop_mode(void);

#endif // !defined(PIDP8I_H)
Added src/gpio-common.c.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * gpio-common.c: functions common to both gpio.c and gpio-nls.c
 *
 * Copyright © 2015-2017 Oscar Vermeulen 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.
 *
 * www.obsolescenceguaranteed.blogspot.com
 * 
 * The only communication with the main program (simh):
 * - external variable ledstatus is read to determine which leds to light.
 * - external variable switchstatus is updated with current switch settings.
*/

#include "gpio-common.h"

#include "config.h"

#include <pthread.h>
#include <sys/file.h>
#include <sys/time.h>

#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_TIME_H
#	include <time.h>
#endif

#define BLOCK_SIZE (4*1024)


// Flag set after we successfully init the GPIO mechanism.  While this
// is false, the rest of the code knows not to expect useful values for
// LED and switch states.  It is also useful as a cross-thread signal,
// since merely starting the blink() thread doesn't tell you whether it
// managed to lock the GPIO device.
uint8_t pidp8i_gpio_present;

struct bcm2835_peripheral gpio;	// needs initialisation


// A constant meaning "indeterminate milliseconds", used for error
// returns from ms_time() and for the case where the switch is in the
// stable state in the switch_state array.
static const ms_time_t na_ms = (ms_time_t)-1;


// Adjust columns to scan based on whether the serial mod was done, as
// that affects the free GPIOs for our use, and how the PCB connects
// them to the LED matrix.
#ifdef PCB_SERIAL_MOD
uint8_t cols[] = {13, 12, 11,    10, 9, 8,    7, 6, 5,    4, 3, 2};
#else
uint8_t cols[] = {13, 12, 11,    10, 9, 8,    7, 6, 5,    4, 15, 14};
#endif

uint8_t ledrows[] = {20, 21, 22, 23, 24, 25, 26, 27};

uint8_t rows[] = {16, 17, 18};

// Array sizes.  Must be declared twice because we need to export them,
// and C doesn't have true const, as C++ does.
#define NCOLS    (sizeof(cols)    / sizeof(cols[0]))
#define NLEDROWS (sizeof(ledrows) / sizeof(ledrows[0]))
#define NROWS    (sizeof(rows)    / sizeof(rows[0]))
const size_t ncols    = NCOLS;
const size_t nledrows = NLEDROWS;
const size_t nrows    = NROWS;

// The public switch and LED API: other threads poke values into
// ledstatus to affect our GPIO LED pin driving loop and read values
// from switchstatus to discover our current published value of the
// switch states.  The latter may differ from the *actual* switch
// states due to the debouncing procedure.
uint16_t switchstatus[NROWS];	// bitfield: sparse nrows x ncols switch matrix
uint16_t ledstatus[NLEDROWS];	// bitfield: sparse nledrows x ncols LED matrix

// Time-delayed reaction to switch changes to debounce the contacts.
// This is especially important with the incandescent lamp simulation
// feature enabled since that speeds up the GPIO scanning loop, making
// it more susceptible to contact bounce.
struct switch_state {
	// switch state currently reported via switchstatus[]
	int stable_state;

	// ms the switch state has been != stable_state, or na_ms
	// if it is currently in that same state
	ms_time_t last_change;		
};
static struct switch_state gss[NROWS][NCOLS];
int gss_initted = 0;
static const ms_time_t debounce_ms = 50;	// time switch state must remain stable

// Name of GPIO memory-mapped device
static const char* gpio_mem_dev = "/dev/gpiomem";


// Exposes the physical address defined in the passed structure
int map_peripheral(struct bcm2835_peripheral *p)
{
	// Open the GPIO device
	if ((p->mem_fd = open(gpio_mem_dev, O_RDWR|O_SYNC) ) < 0) {
#ifdef DEBUG
		printf("Failed to open %s: %s\n", gpio_mem_dev, strerror(errno));
		puts("Disabling PiDP-8/I front panel functionality.");
#endif
		return -1;
	}

	// Attempt to lock it.  If we can't, another program has it locked,
	// so we shouldn't keep running; it'll just end in tears.
	if (flock(p->mem_fd, LOCK_EX | LOCK_NB) < 0) {
		if (errno == EWOULDBLOCK) {
			printf("Failed to lock %s.  Only one PiDP-8/I\n", gpio_mem_dev);
			puts("program can be running at a given time.");
		}
		else {
			printf("Failed to lock %s: %s\n", gpio_mem_dev, strerror(errno));
			puts("Only one PiDP-8/I program can be running at a given time.");
		}
		return -1;
	}

	// Map the GPIO peripheral into our address space
	if ((p->map = mmap(
			NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED,
			p->mem_fd,
			p->addr_p)) == MAP_FAILED) {
		perror("mmap");
		return -1;
	}

	// Success!
	p->addr = (volatile unsigned int *)p->map;
	pidp8i_gpio_present = 1;
	return 0;
}


void unmap_peripheral(struct bcm2835_peripheral *p)
{
	// Unwind the map_peripheral() steps in reverse order
	if (pidp8i_gpio_present) {
		if (p->mem_fd > 0) {
			if (p->map) munmap(p->map, BLOCK_SIZE);
			flock(p->mem_fd, LOCK_UN);
			close(p->mem_fd);
		}
		pidp8i_gpio_present = 0;
	}
}


unsigned bcm_host_get_peripheral_address(void)		// find Pi's GPIO base address
{
	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)
			address = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0;
		fclose(fp);
	}

	return address == ~0 ? 0x20000000 : address;
}


void sleep_ns(ns_time_t ns)
{
	struct timespec ts = { 0, ns };
#if defined(HAVE_CLOCK_NANOSLEEP)
	clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL);
#elif defined(HAVE_NANOSLEEP)
	nanosleep(&ts, NULL);
#elif defined(HAVE_USLEEP)
	usleep(ns / 1000);
#else
#	error Cannot build GPIO controller without high-res "sleep" function!
#endif
}


// Like time(2) except that it returns milliseconds since the Unix epoch
ms_time_t ms_time(ms_time_t* pt)
{
	struct timeval tv;
	if (gettimeofday(&tv, 0) == 0) {
		ms_time_t t = (ms_time_t)(tv.tv_sec / 1000.0 + tv.tv_usec * 1000.0);
		if (pt) *pt = t;
		return t;
	}
	else {
		return na_ms;
	}
}


// Save given switch state ss into the exported switchstatus bitfield
// so the simulator core will see it.  (Constrast the gss matrix,
// which holds our internal view of the unstable truth.)
static void report_ss(int row, int col, int ss,
		struct switch_state* pss)
{
	pss->stable_state = ss;
	pss->last_change = na_ms;

	int mask = 1 << col;
	if (ss) switchstatus[row] |=  mask;
	else    switchstatus[row] &= ~mask;

	#ifdef DEBUG
		printf("%cSS[%d][%02d] = %d  ", gss_initted ? 'N' : 'I', row, col, ss);
	#endif
}


// Given the state of the switch at (row,col), work out if this requires
// a change in our exported switch state.
void debounce_switch(int row, int col, int ss, ms_time_t now_ms)
{
	struct switch_state* pss = &gss[row][col];

	if (!gss_initted) {
		// First time thru, so set this switch's module-global and
		// exported state to its defaults now that we know the switch's
		// initial state.
		report_ss(row, col, ss, pss);
	}
	else if (ss == pss->stable_state) {
		// This switch is still/again in the state we consider "stable",
		// which we are reporting in our switchstatus bitfield.  Reset
		// the debounce timer in case it is returning to its stable
		// state from a brief blip into the other state.
		pss->last_change = na_ms;
	}
	else if (pss->last_change == na_ms) {
		// This switch just left what we consider the "stable" state, so
		// start the debounce timer.
		pss->last_change = now_ms;
	}
	else if ((now_ms - pss->last_change) > debounce_ms) {
		// Switch has been in the new state long enough for the contacts
		// to have stopped bouncing: report its state change to outsiders.
		report_ss(row, col, ss, pss);
	}
	// else, switch was in the new state both this time and the one prior,
	// but it hasn't been there long enough to report it
}


// Write a configuration string tag.
static const char* pi_type(int p)
{
	if (p == 0x20200000) {
		return "pi1+";
	}
	else {
		FILE* fp = fopen("/proc/device-tree/model", "r");
		if (fp) {
			static char ac[60];
			if (fgets(ac, sizeof(ac), fp) && (strlen(ac) > 20)) {
				if (strstr(ac, "Raspberry Pi ") == ac) {
					int series = atoi(ac + 13);
					char model = 'x';
					char* pm = strstr(ac, " Model ");
					if (pm) model = tolower(pm[7]);
					snprintf(ac, sizeof(ac), "pi%d%c", series, model);
					return ac;
				}
			}
			return "pi2+";
		}
	}

	return 0;		// not a Pi
}


// The GPIO thread entry point: initializes GPIO and then calls
// the blink_core() implementation linked to this program.

void *blink(void *terminate)
{
	// Find GPIO address (it varies by Pi model)
	gpio.addr_p = bcm_host_get_peripheral_address() + 0x200000;

	// 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;

	// Map the GPIO peripheral, but hold off exiting if it fails, until
	// we report its absence in the config line.
	int mapped = map_peripheral(&gpio) == 0;

	// Tell the user about our configuration, succinctly
	const char* pt = pi_type(mapped ? gpio.addr_p : 0);
	printf(
		"PiDP-8/I @VERSION@ [%s] [@LED_DRIVER_MODULE@ls] [%spcb]%s"
#ifdef DEBUG
		" [debug]"
#endif
		"%s",
		pt ? pt : "cake",		// pt == 0 == not a Pi
		pt ? 
#ifdef PCB_SERIAL_MOD
			"ser" : 
#else
			"std" : 
#endif
			"no",
		mapped ? " [gpio]" : "",
		rt     ? " [rt]" : ""
	);

	// Hand off control to the blink_core() variant linked to this
	// program: either the new incandescent lamp simulator or the old
	// stock version.
	if (mapped) {
		// initialise GPIO (all pins used as inputs, with pull-ups enabled on cols)
		//  INSERT CODE HERE TO SET GPIO 14 AND 15 TO I/O INSTEAD OF ALT 0.
		//  AT THE MOMENT, USE "sudo ./gpio mode 14 in" and "sudo ./gpio mode 15 in". "sudo ./gpio readall" to verify.
		#define pgpio (&gpio)
		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
			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
		usleep(1);  // must wait 150 cycles
#ifdef PCB_SERIAL_MOD
		GPIO_PULLCLK0 = 0x03ffc; // selects GPIO pins 2..13 (frees up serial port on 14 & 15)
#else
		GPIO_PULLCLK0 = 0x0fff0; // selects GPIO pins 4..15 (assumes we avoid pins 2 and 3!)
#endif
		usleep(1);
		GPIO_PULL = 0; // reset GPPUD register
		usleep(1);
		GPIO_PULLCLK0 = 0; // remove clock
		usleep(1); // probably unnecessary

		// BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs
		GPIO_PULL = 1;  // pull-down to avoid ghosting (dec2015)
		usleep(1);  // must wait 150 cycles
		GPIO_PULLCLK0 = 0x0ff00000; // selects GPIO pins 20..27
		usleep(1);
		GPIO_PULL = 0; // reset GPPUD register
		usleep(1);
		GPIO_PULLCLK0 = 0; // remove clock
		usleep(1); // 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
		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

		extern void blink_core(struct bcm2835_peripheral*, int* terminate);
		blink_core(&gpio, (int*)terminate);
	}

    // blink_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 mapped ? 0 : (void*)-1;
}
Added src/gpio-common.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * gpio-common.h: public interface for the PiDP-8/I's GPIO module
 *
 * Copyright © 2015-2017 Oscar Vermeulen 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.
*/

#if !defined(PIDP8I_GPIO_H)
#define PIDP8I_GPIO_H

#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
 
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>


// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x)
#define INP_GPIO(g)   *(pgpio->addr + ((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g)   *(pgpio->addr + ((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(pgpio->addr + (((g)/10))) |= (((a)<=3?(a) + 4:(a)==4?3:2)<<(((g)%10)*3))

#define GPIO_SET  *(pgpio->addr + 7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR  *(pgpio->addr + 10) // clears bits which are 1 ignores bits which are 0

#define GPIO_READ(g)  *(pgpio->addr + 13) &= (1<<(g))

#define GPIO_PULL *(pgpio->addr + 37) // pull up/pull down
#define GPIO_PULLCLK0 *(pgpio->addr + 38) // pull up/pull down clock


// Switch masks, SSn, used against switchstatus[n]
#define SS0_SR_B11 04000
#define SS0_SR_B10 02000
#define SS0_SR_B09 01000
#define SS0_SR_B08 00400
#define SS0_SR_B07 00200
#define SS0_SR_B06 00100
#define SS0_SR_B05 00040
#define SS0_SR_B04 00020
#define SS0_SR_B03 00010
#define SS0_SR_B02 00004
#define SS0_SR_B01 00002
#define SS0_SR_B00 00001

#define SS1_DF_B2  04000
#define SS1_DF_B1  02000
#define SS1_DF_B0  01000
#define SS1_DF_ALL (SS1_DF_B2 | SS1_DF_B1 | SS1_DF_B0)

#define SS1_IF_B2  00400
#define SS1_IF_B1  00200
#define SS1_IF_B0  00100
#define SS1_IF_ALL (SS1_IF_B2 | SS1_IF_B1 | SS1_IF_B0)

#define SS2_START  04000
#define SS2_L_ADD  02000
#define SS2_DEP    01000
#define SS2_EXAM   00400
#define SS2_CONT   00200
#define SS2_STOP   00100
#define SS2_S_STEP 00040
#define SS2_S_INST 00020
 
// Info for accessing the GPIO peripheral on the SoC
struct bcm2835_peripheral {
    uint32_t addr_p;
    int mem_fd;
    void *map;
    volatile unsigned int *addr;
};

typedef uint64_t   ns_time_t;
typedef useconds_t us_time_t;
typedef uint32_t   ms_time_t;

extern uint16_t switchstatus[];
extern uint16_t ledstatus[];
extern uint8_t cols[];
extern uint8_t ledrows[];
extern uint8_t rows[];
extern const size_t ncols, nledrows, nrows;
extern uint8_t pidp8i_gpio_present;

extern int gss_initted;

extern void *blink(void *ptr);	// thread entry point to the gpio module
extern unsigned bcm_host_get_peripheral_address(void);
extern void debounce_switch(int row, int col, int ss, ms_time_t now_ms);
extern int map_peripheral(struct bcm2835_peripheral *p);
extern ms_time_t ms_time(ms_time_t* pt);
extern void sleep_ns(ns_time_t ns);
#define sleep_us(us) usleep(us)
#define sleep_ms(ms) usleep(ms * 1000)
void unmap_peripheral(struct bcm2835_peripheral *p);
 
#endif // !defined(PIDP8I_GPIO_H)
Added src/gpio-ils.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * gpio-ils.c: implements blink_core() for Ian Schofield's incandescent
 *             lamp simulator
 * 
 * Copyright © 2015-2017 Oscar Vermeulen, Ian Schofield, 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.
 *
 * www.obsolescenceguaranteed.blogspot.com
*/

#include "gpio-common.h"

#include "sim_defs.h"


//// CONSTANTS /////////////////////////////////////////////////////////

// Brightness range is [0, MAX_BRIGHTNESS) truncated.
#define MAX_BRIGHTNESS 32

// On each iteration, we add or subtract a proportion of the current LED
// value back to it as its new brightness, based on the LED's current
// internal state.  This gives a nonlinear increase/decrease behavior,
// where rising from "off" or dropping from "full-on" is fast to start
// and slows down as it approaches its destination.
//
// We use an asymmetric function depending on whether the LED is turning
// on or off to better mimic the behavior of an incandescent lamp, which
// reaches full brightness faster than it turns fully off.
#define RISING_FACTOR 0.02
#define FALLING_FACTOR 0.008


//// blink_core ////////////////////////////////////////////////////////
// The GPIO module's main loop core, called from thread entry point in
// gpio-common.c.

void blink_core(struct bcm2835_peripheral* pgpio, int* terminate)
{
    int i, j, k;
    const us_time_t intervl = 5;    // light each row of leds 5 µs
    ms_time_t now_ms;

	float brtval[96];
	uint8 brctr[96], bctr = 0, ndx;
	memset(brtval, 0, sizeof (brtval));

    while (*terminate == 0) {
        // prepare for lighting LEDs by setting col pins to output
        for (i = 0; i < ncols; i++) {
            INP_GPIO(cols[i]);
            OUT_GPIO(cols[i]);          // Define cols as output
        }
        if (bctr == 0) {
			memset(brctr, 0, sizeof (brctr));
            bctr = MAX_BRIGHTNESS;
        }

		// Increase or decrease each LED's brightness based on whether
		// it is currently enabled.  These values affect the duty cycle
		// of the signal put out by the GPIO thread to each LED, thus
		// controlling brightness.
		float *p = brtval;
		for (int row = 0; row < nledrows; ++row) {
			for (int col = 0, msk = 1; col < ncols; ++col, ++p, msk <<= 1) {
				if (ledstatus[row] & msk)
					*p += (MAX_BRIGHTNESS - *p) * RISING_FACTOR;
				else
					*p -= *p * FALLING_FACTOR;
			}
		}

        // light up LEDs
        for (i = ndx = 0; i < nledrows; i++) {
            // Toggle columns for this ledrow (which LEDs should be on (CLR = on))
            for (k = 0; k < ncols; k++, ndx++) {
                if (++brctr[ndx] < brtval[ndx])
                    GPIO_CLR = 1 << cols[k];
                else
                    GPIO_SET = 1 << cols[k];
            }

            // Toggle this ledrow on
            INP_GPIO(ledrows[i]);
            GPIO_SET = 1 << ledrows[i]; // test for flash problem
            OUT_GPIO(ledrows[i]);

            sleep_us(intervl);

            // Toggle ledrow off
            GPIO_CLR = 1 << ledrows[i]; // superstition
            INP_GPIO(ledrows[i]);

            sleep_us(intervl);
        }

        // prepare for reading switches
        ms_time(&now_ms);
        for (i = 0; i < ncols; i++) {
            INP_GPIO(cols[i]);          // flip columns to input. Need internal pull-ups enabled.
        }

        // read three rows of switches
        for (i = 0; i < nrows; i++) {
            INP_GPIO(rows[i]);          
            OUT_GPIO(rows[i]);          // turn on one switch row
            GPIO_CLR = 1 << rows[i];    // and output 0V to overrule built-in pull-up from column input pin

            sleep_ns(intervl * 1000 / 100);

            for (j = 0; j < ncols; j++) {   // ncols switches in each row
                int ss = GPIO_READ(cols[j]);
                debounce_switch(i, j, !!ss, now_ms);
            }

            INP_GPIO(rows[i]);          // stop sinking current from this row of switches
        }

        fflush(stdout);
        gss_initted = 1;
        bctr--;

#if defined(HAVE_SCHED_YIELD)
        sched_yield();
#endif
    }
}
Added src/gpio-nls.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * gpio-nls.c: implements blink_core() with the original simple LED driver
 *
 * This file differs from gpio.c in that it does not include the
 * incandescent lamp simulator feature by Ian Schofield.  It is
 * more directly descended from the original gpio.c by Oscar Vermeulen.
 * 
 * Copyright © 2015-2017 Oscar Vermeulen 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.
 *
 * www.obsolescenceguaranteed.blogspot.com
*/

#include "gpio-common.h"


//// blink_core ////////////////////////////////////////////////////////
// The GPIO module's main loop core, called from thread entry point in
// gpio-common.c.

void blink_core(struct bcm2835_peripheral* pgpio, int* terminate)
{
    int i, j, k;
    const us_time_t intervl = 300;  // light each row of leds 300 µs
    ms_time_t now_ms;

    while (*terminate == 0) {
        // prepare for lighting LEDs by setting col pins to output
        for (i = 0; i < ncols; i++) {
            INP_GPIO(cols[i]);
            OUT_GPIO(cols[i]);          // Define cols as output
        }
        
        // light up LEDs
        for (i = 0; i < nledrows; i++) {
            // Toggle columns for this ledrow (which LEDs should be on (CLR = on))
            for (k = 0; k <ncols; k++) {
                if ((ledstatus[i] & (1 << k)) == 0)
                    GPIO_SET = 1 << cols[k];
                else 
                    GPIO_CLR = 1 << cols[k];
            }

            // Toggle this ledrow on
            INP_GPIO(ledrows[i]);
            GPIO_SET = 1 << ledrows[i]; // test for flash problem
            OUT_GPIO(ledrows[i]);

            sleep_us(intervl);

            // Toggle ledrow off
            GPIO_CLR = 1 << ledrows[i]; // superstition
            INP_GPIO(ledrows[i]);
            sleep_ns(10000); // waste of cpu cycles but may help against udn2981 ghosting, not flashes though
        }

        // prepare for reading switches
        ms_time(&now_ms);
        for (i = 0; i < ncols; i++)
            INP_GPIO(cols[i]);          // flip columns to input. Need internal pull-ups enabled.

        // read three rows of switches
        for (i = 0; i < nrows; i++) {
            INP_GPIO(rows[i]);          
            OUT_GPIO(rows[i]);          // turn on one switch row
            GPIO_CLR = 1 << rows[i];    // and output 0V to overrule built-in pull-up from column input pin

            sleep_us(intervl / 100);

            for (j = 0; j < ncols; j++) {      // ncols switches in each row
                int ss = GPIO_READ(cols[j]);
                debounce_switch(i, j, !!ss, now_ms);
            }

            INP_GPIO(rows[i]);          // stop sinking current from this row of switches
        }

        fflush(stdout);
        gss_initted = 1;

#if defined(HAVE_SCHED_YIELD)
        sched_yield();
#endif
    }
}
Added src/scanswitch.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Scan switches for PiDP-8/I front panel
 * 
 * www.obsolescenceguaranteed.blogspot.com
 *
 * Copyright (c) 2015-2016 Oscar Vermeulen
 *
 * 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.
 * 
*/

#include "gpio-common.h"

#define short_wait() sleep_ms(100)

#define pgpio (&gpio)


int main()
{
	int i,j,k,switchscan[2], tmp;
	struct bcm2835_peripheral gpio;
	
	// ------------ Find gpio address (different for Pi 2) -------------

	gpio.addr_p = bcm_host_get_peripheral_address() + 0x200000;

	if (gpio.addr_p== 0x20200000) printf("scanswitch - RPi Plus\n");
	else printf("scanswitch - RPi 2\n");

	if(map_peripheral(&gpio) == -1) 
	{	printf("Failed to map the physical GPIO registers into the virtual memory space.\n");
		return -1;
	}

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

	// 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?
		switchscan[row-1]=0;

		for (j=0;j<ncols;j++)			// 12 switches in each row
		{	tmp = GPIO_READ(cols[j]);
			if (tmp==0)
				switchscan[row-1] += 1<<j;
		}
		INP_GPIO(rows[row]);			// stop sinking current from this row of switches
	}

	unmap_peripheral(&gpio);

	if ( ((switchscan[1] >> 6) & 1) == 1 )	// STOP switch enabled,
		return 8;				// 8: STOP enabled, no bootscript
	else
		return (switchscan[0] >> 6) & 07;	// 0-7: x.script to be used in PiDP-8/I
}

Added src/scp.c.in.

more than 10,000 changes

Added src/scp.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* scp.h: simulator control program headers

   Copyright (c) 1993-2009, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not
   be used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   05-Dec-10    MP      Added macro invocation of sim_debug 
   09-Aug-06    JDB     Added assign_device and deassign_device
   14-Jul-06    RMS     Added sim_activate_abs
   06-Jan-06    RMS     Added fprint_stopped_gen
                        Changed arg type in sim_brk_test
   07-Feb-05    RMS     Added ASSERT command
   09-Sep-04    RMS     Added reset_all_p
   14-Feb-04    RMS     Added debug prototypes (from Dave Hittner)
   02-Jan-04    RMS     Split out from SCP
*/

#ifndef SIM_SCP_H_
#define SIM_SCP_H_     0

#ifdef  __cplusplus
extern "C" {
#endif

/* run_cmd parameters */

#define RU_RUN          0                               /* run */
#define RU_GO           1                               /* go */
#define RU_STEP         2                               /* step */
#define RU_NEXT         3                               /* step or step/over */
#define RU_CONT         4                               /* continue */
#define RU_BOOT         5                               /* boot */

/* exdep_cmd parameters */

#define EX_D            0                               /* deposit */
#define EX_E            1                               /* examine */
#define EX_I            2                               /* interactive */

/* brk_cmd parameters */

#define SSH_ST          0                               /* set */
#define SSH_SH          1                               /* show */
#define SSH_CL          2                               /* clear */

/* get_sim_opt parameters */

#define CMD_OPT_SW      001                             /* switches */
#define CMD_OPT_OF      002                             /* output file */
#define CMD_OPT_SCH     004                             /* search */
#define CMD_OPT_DFT     010                             /* defaults */

/* Command processors */

t_stat reset_cmd (int32 flag, CONST char *ptr);
t_stat exdep_cmd (int32 flag, CONST char *ptr);
t_stat eval_cmd (int32 flag, CONST char *ptr);
t_stat load_cmd (int32 flag, CONST char *ptr);
t_stat run_cmd (int32 flag, CONST char *ptr);
void run_cmd_message (const char *unechod_cmdline, t_stat r);
t_stat attach_cmd (int32 flag, CONST char *ptr);
t_stat detach_cmd (int32 flag, CONST char *ptr);
t_stat assign_cmd (int32 flag, CONST char *ptr);
t_stat deassign_cmd (int32 flag, CONST char *ptr);
t_stat save_cmd (int32 flag, CONST char *ptr);
t_stat restore_cmd (int32 flag, CONST char *ptr);
t_stat exit_cmd (int32 flag, CONST char *ptr);
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 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 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__
#define GCC_FMT_ATTR(n, m)
#endif
#if !defined(GCC_FMT_ATTR)
#define GCC_FMT_ATTR(n, m) __attribute__ ((format (__printf__, n, m)))
#endif

/* Utility routines */

t_stat sim_process_event (void);
t_stat sim_activate (UNIT *uptr, int32 interval);
t_stat _sim_activate (UNIT *uptr, int32 interval);
t_stat sim_activate_abs (UNIT *uptr, int32 interval);
t_stat sim_activate_notbefore (UNIT *uptr, int32 rtime);
t_stat sim_activate_after (UNIT *uptr, uint32 usecs_walltime);
t_stat sim_activate_after_d (UNIT *uptr, double usecs_walltime);
t_stat _sim_activate_after (UNIT *uptr, double usecs_walltime);
t_stat sim_activate_after_abs (UNIT *uptr, uint32 usecs_walltime);
t_stat sim_activate_after_abs_d (UNIT *uptr, double usecs_walltime);
t_stat _sim_activate_after_abs (UNIT *uptr, double usecs_walltime);
t_stat sim_cancel (UNIT *uptr);
t_bool sim_is_active (UNIT *uptr);
int32 sim_activate_time (UNIT *uptr);
int32 _sim_activate_time (UNIT *uptr);
double sim_activate_time_usecs (UNIT *uptr);
t_stat sim_run_boot_prep (int32 flag);
double sim_gtime (void);
uint32 sim_grtime (void);
int32 sim_qcount (void);
t_stat attach_unit (UNIT *uptr, CONST char *cptr);
t_stat detach_unit (UNIT *uptr);
t_stat assign_device (DEVICE *dptr, const char *cptr);
t_stat deassign_device (DEVICE *dptr);
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);
t_stat get_yn (const char *ques, t_stat deflt);
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_strncasecmp (const char *string1, const char *string2, size_t len);
int sim_strcasecmp (const char *string1, const char *string2);
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);
CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi,
    uint32 rdx, t_addr max, char term);
t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize);
char *sim_encode_quoted_string (const uint8 *iptr, uint32 size);
void fprint_buffer_string (FILE *st, const uint8 *buf, uint32 size);
t_value strtotv (CONST char *cptr, CONST char **endptr, uint32 radix);
int Fprintf (FILE *f, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
/* Use scp.c provided fprintf function */
#define fprintf Fprintf
#define fputs(_s,_f) Fprintf(_f,"%s",_s)
#define fputc(_c,_f) Fprintf(_f,"%c",_c)
t_stat sim_set_memory_load_file (const unsigned char *data, size_t size);
int Fgetc (FILE *f);
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);
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);
t_stat sim_register_internal_device (DEVICE *dptr);
void sim_sub_args (char *in_str, size_t in_str_size, char *do_arg[]);
REG *find_reg (CONST char *ptr, CONST char **optr, DEVICE *dptr);
CTAB *find_ctab (CTAB *tab, const char *gbuf);
C1TAB *find_c1tab (C1TAB *tab, const char *gbuf);
SHTAB *find_shtab (SHTAB *tab, const char *gbuf);
t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
BRKTAB *sim_brk_fnd (t_addr loc);
uint32 sim_brk_test (t_addr bloc, uint32 btyp);
void sim_brk_clrspc (uint32 spc, uint32 btyp);
void sim_brk_npc (uint32 cnt);
void sim_brk_setact (const char *action);
const char *sim_brk_message(void);
t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay);
t_stat sim_show_send_input (FILE *st, const SEND *snd);
t_bool sim_send_poll_data (SEND *snd, t_stat *stat);
t_stat sim_send_clear (SEND *snd);
t_stat sim_set_expect (EXPECT *exp, CONST char *cptr);
t_stat sim_set_noexpect (EXPECT *exp, const char *cptr);
t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, uint32 after, int32 switches, const char *act);
t_stat sim_exp_clr (EXPECT *exp, const char *match);
t_stat sim_exp_clrall (EXPECT *exp);
t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match);
t_stat sim_exp_showall (FILE *st, const EXPECT *exp);
t_stat sim_exp_check (EXPECT *exp, uint8 data);
CONST char *match_ext (CONST char *fnam, const char *ext);
t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
const char *sim_error_text (t_stat stat);
t_stat sim_string_to_stat (const char *cptr, t_stat *cond);
t_stat sim_cancel_step (void);
void sim_printf (const char *fmt, ...) GCC_FMT_ATTR(1, 2);
void sim_perror (const char *msg);
t_stat sim_messagef (t_stat stat, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void sim_data_trace(DEVICE *dptr, UNIT *uptr, const uint8 *data, const char *position, size_t len, const char *txt, uint32 reason);
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)
#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, ...);
t_stat scp_vhelp (FILE *st, DEVICE *dptr,
                  UNIT *uptr, int32 flag, const char *help, const char *cptr, va_list ap);
t_stat scp_helpFromFile (FILE *st, DEVICE *dptr,
                         UNIT *uptr, int32 flag, const char *help, const char *cptr, ...);
t_stat scp_vhelpFromFile (FILE *st, DEVICE *dptr,
                          UNIT *uptr, int32 flag, const char *help, const char *cptr, va_list ap);

/* Global data */

extern DEVICE *sim_dflt_dev;
extern int32 sim_interval;
extern int32 sim_switches;
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 */
extern int32 sim_deb_switches;                          /* debug display flags */
extern struct timespec sim_deb_basetime;                /* debug base time for relative time output */
extern DEVICE **sim_internal_devices;
extern uint32 sim_internal_device_count;
extern UNIT *sim_clock_queue;
extern int32 sim_is_running;
extern t_bool sim_processing_event;                     /* Called from sim_process_event */
extern char *sim_prompt;                                /* prompt string */
extern const char *sim_savename;                        /* Simulator Name used in Save/Restore files */
extern t_value *sim_eval;
extern volatile int32 stop_cpu;
extern uint32 sim_brk_types;                            /* breakpoint info */
extern uint32 sim_brk_dflt;
extern uint32 sim_brk_summ;
extern uint32 sim_brk_match_type;
extern t_addr sim_brk_match_addr;
extern BRKTYPTAB *sim_brk_type_desc;                      /* type descriptions */
extern FILE *stdnul;
extern t_bool sim_asynch_enabled;
#if defined(SIM_ASYNCH_IO)
int sim_aio_update_queue (void);
void sim_aio_activate (ACTIVATE_API caller, UNIT *uptr, int32 event_time);
#endif

/* VM interface */

extern char sim_name[];
extern DEVICE *sim_devices[];
extern REG *sim_PC;
extern const char *sim_stop_messages[];
extern t_stat sim_instr (void);
extern t_stat sim_load (FILE *ptr, CONST char *cptr, CONST char *fnam, int flag);
extern int32 sim_emax;
extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,
    UNIT *uptr, int32 sw);
extern t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val,
    int32 sw);

/* The per-simulator init routine is a weak global that defaults to NULL
   The other per-simulator pointers can be overrriden by the init routine */

WEAK extern void (*sim_vm_init) (void);
extern char *(*sim_vm_read) (char *ptr, int32 size, FILE *stream);
extern void (*sim_vm_post) (t_bool from_scp);
extern CTAB *sim_vm_cmd;
extern void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr);
extern void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr);
extern t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr);
extern t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason);
extern t_value (*sim_vm_pc_value) (void);
extern t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs);

#ifdef  __cplusplus
}
#endif

#endif
Added src/sim_console.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_console.c: simulator console I/O library

   Copyright (c) 1993-2014, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   02-Jan-14    RMS     Added tab stop routines
   18-Mar-12    RMS     Removed unused reference to sim_switches (Dave Bryan)
   07-Dec-11    MP      Added sim_ttisatty to support reasonable behaviour (i.e. 
                        avoid in infinite loop) in the main command input
                        loop when EOF is detected and input is coming from 
                        a file (or a null device: /dev/null or NUL:) This may
                        happen when a simulator is running in a background 
                        process.
   17-Apr-11    MP      Cleaned up to support running in a background/detached
                        process
   20-Jan-11    MP      Fixed support for BREAK key on Windows to account 
                        for/ignore other keyboard Meta characters.
   18-Jan-11    MP      Added log file reference count support
   17-Jan-11    MP      Added support for a "Buffered" behaviors which include:
                        - If Buffering is enabled and Telnet is enabled, a
                          telnet connection is not required for simulator 
                          operation (instruction execution).
                        - If Buffering is enabled, all console output is 
                          written to the buffer at all times (deleting the
                          oldest buffer contents on overflow).
                        - when a connection is established on the console 
                          telnet port, the whole contents of the Buffer is
                          presented on the telnet session and connection 
                          will then proceed as if the connection had always
                          been there.
                        This concept allows a simulator to run in the background
                        and when needed a console session to be established.  
                        The "when needed" case usually will be interested in 
                        what already happened before looking to address what 
                        to do, hence the buffer contents being presented.
   28-Dec-10    MP      Added support for BREAK key on Windows
   30-Sep-06    RMS     Fixed non-printable characters in KSR mode
   22-Jun-06    RMS     Implemented SET/SHOW PCHAR
   31-May-06    JDB     Fixed bug if SET CONSOLE DEBUG with no argument
   22-Nov-05    RMS     Added central input/output conversion support
   05-Nov-04    RMS     Moved SET/SHOW DEBUG under CONSOLE hierarchy
   28-Oct-04    JDB     Fixed SET CONSOLE to allow comma-separated parameters
   20-Aug-04    RMS     Added OS/2 EMX fixes (Holger Veit)
   14-Jul-04    RMS     Revised Windows console code (Dave Bryan)
   28-May-04    RMS     Added SET/SHOW CONSOLE
                RMS     Added break, delete character maps
   02-Jan-04    RMS     Removed timer routines, added Telnet console routines
                RMS     Moved console logging to OS-independent code
   25-Apr-03    RMS     Added long seek support (Mark Pizzolato)
                        Added Unix priority control (Mark Pizzolato)
   24-Sep-02    RMS     Removed VT support, added Telnet console support
                        Added CGI support (Brian Knittel)
                        Added MacOS sleep (Peter Schorn)
   14-Jul-02    RMS     Added Windows priority control (Mark Pizzolato)
   20-May-02    RMS     Added Windows VT support (Fischer Franz)
   01-Feb-02    RMS     Added VAX fix (Robert Alan Byer)
   19-Sep-01    RMS     More MacOS changes
   31-Aug-01    RMS     Changed int64 to t_int64 for Windoze
   20-Jul-01    RMS     Added MacOS support (Louis Chretien, Peter Schorn, Ben Supnik)
   15-May-01    RMS     Added logging support
   05-Mar-01    RMS     Added clock calibration support
   08-Dec-00    BKR     Added OS/2 support (Bruce Ray)
   18-Aug-98    RMS     Added BeOS support
   13-Oct-97    RMS     Added NetBSD terminal support
   25-Jan-97    RMS     Added POSIX terminal I/O support
   02-Jan-97    RMS     Fixed bug in sim_poll_kbd

   This module implements the following routines to support terminal and 
   Remote Console I/O:

   sim_poll_kbd                 poll for keyboard input
   sim_putchar                  output character to console
   sim_putchar_s                output character to console, stall if congested
   sim_set_console              set console parameters
   sim_show_console             show console parameters
   sim_set_remote_console       set remote console parameters
   sim_show_remote_console      show remote console parameters
   sim_set_cons_buff            set console buffered
   sim_set_cons_unbuff          set console unbuffered
   sim_set_cons_log             set console log
   sim_set_cons_nolog           set console nolog
   sim_show_cons_buff           show console buffered
   sim_show_cons_log            show console log
   sim_tt_inpcvt                convert input character per mode
   sim_tt_outcvt                convert output character per mode
   sim_cons_get_send            get console send structure address
   sim_cons_get_expect          get console expect structure address
   sim_show_cons_send_input     show pending input data
   sim_show_cons_expect         show expect rules and state
   sim_ttinit                   called once to get initial terminal state
   sim_ttrun                    called to put terminal into run state
   sim_ttcmd                    called to return terminal to command state
   sim_ttclose                  called once before the simulator exits
   sim_ttisatty                 called to determine if running interactively
   sim_os_poll_kbd              poll for keyboard input
   sim_os_putchar               output character to console

   The first group is OS-independent; the second group is OS-dependent.

   The following routines are exposed but deprecated:

   sim_set_telnet               set console to Telnet port
   sim_set_notelnet             close console Telnet port
   sim_show_telnet              show console status
*/

#include "sim_defs.h"
#include "sim_tmxr.h"
#include "sim_serial.h"
#include "sim_timer.h"
#include <ctype.h>
#include <math.h>

#ifdef __HAIKU__
#define nice(n) ({})
#endif

/* Forward Declaraations of Platform specific routines */

static t_stat sim_os_poll_kbd (void);
static t_bool sim_os_poll_kbd_ready (int ms_timeout);
static t_stat sim_os_putchar (int32 out);
static t_stat sim_os_ttinit (void);
static t_stat sim_os_ttrun (void);
static t_stat sim_os_ttcmd (void);
static t_stat sim_os_ttclose (void);
static t_bool sim_os_ttisatty (void);

static t_stat sim_set_rem_telnet (int32 flag, CONST char *cptr);
static t_stat sim_set_rem_bufsize (int32 flag, CONST char *cptr);
static t_stat sim_set_rem_connections (int32 flag, CONST char *cptr);
static t_stat sim_set_rem_timeout (int32 flag, CONST char *cptr);
static t_stat sim_set_rem_master (int32 flag, CONST char *cptr);

/* Deprecated CONSOLE HALT, CONSOLE RESPONSE and CONSOLE DELAY support */
static t_stat sim_set_halt (int32 flag, CONST char *cptr);
static t_stat sim_set_response (int32 flag, CONST char *cptr);
static t_stat sim_set_delay (int32 flag, CONST char *cptr);


#define KMAP_WRU        0
#define KMAP_BRK        1
#define KMAP_DEL        2
#define KMAP_MASK       0377
#define KMAP_NZ         0400

int32 sim_int_char = 005;                               /* interrupt character */
int32 sim_brk_char = 000;                               /* break character */
int32 sim_tt_pchar = 0x00002780;
#if defined (_WIN32) || defined (__OS2__) || (defined (__MWERKS__) && defined (macintosh))
int32 sim_del_char = '\b';                              /* delete character */
#else
int32 sim_del_char = 0177;
#endif

static t_stat sim_con_poll_svc (UNIT *uptr);                /* console connection poll routine */
static t_stat sim_con_reset (DEVICE *dptr);                 /* console reset routine */
static t_stat sim_con_attach (UNIT *uptr, CONST char *ptr); /* console attach routine (save,restore) */
static t_stat sim_con_detach (UNIT *uptr);                  /* console detach routine (save,restore) */

UNIT sim_con_units[2] = {{ UDATA (&sim_con_poll_svc, UNIT_ATTABLE, 0)}}; /* console connection unit */
#define sim_con_unit sim_con_units[0]

/* debugging bitmaps */
#define DBG_TRC  TMXR_DBG_TRC                           /* trace routine calls */
#define DBG_XMT  TMXR_DBG_XMT                           /* display Transmitted Data */
#define DBG_RCV  TMXR_DBG_RCV                           /* display Received Data */
#define DBG_RET  TMXR_DBG_RET                           /* display Returned Received Data */
#define DBG_ASY  TMXR_DBG_ASY                           /* asynchronous thread activity */
#define DBG_CON  TMXR_DBG_CON                           /* display connection activity */
#define DBG_EXP  0x00000001                             /* Expect match activity */
#define DBG_SND  0x00000002                             /* Send (Inject) data activity */

static DEBTAB sim_con_debug[] = {
  {"TRC",    DBG_TRC, "routine calls"},
  {"XMT",    DBG_XMT, "Transmitted Data"},
  {"RCV",    DBG_RCV, "Received Data"},
  {"RET",    DBG_RET, "Returned Received Data"},
  {"ASY",    DBG_ASY, "asynchronous activity"},
  {"CON",    DBG_CON, "connection activity"},
  {"EXP",    DBG_EXP, "Expect match activity"},
  {"SND",    DBG_SND, "Send (Inject) data activity"},
  {0}
};

static REG sim_con_reg[] = {
    { ORDATAD (WRU,   sim_int_char,  8, "interrupt character") },
    { ORDATAD (BRK,   sim_brk_char,  8, "break character") },
    { ORDATAD (DEL,   sim_del_char,  8, "delete character ") },
    { ORDATAD (PCHAR, sim_tt_pchar, 32, "printable character mask") },
  { 0 },
};

static MTAB sim_con_mod[] = {
  { 0 },
};

static const char *sim_con_telnet_description (DEVICE *dptr)
{
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, 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};
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)
{
sim_con_console_port = FALSE;
return SCPE_OK;
}

/* Unit service for console connection polling */

static t_stat sim_con_poll_svc (UNIT *uptr)
{
if ((sim_con_tmxr.master == 0) &&                       /* not Telnet and not serial and not WRU polling? */
    (sim_con_ldsc.serport == 0) &&
    (sim_con_console_port))
    return SCPE_OK;                                     /* done */
if (tmxr_poll_conn (&sim_con_tmxr) >= 0)                /* poll connect */
    sim_con_ldsc.rcve = 1;                              /* rcv enabled */
sim_activate_after(uptr, 1000000);                      /* check again in 1 second */
if (!sim_con_console_port)                              /* WRU poll needed */
    sim_poll_kbd();                                     /* sets global stop_cpu when WRU received */
if (sim_con_ldsc.conn)
    tmxr_send_buffered_data (&sim_con_ldsc);            /* try to flush any buffered data */
return SCPE_OK;
}

static t_stat sim_con_reset (DEVICE *dptr)
{
dptr->units[1].flags = UNIT_DIS;
return sim_con_poll_svc (&dptr->units[0]);              /* establish polling as needed */
}

/* Console Attach/Detach - only used indirectly in restore */

static t_stat sim_con_attach (UNIT *uptr, CONST char *ptr)
{
return tmxr_attach (&sim_con_tmxr, &sim_con_unit, ptr);
}

static t_stat sim_con_detach (UNIT *uptr)
{
return sim_set_notelnet (0, NULL);
}

/* Set/show data structures */

static CTAB set_con_tab[] = {
    { "WRU", &sim_set_kmap, KMAP_WRU | KMAP_NZ },
    { "BRK", &sim_set_kmap, KMAP_BRK },
    { "DEL", &sim_set_kmap, KMAP_DEL |KMAP_NZ },
    { "PCHAR", &sim_set_pchar, 0 },
    { "SPEED", &sim_set_cons_speed, 0 },
    { "TELNET", &sim_set_telnet, 0 },
    { "NOTELNET", &sim_set_notelnet, 0 },
    { "SERIAL", &sim_set_serial, 0 },
    { "NOSERIAL", &sim_set_noserial, 0 },
    { "LOG", &sim_set_logon, 0 },
    { "NOLOG", &sim_set_logoff, 0 },
    { "DEBUG", &sim_set_debon, 0 },
    { "NODEBUG", &sim_set_deboff, 0 },
#define CMD_WANTSTR     0100000
    { "HALT", &sim_set_halt, 1 | CMD_WANTSTR },
    { "NOHALT", &sim_set_halt, 0 },
    { "DELAY", &sim_set_delay, 0 },
    { "RESPONSE", &sim_set_response, 1 | CMD_WANTSTR },
    { "NORESPONSE", &sim_set_response, 0 },
    { NULL, NULL, 0 }
    };

static CTAB set_rem_con_tab[] = {
    { "CONNECTIONS", &sim_set_rem_connections, 0 },
    { "TELNET", &sim_set_rem_telnet, 1 },
    { "BUFFERSIZE", &sim_set_rem_bufsize, 1 },
    { "NOTELNET", &sim_set_rem_telnet, 0 },
    { "TIMEOUT", &sim_set_rem_timeout, 0 },
    { "MASTER", &sim_set_rem_master, 1 },
    { "NOMASTER", &sim_set_rem_master, 0 },
    { NULL, NULL, 0 }
    };

static SHTAB show_con_tab[] = {
    { "WRU", &sim_show_kmap, KMAP_WRU },
    { "BRK", &sim_show_kmap, KMAP_BRK },
    { "DEL", &sim_show_kmap, KMAP_DEL },
    { "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 },
    { "INPUT", &sim_show_cons_send_input, 0 },
    { "RESPONSE", &sim_show_cons_send_input, 0 },
    { "DELAY", &sim_show_cons_expect, 0 },
    { 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 },
    { "NOBUFFERED", &sim_set_cons_unbuff, 0 },
    { "UNBUFFERED", &sim_set_cons_unbuff, 0 },
    { NULL, NULL, 0 }
    };

static CTAB set_con_serial_tab[] = {
    { "LOG", &sim_set_cons_log, 0 },
    { "NOLOG", &sim_set_cons_nolog, 0 },
    { NULL, NULL, 0 }
    };

static int32 *cons_kmap[] = {
    &sim_int_char,
    &sim_brk_char,
    &sim_del_char
    };

/* Console I/O package.

   The console terminal can be attached to the controlling window
   or to a Telnet connection.  If attached to a Telnet connection,
   the console is described by internal terminal multiplexor
   sim_con_tmxr and internal terminal line description sim_con_ldsc.
*/

/* SET CONSOLE command */

t_stat sim_set_console (int32 flag, CONST char *cptr)
{
char *cvptr, gbuf[CBUFSIZE];
CTAB *ctptr;
t_stat r;

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_2FARG;
while (*cptr != 0) {                                    /* do all mods */
    cptr = get_glyph_nc (cptr, gbuf, ',');              /* get modifier */
    if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
        *cvptr++ = 0;
    get_glyph (gbuf, gbuf, 0);                          /* modifier to UC */
    if ((ctptr = find_ctab (set_con_tab, gbuf))) {      /* match? */
        r = ctptr->action (ctptr->arg, cvptr);          /* do the rest */
        if (r != SCPE_OK)
            return r;
        }
    else return SCPE_NOPARAM;
    }
return SCPE_OK;
}

/* SHOW CONSOLE command */

t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
char gbuf[CBUFSIZE];
SHTAB *shptr;
int32 i;

if (*cptr == 0) {                                       /* show all */
    for (i = 0; show_con_tab[i].name; i++)
        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;
    }
return SCPE_OK;
}

t_stat sim_rem_con_poll_svc (UNIT *uptr);               /* remote console connection poll routine */
t_stat sim_rem_con_data_svc (UNIT *uptr);               /* remote console connection data routine */
t_stat sim_rem_con_reset (DEVICE *dptr);                /* remote console reset routine */
UNIT sim_rem_con_unit[2] = {
    { UDATA (&sim_rem_con_poll_svc, UNIT_IDLE, 0)  },   /* remote console connection polling unit */
    { UDATA (&sim_rem_con_data_svc, UNIT_IDLE|UNIT_DIS, 0)  }};  /* console data handling unit */

DEBTAB sim_rem_con_debug[] = {
  {"TRC",    DBG_TRC, "routine calls"},
  {"XMT",    DBG_XMT, "Transmitted Data"},
  {"RCV",    DBG_RCV, "Received Data"},
  {"CON",    DBG_CON, "connection activity"},
  {0}
};

MTAB sim_rem_con_mod[] = {
  { 0 },
};

static const char *sim_rem_con_description (DEVICE *dptr)
{
return "Remote Console Facility";
}

DEVICE sim_remote_console = {
    "REM-CON", sim_rem_con_unit, NULL, sim_rem_con_mod, 
    2, 0, 0, 0, 0, 0, 
    NULL, NULL, sim_rem_con_reset, NULL, NULL, NULL, 
    NULL, DEV_DEBUG | DEV_NOSAVE, 0, sim_rem_con_debug,
    NULL, NULL, NULL, NULL, NULL, sim_rem_con_description};
#define MAX_REMOTE_SESSIONS 40          /* Arbitrary Session Limit */
static int32 *sim_rem_buf_size = NULL;
static int32 *sim_rem_buf_ptr = NULL;
static char **sim_rem_buf = NULL;
static t_bool *sim_rem_single_mode = NULL;  /* per line command mode (single command or must continue) */
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 */
static t_bool sim_rem_master_was_enabled = FALSE; /* Master was Enabled */
static t_bool sim_rem_master_was_connected = FALSE; /* Master Mode has been connected */
static t_offset sim_rem_cmd_log_start = 0;  /* Log File saved position */


/* SET REMOTE CONSOLE command */

t_stat sim_set_remote_console (int32 flag, CONST char *cptr)
{
char *cvptr, gbuf[CBUFSIZE];
CTAB *ctptr;
t_stat r;

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_2FARG;
while (*cptr != 0) {                                    /* do all mods */
    cptr = get_glyph_nc (cptr, gbuf, ',');              /* get modifier */
    if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
        *cvptr++ = 0;
    get_glyph (gbuf, gbuf, 0);                          /* modifier to UC */
    if ((ctptr = find_ctab (set_rem_con_tab, gbuf))) {  /* match? */
        r = ctptr->action (ctptr->arg, cvptr);          /* do the rest */
        if (r != SCPE_OK)
            return r;
        }
    else return SCPE_NOPARAM;
    }
return SCPE_OK;
}

/* SHOW REMOTE CONSOLE command */

t_stat sim_show_remote_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
int32 i, connections;
TMLN *lp;

if (*cptr != 0)
    return SCPE_NOPARAM;
if (sim_rem_active_number >= 0) {
    if (sim_rem_master_mode && (sim_rem_active_number == 0))
        fprintf (st, "Running from Master Mode Remote Console Connection\n");
    else
        fprintf (st, "Running from Remote Console Connection %d\n", sim_rem_active_number);
    }
if (sim_rem_con_tmxr.lines > 1)
    fprintf (st, "Remote Console Input Connections from %d sources are supported concurrently\n", sim_rem_con_tmxr.lines);
if (sim_rem_read_timeout)
    fprintf (st, "Remote Console Input automatically continues after %d seconds\n", sim_rem_read_timeout);
if (!sim_rem_con_tmxr.master)
    fprintf (st, "Remote Console Command input is disabled\n");
else {
    fprintf (st, "Remote Console Command Input listening on TCP port: %s\n", sim_rem_con_unit[0].filename);
    fprintf (st, "Remote Console Per Command Output buffer size:      %d bytes\n", sim_rem_con_tmxr.buffered);
    }
for (i=connections=0; i<sim_rem_con_tmxr.lines; i++) {
    lp = &sim_rem_con_tmxr.ldsc[i];
    if (!lp->conn)
        continue;
    ++connections;
    if (connections == 1)
        fprintf (st, "Remote Console Connections:\n");
    tmxr_fconns (st, lp, i);
    if (sim_rem_read_timeouts[i] != sim_rem_read_timeout) {
        if (sim_rem_read_timeouts[i])
            fprintf (st, "Remote Console Input on connection %d automatically continues after %d seconds\n", i, sim_rem_read_timeouts[i]);
        else
            fprintf (st, "Remote Console Input on connection %d does not continue automatically\n", i);
        }
    }
return SCPE_OK;
}

/* Unit service for remote console connection polling */

t_stat sim_rem_con_poll_svc (UNIT *uptr)
{
int32 c;

c = tmxr_poll_conn (&sim_rem_con_tmxr);
if (c >= 0) {                                           /* poll connect */
    TMLN *lp = &sim_rem_con_tmxr.ldsc[c];
    char wru_name[8];

    sim_activate_after(uptr+1, 1000000);                /* start data poll after 1 second */
    lp->rcve = 1;                                       /* rcv enabled */
    sim_rem_buf_ptr[c] = 0;                             /* start with empty command buffer */
    sim_rem_single_mode[c] = TRUE;                      /* start in single command mode */
    sim_rem_read_timeouts[c] = sim_rem_read_timeout;    /* Start with default timeout */
    if (isprint(sim_int_char&0xFF))
        sprintf(wru_name, "'%c'", sim_int_char&0xFF);
    else
        if (sim_int_char <= 26)
            sprintf(wru_name, "^%c", '@' + (sim_int_char&0xFF));
        else
            sprintf(wru_name, "'\\%03o'", sim_int_char&0xFF);
    tmxr_linemsgf (lp, "%s Remote Console\r\n"
                       "Enter single commands or to enter multiple command mode enter the %s character\r"
                       "%s",
                       sim_name, wru_name, 
                       ((sim_rem_master_mode && (c == 0)) ? "" : "\nSimulator Running..."));
    if (sim_rem_master_mode && (c == 0))                /* Master Mode session? */
        sim_rem_single_mode[c] = FALSE;                 /*  start in multi-command mode */
    tmxr_send_buffered_data (lp);                       /* flush buffered data */
    }
sim_activate_after(uptr, 1000000);                      /* check again in 1 second */
if (sim_con_ldsc.conn)
    tmxr_send_buffered_data (&sim_con_ldsc);            /* try to flush any buffered data */
return SCPE_OK;
}

static t_stat x_continue_cmd (int32 flag, CONST char *cptr)
{
return SCPE_IERR;           /* This routine should never be called */
}

static t_stat x_step_cmd (int32 flag, CONST char *cptr)
{
return SCPE_IERR;           /* This routine should never be called */
}

static t_stat x_run_cmd (int32 flag, CONST char *cptr)
{
return SCPE_IERR;           /* This routine should never be called */
}

static t_stat x_help_cmd (int32 flag, CONST char *cptr);

static CTAB allowed_remote_cmds[] = {
    { "EXAMINE",  &exdep_cmd,      EX_E },
    { "DEPOSIT",  &exdep_cmd,      EX_D },
    { "EVALUATE", &eval_cmd,          0 },
    { "ATTACH",   &attach_cmd,        0 },
    { "DETACH",   &detach_cmd,        0 },
    { "ASSIGN",   &assign_cmd,        0 },
    { "DEASSIGN", &deassign_cmd,      0 },
    { "CONTINUE", &x_continue_cmd,    0 },
    { "STEP",     &x_step_cmd,        0 },
    { "PWD",      &pwd_cmd,           0 },
    { "SAVE",     &save_cmd,          0 },
    { "DIR",      &dir_cmd,           0 },
    { "LS",       &dir_cmd,           0 },
    { "ECHO",     &echo_cmd,          0 },
    { "SET",      &set_cmd,           0 },
    { "SHOW",     &show_cmd,          0 },
    { "HELP",     &x_help_cmd,        0 },
    { NULL,       NULL }
    };

static CTAB allowed_master_remote_cmds[] = {
    { "EXAMINE",  &exdep_cmd,      EX_E },
    { "DEPOSIT",  &exdep_cmd,      EX_D },
    { "EVALUATE", &eval_cmd,          0 },
    { "ATTACH",   &attach_cmd,        0 },
    { "DETACH",   &detach_cmd,        0 },
    { "ASSIGN",   &assign_cmd,        0 },
    { "DEASSIGN", &deassign_cmd,      0 },
    { "CONTINUE", &x_continue_cmd,    0 },
    { "STEP",     &x_step_cmd,        0 },
    { "PWD",      &pwd_cmd,           0 },
    { "SAVE",     &save_cmd,          0 },
    { "CD",       &set_default_cmd,   0 },
    { "DIR",      &dir_cmd,           0 },
    { "LS",       &dir_cmd,           0 },
    { "ECHO",     &echo_cmd,          0 },
    { "SET",      &set_cmd,           0 },
    { "SHOW",     &show_cmd,          0 },
    { "HELP",     &x_help_cmd,        0 },
    { "EXIT",     &exit_cmd,          0 },
    { "QUIT",     &exit_cmd,          0 },
    { "RUN",      &x_run_cmd,    RU_RUN },
    { "GO",       &x_run_cmd,     RU_GO },
    { "BOOT",     &x_run_cmd,   RU_BOOT },
    { "BREAK",    &brk_cmd,      SSH_ST },
    { "NOBREAK",  &brk_cmd,      SSH_CL },
    { NULL,       NULL }
    };

static CTAB allowed_single_remote_cmds[] = {
    { "ATTACH",   &attach_cmd,        0 },
    { "DETACH",   &detach_cmd,        0 },
    { "EXAMINE",  &exdep_cmd,      EX_E },
    { "EVALUATE", &eval_cmd,          0 },
    { "PWD",      &pwd_cmd,           0 },
    { "DIR",      &dir_cmd,           0 },
    { "LS",       &dir_cmd,           0 },
    { "ECHO",     &echo_cmd,          0 },
    { "SHOW",     &show_cmd,          0 },
    { "HELP",     &x_help_cmd,        0 },
    { NULL,       NULL }
    };

static t_stat x_help_cmd (int32 flag, CONST char *cptr)
{
CTAB *cmdp, *cmdph;

if (*cptr) {
    int32 saved_switches = sim_switches;
    t_stat r;

    sim_switches |= SWMASK ('F');
    r = help_cmd (flag, cptr);
    sim_switches = saved_switches;
    return r;
    }
sim_printf ("Help is available for the following Remote Console commands:\r\n");
for (cmdp=allowed_remote_cmds; cmdp->name != NULL; ++cmdp) {
    cmdph = find_cmd (cmdp->name);
    if (cmdph && cmdph->help)
        sim_printf ("    %s\r\n", cmdp->name);
    }
sim_printf ("Enter \"HELP cmd\" for detailed help on a command\r\n");
return SCPE_OK;
}

static t_stat _sim_rem_message (const char *cmd, t_stat stat)
{
CTAB *cmdp = NULL;
t_stat stat_nomessage = stat & SCPE_NOMESSAGE;  /* extract possible message supression flag */

cmdp = find_cmd (cmd);
stat = SCPE_BARE_STATUS(stat);              /* remove possible flag */
if (!stat_nomessage) {
    if (cmdp && (cmdp->message))                /* special message handler? */
        cmdp->message (NULL, stat);             /* let it deal with display */
    else {
        if (stat >= SCPE_BASE)                  /* error? */
            sim_printf ("%s\r\n", sim_error_text (stat));
        }
    }
return stat;
}

static void _sim_rem_log_out (TMLN *lp)
{
char cbuf[4*CBUFSIZE];

if (sim_log) {
    int32 unwritten;

    fflush (sim_log);
    sim_fseeko (sim_log, sim_rem_cmd_log_start, SEEK_SET);
    cbuf[sizeof(cbuf)-1] = '\0';
    while (fgets (cbuf, sizeof(cbuf)-1, sim_log))
        tmxr_linemsgf (lp, "%s", cbuf);
    if (!tmxr_input_pending_ln (lp)) {
        do {
            unwritten = tmxr_send_buffered_data (lp);
            if (unwritten == lp->txbsz)
                sim_os_ms_sleep (100);
            } while (unwritten == lp->txbsz);
        }
    }
}

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;

strcpy (cbuf, sim_rem_command_buf);
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 */

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);
sim_ttrun ();                                   /* set console mode */
sim_cancel (&sim_rem_con_unit[1]);              /* force immediate activation of sim_rem_con_data_svc */
sim_activate (&sim_rem_con_unit[1], -1);
sim_switches = saved_switches;                  /* restore original switches */
}

/* Unit service for remote console data polling */

t_stat sim_rem_con_data_svc (UNIT *uptr)
{
int32 i, j, c = 0;
t_stat stat = SCPE_OK;
t_bool active_command = FALSE;
int32 steps = 0;
t_bool was_active_command = (sim_rem_cmd_active_line != -1);
t_bool got_command;
t_bool close_session = FALSE;
TMLN *lp;
char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], *argv[1] = {NULL};
CONST char *cptr;
CTAB *cmdp = NULL;
CTAB *basecmdp = NULL;
uint32 read_start_time = 0;

tmxr_poll_rx (&sim_rem_con_tmxr);                      /* poll input */
for (i=(was_active_command ? sim_rem_cmd_active_line : 0); 
     (i < sim_rem_con_tmxr.lines) && (!active_command); 
     i++) {
    t_bool master_session = (sim_rem_master_mode && (i == 0));

    lp = &sim_rem_con_tmxr.ldsc[i];
    if (!lp->conn)
        continue;
    if (master_session && !sim_rem_master_was_connected) {
        tmxr_linemsgf (lp, "\nMaster Mode Session\r\n");
        tmxr_send_buffered_data (lp);                   /* flush any buffered data */
        }
    sim_rem_master_was_connected |= master_session;     /* Remember if master ever connected */
    stat = SCPE_OK;
    if ((was_active_command) ||
        (master_session && !sim_rem_single_mode[i])) {
        if (was_active_command) {
            sim_rem_cmd_active_line = -1;               /* Done with active command */
            if (!sim_rem_active_command) {              /* STEP command? */
                stat = SCPE_STEP;
                _sim_rem_message ("STEP", stat);        /* produce a STEP complete message */
                }
            _sim_rem_log_out (lp);
            sim_rem_active_command = NULL;              /* Restart loop to process available input */
            was_active_command = FALSE;
            i = -1;
            continue;
            }
        else {
            sim_is_running = 0;
            sim_stop_timer_services ();
            for (j=0; j < sim_rem_con_tmxr.lines; j++) {
                TMLN *lpj = &sim_rem_con_tmxr.ldsc[j];
                if ((i == j) || (!lpj->conn))
                    continue;
                tmxr_linemsgf (lpj, "\nRemote Master Console(%s) Entering Commands\n", lp->ipad);
                tmxr_send_buffered_data (lpj);         /* flush any buffered data */
                }
            lp = &sim_rem_con_tmxr.ldsc[i];
            }
        }
    else {
        c = tmxr_getc_ln (lp);
        if (!(TMXR_VALID & c))
            continue;
        c = c & ~TMXR_VALID;
        if (sim_rem_single_mode[i]) {
            if (c == sim_int_char) {                    /* ^E (the interrupt character) must start continue mode console interaction */
                sim_rem_single_mode[i] = FALSE;         /* enter multi command mode */
                sim_is_running = 0;
                sim_stop_timer_services ();
                stat = SCPE_STOP;
                _sim_rem_message ("RUN", stat);
                _sim_rem_log_out (lp);
                for (j=0; j < sim_rem_con_tmxr.lines; j++) {
                    TMLN *lpj = &sim_rem_con_tmxr.ldsc[j];
                    if ((i == j) || (!lpj->conn))
                        continue;
                    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 && sim_rem_read_timeouts[i]) {
                    tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", sim_rem_read_timeouts[i]);
                    tmxr_linemsgf (lp, "\r\n");
                    tmxr_send_buffered_data (lp);       /* flush any buffered data */
                    }
                }
            else {
                if ((sim_rem_buf_ptr[i] == 0) &&        /* At beginning of input line */
                    ((c == '\n') ||                     /* Ignore bare LF between commands (Microsoft Telnet bug) */
                     (c == '\r')))                      /* Ignore empty commands */
                    continue;
                if ((c == '\004') || (c == '\032')) {   /* EOF character (^D or ^Z) ? */
                    tmxr_linemsgf (lp, "\r\nGoodbye\r\n");
                    tmxr_send_buffered_data (lp);       /* flush any buffered data */
                    tmxr_reset_ln (lp);
                    continue;
                    }
                if (sim_rem_buf_ptr[i] == 0) {
                    /* we just picked up the first character on a command line */
                    if (!master_session)
                        tmxr_linemsgf (lp, "\r\n%s", sim_prompt);
                    else
                        tmxr_linemsgf (lp, "\r\n%s", sim_is_running ? "SIM> " : "sim> ");
                    sim_debug (DBG_XMT, &sim_remote_console, "Prompt Written: %s\n", sim_is_running ? "SIM> " : "sim> ");
                    if (!tmxr_input_pending_ln (lp))
                        tmxr_send_buffered_data (lp);   /* flush any buffered data */
                    }
                }
            }
        }
    got_command = FALSE;
    while (1) {
        if (stat == SCPE_EXIT)
            return stat|SCPE_NOMESSAGE;
        if (!sim_rem_single_mode[i]) {
            read_start_time = sim_os_msec();
            if (master_session)
                tmxr_linemsg (lp, "sim> ");
            else
                tmxr_linemsg (lp, sim_prompt);
            tmxr_send_buffered_data (lp);               /* flush any buffered data */
            }
        do {
            if (!sim_rem_single_mode[i]) {
                c = tmxr_getc_ln (lp);
                if (!(TMXR_VALID & c)) {
                    tmxr_send_buffered_data (lp);       /* flush any buffered data */
                    if (!master_session && 
                        sim_rem_read_timeouts[i] &&
                        ((sim_os_msec() - read_start_time)/1000 >= sim_rem_read_timeouts[i])) {
                        while (sim_rem_buf_ptr[i] > 0) {/* Erase current input line */
                            tmxr_linemsg (lp, "\b \b");
                            --sim_rem_buf_ptr[i];
                            }
                        if (sim_rem_buf_ptr[i]+80 >= sim_rem_buf_size[i]) {
                            sim_rem_buf_size[i] += 1024;
                            sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]);
                            }
                        strcpy (sim_rem_buf[i], "CONTINUE         ! Automatic continue due to timeout");
                        tmxr_linemsgf (lp, "%s\n", sim_rem_buf[i]);
                        got_command = TRUE;
                        break;
                        }
                    sim_os_ms_sleep (50);
                    tmxr_poll_rx (&sim_rem_con_tmxr);   /* poll input */
                    if (!lp->conn) {                    /* if connection lost? */
                        sim_rem_single_mode[i] = TRUE;  /* No longer multi-command more */
                        break;                          /* done waiting */
                        }
                    continue;
                    }
                read_start_time = sim_os_msec();
                c = c & ~TMXR_VALID;
                }
            switch (c) {
                case 0:     /* no data */
                    break;
                case '\b':  /* Backspace */
                case 127:   /* Rubout */
                    if (sim_rem_buf_ptr[i] > 0) {
                        tmxr_linemsg (lp, "\b \b");
                        --sim_rem_buf_ptr[i];
                        }
                    break;
                case 27:   /* escape */
                case 21:   /* ^U */
                    while (sim_rem_buf_ptr[i] > 0) {
                        tmxr_linemsg (lp, "\b \b");
                        --sim_rem_buf_ptr[i];
                        }
                    break;
                case '\n':
                    if (sim_rem_buf_ptr[i] == 0)
                        break;
                case '\r':
                    tmxr_linemsg (lp, "\r\n");
                    if (sim_rem_buf_ptr[i]+1 >= sim_rem_buf_size[i]) {
                        sim_rem_buf_size[i] += 1024;
                        sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]);
                        }
                    sim_rem_buf[i][sim_rem_buf_ptr[i]++] = '\0';
                    sim_debug (DBG_RCV, &sim_remote_console, "Got Command (%d bytes still in buffer): %s\n", tmxr_input_pending_ln (lp), sim_rem_buf[i]);
                    got_command = TRUE;
                    break;
                case '\004': /* EOF (^D) */
                case '\032': /* EOF (^Z) */
                    while (sim_rem_buf_ptr[i] > 0) {    /* Erase current input line */
                        tmxr_linemsg (lp, "\b \b");
                        --sim_rem_buf_ptr[i];
                        }
                    if (!sim_rem_single_mode[i]) {
                        if (sim_rem_buf_ptr[i]+80 >= sim_rem_buf_size[i]) {
                            sim_rem_buf_size[i] += 1024;
                            sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]);
                            }
                        strcpy (sim_rem_buf[i], "CONTINUE         ! Automatic continue before close");
                        tmxr_linemsgf (lp, "%s\n", sim_rem_buf[i]);
                        got_command = TRUE;
                        }
                    close_session = TRUE;
                    break;
                default:
                    tmxr_putc_ln (lp, c);
                    if (sim_rem_buf_ptr[i]+2 >= sim_rem_buf_size[i]) {
                        sim_rem_buf_size[i] += 1024;
                        sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]);
                        }
                    sim_rem_buf[i][sim_rem_buf_ptr[i]++] = (char)c;
                    sim_rem_buf[i][sim_rem_buf_ptr[i]] = '\0';
                    if (((size_t)sim_rem_buf_ptr[i]) >= sizeof(cbuf))
                        got_command = TRUE;             /* command too long */
                    break;
                }
            c = 0;
            if ((!got_command) && (sim_rem_single_mode[i]) && (tmxr_input_pending_ln (lp))) {
                c = tmxr_getc_ln (lp);
                c = c & ~TMXR_VALID;
                }
            } while ((!got_command) && ((!sim_rem_single_mode[i]) || c));
        if (!tmxr_input_pending_ln (lp))
            tmxr_send_buffered_data (lp);               /* flush any buffered data */
        if ((sim_rem_single_mode[i]) && !got_command) {
            break;
            }
        sim_printf ("Remote Console Command from %s> %s\r\n", lp->ipad, sim_rem_buf[i]);
        got_command = FALSE;
        if (strlen(sim_rem_buf[i]) >= sizeof(cbuf)) {
            sim_printf ("\r\nLine too long. Ignored.  Continuing Simulator execution\r\n");
            tmxr_linemsgf (lp, "\nLine too long. Ignored.  Continuing Simulator execution\n");
            tmxr_send_buffered_data (lp);               /* try to flush any buffered data */
            break;
            }
        strcpy (cbuf, sim_rem_buf[i]);
        sim_rem_buf_ptr[i] = 0;
        sim_rem_buf[i][sim_rem_buf_ptr[i]] = '\0';
        while (isspace(cbuf[0]))
            memmove (cbuf, cbuf+1, strlen(cbuf+1)+1);   /* skip leading whitespace */
        if (cbuf[0] == '\0') {
            if (sim_rem_single_mode[i]) {
                sim_rem_single_mode[i] = FALSE;
                break;
                }
            else
                continue;
            }
        strcpy (sim_rem_command_buf, cbuf);
        sim_sub_args (cbuf, sizeof(cbuf), argv);
        cptr = cbuf;
        cptr = get_glyph (cptr, gbuf, 0);               /* get command glyph */
        sim_switches = 0;                               /* init switches */
        sim_rem_active_number = i;
        if (!sim_log) {                                 /* Not currently logging? */
            int32 save_quiet = sim_quiet;

            sim_quiet = 1;
            sprintf (sim_rem_con_temp_name, "sim_remote_console_%d.temporary_log", (int)getpid());
            sim_set_logon (0, sim_rem_con_temp_name);
            sim_quiet = save_quiet;
            sim_log_temp = TRUE;
            }
        sim_rem_cmd_log_start = sim_ftell (sim_log);
        basecmdp = find_cmd (gbuf);                     /* validate basic command */
        if (basecmdp == NULL) {
            if ((gbuf[0] == ';') || (gbuf[0] == '#')) { /* ignore comment */
                sim_rem_cmd_active_line = i;
                was_active_command = TRUE;
                sim_rem_active_command = &allowed_single_remote_cmds[0];/* Dummy */
                i = i - 1;
                break;
                }
            else
                stat = SCPE_UNK;
            }
        else {
            if ((cmdp = find_ctab (sim_rem_single_mode[i] ? allowed_single_remote_cmds : (master_session ? allowed_master_remote_cmds : allowed_remote_cmds), gbuf))) {/* lookup command */
                if (cmdp->action == &x_continue_cmd)
                    stat = SCPE_OK;
                else {
                    if (cmdp->action == &exit_cmd)
                        return SCPE_EXIT;
                    if (cmdp->action == &x_step_cmd) {
                        steps = 1;                      /* default of 1 instruction */
                        stat = SCPE_OK;
                        if (*cptr != 0) {               /* argument? */
                             cptr = get_glyph (cptr, gbuf, 0);/* get next glyph */
                             if (*cptr != 0)            /* should be end */
                                 stat = SCPE_2MARG;
                             else {
                                 steps = (int32) get_uint (gbuf, 10, INT_MAX, &stat);
                                 if ((stat != SCPE_OK) || (steps <= 0)) /* error? */
                                     stat = SCPE_ARG;
                                 }
                             }
                        if (stat != SCPE_OK)
                            cmdp = NULL;
                        }
                    else {
                        if (cmdp->action == &x_run_cmd) {
                            sim_switches |= SIM_SW_HIDE;/* Request Setup only */
                            stat = basecmdp->action (cmdp->arg, cptr);
                            sim_switches &= ~SIM_SW_HIDE;/* Done with Setup only mode */
                            if (stat == SCPE_OK) {
                                /* switch to CONTINUE after x_run_cmd() did RUN setup */
                                cmdp = find_ctab (allowed_master_remote_cmds, "CONTINUE");
                                }
                            }
                        else
                            stat = SCPE_REMOTE;         /* force processing outside of sim_instr() */
                        }
                    }
                }
            else
                stat = SCPE_INVREM;
            }
        sim_rem_active_number = -1;
        if ((stat != SCPE_OK) && (stat != SCPE_REMOTE))
            stat = _sim_rem_message (gbuf, stat);
        _sim_rem_log_out (lp);
        if (master_session && !sim_rem_master_mode) {
            sim_rem_single_mode[i] = TRUE;
            return SCPE_STOP;
            }
        if (cmdp && (cmdp->action == &x_continue_cmd)) {
            sim_rem_cmd_active_line = -1;               /* Not active_command */
            if (sim_log_temp &&                         /* If we setup a temporary log, clean it now  */
                (!sim_rem_master_mode)) {
                int32 save_quiet = sim_quiet;

                sim_quiet = 1;
                sim_set_logoff (0, NULL);
                sim_quiet = save_quiet;
                remove (sim_rem_con_temp_name);
                sim_log_temp = FALSE;
                }
            else {
                fflush (sim_log);
                sim_rem_cmd_log_start = sim_ftell (sim_log);
                }
            if (!sim_rem_single_mode[i]) {
                tmxr_linemsg (lp, "Simulator Running...");
                tmxr_send_buffered_data (lp);
                for (j=0; j < sim_rem_con_tmxr.lines; j++) {
                    TMLN *lpj = &sim_rem_con_tmxr.ldsc[j];
                    if ((i == j) || (!lpj->conn))
                        continue;
                    tmxr_linemsg (lpj, "Simulator Running...");
                    tmxr_send_buffered_data (lpj);
                    }
                sim_is_running = 1;
                sim_start_timer_services ();
                }
            if (cmdp && (cmdp->action == &x_continue_cmd))
                sim_rem_single_mode[i] = TRUE;
            else {
                if (!sim_rem_single_mode[i]) {
                    if (master_session)
                        tmxr_linemsgf (lp, "%s", "sim> ");
                    else
                        tmxr_linemsgf (lp, "%s", sim_prompt);
                    tmxr_send_buffered_data (lp);
                    }
                }
            break;
            }
        if ((cmdp && (cmdp->action == &x_step_cmd)) ||
            (stat == SCPE_REMOTE)) {
            sim_rem_cmd_active_line = i;
            break;
            }
        }
    if (close_session) {
        tmxr_linemsgf (lp, "\r\nGoodbye\r\n");
        tmxr_send_buffered_data (lp);                   /* flush any buffered data */
        tmxr_reset_ln (lp);
        sim_rem_single_mode[i] = FALSE;
        }
    }
if (sim_rem_master_was_connected &&                     /* Master mode ever connected? */
    !sim_rem_con_tmxr.ldsc[0].sock)                     /* Master Connection lost? */
    return SCPE_EXIT;                                   /* simulator has been 'unplugged' */
if (sim_rem_cmd_active_line != -1) {
    if (steps)
        sim_activate(uptr, steps);                      /* check again after 'steps' instructions */
    else
        return SCPE_REMOTE;                             /* force sim_instr() to exit to process command */
    }
else
    sim_activate_after(uptr, 100000);                   /* check again in 100 milliaeconds */
if (sim_rem_master_was_enabled && !sim_rem_master_mode) {/* Transitioning out of master mode? */
    lp = &sim_rem_con_tmxr.ldsc[0];
    tmxr_linemsgf (lp, "Non Master Mode Session...");   /* report transition */
    tmxr_send_buffered_data (lp);                       /* flush any buffered data */
    return SCPE_STOP|SCPE_NOMESSAGE;                    /* Unwind to the normal input path */
    }
else
    return SCPE_OK;                                     /* keep going */
}

t_stat sim_rem_con_reset (DEVICE *dptr)
{
if (sim_rem_con_tmxr.lines) {
    int32 i;

    for (i=0; i<sim_rem_con_tmxr.lines; i++)
        if (sim_rem_con_tmxr.ldsc[i].conn)
            break;
    if (i != sim_rem_con_tmxr.lines)
        sim_activate_after (&dptr->units[1], 100000);   /* continue polling for open sessions */
    return sim_rem_con_poll_svc (&dptr->units[0]);      /* establish polling as needed */
    }
return SCPE_OK;
}

static t_stat sim_set_rem_telnet (int32 flag, CONST char *cptr)
{
t_stat r;

if (flag) {
    r = sim_parse_addr (cptr, NULL, 0, NULL, NULL, 0, NULL, NULL);
    if (r == SCPE_OK) {
        if (sim_rem_con_tmxr.master)                    /* already open? */
            sim_set_rem_telnet (0, NULL);               /* close first */
        if (sim_rem_con_tmxr.lines == 0)                /* Ir no connection limit set */
            sim_set_rem_connections (0, "1");           /* use 1 */
        sim_rem_con_tmxr.buffered = 8192;               /* Use big enough buffers */
        sim_register_internal_device (&sim_remote_console);
        r = tmxr_attach (&sim_rem_con_tmxr, &sim_rem_con_unit[0], cptr);/* open master socket */
        if (r == SCPE_OK)
            sim_activate_after(&sim_rem_con_unit[0], 1000000); /* check for connection in 1 second */
        return r;
        }
    return SCPE_NOPARAM;
    }
else {
    if (sim_rem_con_tmxr.master) {
        int32 i;

        tmxr_detach (&sim_rem_con_tmxr, &sim_rem_con_unit[0]);
        for (i=0; i<sim_rem_con_tmxr.lines; i++) {
            free (sim_rem_buf[i]);
            sim_rem_buf[i] = NULL;
            sim_rem_buf_size[i] = 0;
            sim_rem_buf_ptr[i] = 0;
            sim_rem_single_mode[i] = TRUE;
            }
        }
    }
return SCPE_OK;
}

static t_stat sim_set_rem_connections (int32 flag, CONST char *cptr)
{
int32 lines;
t_stat r;
int32 i;

if (cptr == NULL)
    return SCPE_ARG;
lines = (int32) get_uint (cptr, 10, MAX_REMOTE_SESSIONS, &r);
if (r != SCPE_OK)
    return r;
if (sim_rem_con_tmxr.master)
    return SCPE_ARG;
for (i=0; i<sim_rem_con_tmxr.lines; i++)
    free (sim_rem_buf[i]);
sim_rem_con_tmxr.lines = lines;
sim_rem_con_tmxr.ldsc = (TMLN *)realloc (sim_rem_con_tmxr.ldsc, sizeof(*sim_rem_con_tmxr.ldsc)*lines);
memset (sim_rem_con_tmxr.ldsc, 0, sizeof(*sim_rem_con_tmxr.ldsc)*lines);
sim_rem_buf = (char **)realloc (sim_rem_buf, sizeof(*sim_rem_buf)*lines);
memset (sim_rem_buf, 0, sizeof(*sim_rem_buf)*lines);
sim_rem_buf_size = (int32 *)realloc (sim_rem_buf_size, sizeof(*sim_rem_buf_size)*lines);
memset (sim_rem_buf_size, 0, sizeof(*sim_rem_buf_size)*lines);
sim_rem_buf_ptr = (int32 *)realloc (sim_rem_buf_ptr, sizeof(*sim_rem_buf_ptr)*lines);
memset (sim_rem_buf_ptr, 0, sizeof(*sim_rem_buf_ptr)*lines);
sim_rem_single_mode = (t_bool *)realloc (sim_rem_single_mode, sizeof(*sim_rem_single_mode)*lines);
memset (sim_rem_single_mode, 0, sizeof(*sim_rem_single_mode)*lines);
sim_rem_read_timeouts = (uint32 *)realloc (sim_rem_read_timeouts, sizeof(*sim_rem_read_timeouts)*lines);
memset (sim_rem_read_timeouts, 0, sizeof(*sim_rem_read_timeouts)*lines);
sim_rem_command_buf = (char *)realloc (sim_rem_command_buf, 4*CBUFSIZE+1);
memset (sim_rem_command_buf, 0, 4*CBUFSIZE+1);
return SCPE_OK;
}

static t_stat sim_set_rem_timeout (int32 flag, CONST char *cptr)
{
int32 timeout;
t_stat r;

if (cptr == NULL)
    return SCPE_ARG;
timeout = (int32) get_uint (cptr, 10, 3600, &r);
if (r != SCPE_OK)
    return r;
if (sim_rem_active_number >= 0)
    sim_rem_read_timeouts[sim_rem_active_number] = timeout;
else
    sim_rem_read_timeout = timeout;
return SCPE_OK;
}

static t_stat sim_set_rem_bufsize (int32 flag, CONST char *cptr)
{
char cmdbuf[CBUFSIZE];
int32 bufsize;
t_stat r;

if (cptr == NULL)
    return SCPE_ARG;
bufsize = (int32) get_uint (cptr, 10, 32768, &r);
if (r != SCPE_OK)
    return r;
if (bufsize < 1400)
    return sim_messagef (SCPE_ARG, "%d is too small.  Minimum size is 1400\n", bufsize);
sprintf(cmdbuf, "BUFFERED=%d", bufsize);
return tmxr_open_master (&sim_rem_con_tmxr, cmdbuf);    /* open master socket */
}

/* Enable or disable Remote Console master mode */

/* In master mode, commands are subsequently processed from the
   primary/initial (master mode) remote console session.  Commands
   are processed from that source until that source disables master
   mode or the simulator exits 
 */

static t_stat sim_set_rem_master (int32 flag, CONST char *cptr)
{
t_stat stat = SCPE_OK;

if (cptr && *cptr)
    return SCPE_2MARG;

if (sim_rem_active_number > 0) {
    sim_printf ("Can't change Remote Console mode from Remote Console\n");
    return SCPE_INVREM;
    }

if (sim_rem_con_tmxr.master || (!flag))         /* Remote Console Enabled? */
    sim_rem_master_mode = flag;
else {
    sim_printf ("Can't enable Remote Console Master mode with Remote Console disabled\n");
    return SCPE_INVREM;
    }

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;
    while (sim_rem_master_mode) {
        sim_rem_single_mode[0] = FALSE;
        sim_cancel (&sim_rem_con_unit[1]);
        sim_activate (&sim_rem_con_unit[1], -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);
            }
        if (stat == SCPE_EXIT)
            sim_rem_master_mode = FALSE;
        }
    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;
        sim_set_logoff (0, NULL);
        sim_quiet = save_quiet;
        remove (sim_rem_con_temp_name);
        sim_log_temp = FALSE;
        }
    stat |= stat_nomessage;
    }
else {
    sim_rem_single_mode[0] = TRUE;                          /* Force remote session into single command mode */
    }

return stat;
}

/* Set keyboard map */

t_stat sim_set_kmap (int32 flag, CONST char *cptr)
{
DEVICE *dptr = sim_devices[0];
int32 val, rdx;
t_stat r;

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_2FARG;
if (dptr->dradix == 16) rdx = 16;
else rdx = 8;
val = (int32) get_uint (cptr, rdx, 0177, &r);
if ((r != SCPE_OK) ||
    ((val == 0) && (flag & KMAP_NZ)))
    return SCPE_ARG;
*(cons_kmap[flag & KMAP_MASK]) = val;
return SCPE_OK;
}

/* Show keyboard map */

t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
if (sim_devices[0]->dradix == 16)
    fprintf (st, "%s = %X\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK]));
else fprintf (st, "%s = %o\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK]));
return SCPE_OK;
}

/* Set printable characters */

t_stat sim_set_pchar (int32 flag, CONST char *cptr)
{
DEVICE *dptr = sim_devices[0];
uint32 val, rdx;
t_stat r;

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_2FARG;
if (dptr->dradix == 16) rdx = 16;
else rdx = 8;
val = (uint32) get_uint (cptr, rdx, 0xFFFFFFFF, &r);
if ((r != SCPE_OK) ||
    ((val & 0x00002400) == 0))
    return SCPE_ARG;
sim_tt_pchar = val;
return SCPE_OK;
}

/* Show printable characters */

t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
if (sim_devices[0]->dradix == 16)
    fprintf (st, "pchar mask = %X", sim_tt_pchar);
else fprintf (st, "pchar mask = %o", sim_tt_pchar);
if (sim_tt_pchar) {
    static const char *pchars[] = {"NUL(^@)", "SOH(^A)", "STX(^B)", "ETX(^C)", "EOT(^D)", "ENQ(^E)", "ACK(^F)", "BEL(^G)", 
                                   "BS(^H)" , "HT(^I)",  "LF(^J)",  "VT(^K)",  "FF(^L)",  "CR(^M)",  "SO(^N)",  "SI(^O)",
                                   "DLE(^P)", "DC1(^Q)", "DC2(^R)", "DC3(^S)", "DC4(^T)", "NAK(^U)", "SYN(^V)", "ETB(^W)",
                                   "CAN(^X)", "EM(^Y)",  "SUB(^Z)", "ESC",     "FS",      "GS",      "RS",      "US"};
    int i;
    t_bool found = FALSE;

    fprintf (st, " {");
    for (i=31; i>=0; i--)
        if (sim_tt_pchar & (1 << i)) {
            fprintf (st, "%s%s", found ? "," : "", pchars[i]);
            found = TRUE;
            }
    fprintf (st, "}");
    }
fprintf (st, "\n");
return SCPE_OK;
}

/* Set input speed (bps) */

t_stat sim_set_cons_speed (int32 flag, CONST char *cptr)
{
return tmxr_set_line_speed (&sim_con_ldsc, cptr);
}

t_stat sim_show_cons_speed (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
if (sim_con_ldsc.rxbps) {
    fprintf (st, "Speed = %d", sim_con_ldsc.rxbps);
    if (sim_con_ldsc.rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
        fprintf (st, "*%.0f", sim_con_ldsc.rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
    fprintf (st, " bps\n");
    }
return SCPE_OK;
}

/* Set log routine */

t_stat sim_set_logon (int32 flag, CONST char *cptr)
{
char gbuf[CBUFSIZE];
t_stat r;
time_t now;

if ((cptr == NULL) || (*cptr == 0))                     /* need arg */
    return SCPE_2FARG;
cptr = get_glyph_nc (cptr, gbuf, 0);                    /* get file name */
if (*cptr != 0)                                         /* now eol? */
    return SCPE_2MARG;
sim_set_logoff (0, NULL);                               /* close cur log */
r = sim_open_logfile (gbuf, FALSE, &sim_log, &sim_log_ref); /* open log */
if (r != SCPE_OK)                                       /* error? */
    return r;
if (!sim_quiet)
    printf ("Logging to file \"%s\"\n", 
             sim_logfile_name (sim_log, sim_log_ref));
fprintf (sim_log, "Logging to file \"%s\"\n", 
             sim_logfile_name (sim_log, sim_log_ref));  /* start of log */
time(&now);
fprintf (sim_log, "Logging to file \"%s\" at %s", sim_logfile_name (sim_log, sim_log_ref), ctime(&now));
return SCPE_OK;
}

/* Set nolog routine */

t_stat sim_set_logoff (int32 flag, CONST char *cptr)
{
if (cptr && (*cptr != 0))                               /* now eol? */
    return SCPE_2MARG;
if (sim_log == NULL)                                    /* no log? */
    return SCPE_OK;
if (!sim_quiet)
    printf ("Log file closed\n");
fprintf (sim_log, "Log file closed\n");
sim_close_logfile (&sim_log_ref);                       /* close log */
sim_log = NULL;
return SCPE_OK;
}

/* Show log status */

t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
if (cptr && (*cptr != 0))
    return SCPE_2MARG;
if (sim_log)
    fprintf (st, "Logging enabled to \"%s\"\n", 
                 sim_logfile_name (sim_log, sim_log_ref));
else fprintf (st, "Logging disabled\n");
return SCPE_OK;
}

/* Set debug routine */

t_stat sim_set_debon (int32 flag, CONST char *cptr)
{
char gbuf[CBUFSIZE];
t_stat r;
time_t now;

sim_deb_switches = sim_switches;                        /* save debug switches */
if ((cptr == NULL) || (*cptr == 0))                     /* need arg */
    return SCPE_2FARG;
cptr = get_glyph_nc (cptr, gbuf, 0);                    /* get file name */
if (*cptr != 0)                                         /* now eol? */
    return SCPE_2MARG;
r = sim_open_logfile (gbuf, FALSE, &sim_deb, &sim_deb_ref);

if (r != SCPE_OK)
    return r;

if (sim_deb_switches & SWMASK ('R')) {
    clock_gettime(CLOCK_REALTIME, &sim_deb_basetime);
    if (!(sim_deb_switches & (SWMASK ('A') | SWMASK ('T'))))
        sim_deb_switches |= SWMASK ('T');
    }
if (!sim_quiet) {
    sim_printf ("Debug output to \"%s\"\n", sim_logfile_name (sim_deb, sim_deb_ref));
    if (sim_deb_switches & SWMASK ('P'))
        sim_printf ("   Debug messages contain current PC value\n");
    if (sim_deb_switches & SWMASK ('T'))
        sim_printf ("   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'))
        sim_printf ("   Debug messages display time of day as seconds.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : "");
    time(&now);
    fprintf (sim_deb, "Debug output to \"%s\" at %s", sim_logfile_name (sim_deb, sim_deb_ref), ctime(&now));
    show_version (sim_deb, NULL, NULL, 0, NULL);
    }
if (sim_deb_switches & SWMASK ('N'))
    sim_deb_switches &= ~SWMASK ('N');          /* Only process the -N flag initially */

return SCPE_OK;
}

t_stat sim_debug_flush (void)
{
int32 saved_quiet = sim_quiet;
int32 saved_sim_switches = sim_switches;
int32 saved_deb_switches = sim_deb_switches;
struct timespec saved_deb_basetime = sim_deb_basetime;
char saved_debug_filename[CBUFSIZE];

if (sim_deb == NULL)                                    /* no debug? */
    return SCPE_OK;

if (sim_deb == sim_log) {                               /* debug is log */
    fflush (sim_deb);                                   /* fflush is the best we can do */
    return SCPE_OK;
    }

strcpy (saved_debug_filename, sim_logfile_name (sim_deb, sim_deb_ref));

sim_quiet = 1;
sim_set_deboff (0, NULL);
sim_switches = saved_deb_switches;
sim_set_debon (0, saved_debug_filename);
sim_deb_basetime = saved_deb_basetime;
sim_switches = saved_sim_switches;
sim_quiet = saved_quiet;
return SCPE_OK;
}

/* Set nodebug routine */

t_stat sim_set_deboff (int32 flag, CONST char *cptr)
{
if (cptr && (*cptr != 0))                               /* now eol? */
    return SCPE_2MARG;
if (sim_deb == NULL)                                    /* no debug? */
    return SCPE_OK;
sim_close_logfile (&sim_deb_ref);
sim_deb = NULL;
sim_deb_switches = 0;
if (!sim_quiet)
    sim_printf ("Debug output disabled\n");
return SCPE_OK;
}

/* Show debug routine */

t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
int32 i;

if (cptr && (*cptr != 0))
    return SCPE_2MARG;
if (sim_deb) {
    fprintf (st, "Debug output enabled to \"%s\"\n", 
                 sim_logfile_name (sim_deb, sim_deb_ref));
    if (sim_deb_switches & SWMASK ('P'))
        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->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->dctrl)) {
            fprintf (st, "Device: %-6s ", dptr->name);
            show_dev_debug (st, dptr, NULL, 0, NULL);
            }
        }
    }
else fprintf (st, "Debug output disabled\n");
return SCPE_OK;
}

/* SET CONSOLE command */

/* Set console to Telnet port (and parameters) */

t_stat sim_set_telnet (int32 flag, CONST char *cptr)
{
char *cvptr, gbuf[CBUFSIZE];
CTAB *ctptr;
t_stat r;

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_2FARG;
while (*cptr != 0) {                                    /* do all mods */
    cptr = get_glyph_nc (cptr, gbuf, ',');              /* get modifier */
    if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
        *cvptr++ = 0;
    get_glyph (gbuf, gbuf, 0);                          /* modifier to UC */
    if ((ctptr = find_ctab (set_con_telnet_tab, gbuf))) { /* match? */
        r = ctptr->action (ctptr->arg, cvptr);          /* do the rest */
        if (r != SCPE_OK)
            return r;
        }
    else {
        if (sim_con_tmxr.master)                        /* already open? */
            sim_set_notelnet (0, NULL);                 /* close first */
        r = tmxr_attach (&sim_con_tmxr, &sim_con_unit, gbuf);/* open master socket */
        if (r == SCPE_OK)
            sim_activate_after(&sim_con_unit, 1000000); /* check for connection in 1 second */
        else
            return r;
        }
    }
return SCPE_OK;
}

/* Close console Telnet port */

t_stat sim_set_notelnet (int32 flag, CONST char *cptr)
{
if (cptr && (*cptr != 0))                               /* too many arguments? */
    return SCPE_2MARG;
if (sim_con_tmxr.master == 0)                           /* ignore if already closed */
    return SCPE_OK;
return tmxr_close_master (&sim_con_tmxr);               /* close master socket */
}

/* Show console Telnet status */

t_stat sim_show_telnet (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr)
{
if (cptr && (*cptr != 0))
    return SCPE_2MARG;
if ((sim_con_tmxr.master == 0) && 
    (sim_con_ldsc.serport == 0))
    fprintf (st, "Connected to console window\n");
else {
    if (sim_con_ldsc.serport) {
        fprintf (st, "Connected to ");
        tmxr_fconns (st, &sim_con_ldsc, -1);
        }
    else 
        if (sim_con_ldsc.sock == 0)
            fprintf (st, "Listening on port %s\n", sim_con_tmxr.port);
        else {
            fprintf (st, "Listening on port %s, connection from %s\n",
                sim_con_tmxr.port, sim_con_ldsc.ipad);
            tmxr_fconns (st, &sim_con_ldsc, -1);
            }
    tmxr_fstats (st, &sim_con_ldsc, -1);
    }
return SCPE_OK;
}

/* Set console to Buffering  */

t_stat sim_set_cons_buff (int32 flg, CONST char *cptr)
{
char cmdbuf[CBUFSIZE];

sprintf(cmdbuf, "BUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : "");
return tmxr_open_master (&sim_con_tmxr, cmdbuf);      /* open master socket */
}

/* Set console to NoBuffering */

t_stat sim_set_cons_unbuff (int32 flg, CONST char *cptr)
{
char cmdbuf[CBUFSIZE];

sprintf(cmdbuf, "UNBUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : "");
return tmxr_open_master (&sim_con_tmxr, cmdbuf);      /* open master socket */
}

/* Set console to Logging */

t_stat sim_set_cons_log (int32 flg, CONST char *cptr)
{
char cmdbuf[CBUFSIZE];

sprintf(cmdbuf, "LOG%c%s", cptr ? '=' : '\0', cptr ? cptr : "");
return tmxr_open_master (&sim_con_tmxr, cmdbuf);      /* open master socket */
}

/* Set console to NoLogging */

t_stat sim_set_cons_nolog (int32 flg, CONST char *cptr)
{
char cmdbuf[CBUFSIZE];

sprintf(cmdbuf, "NOLOG%c%s", cptr ? '=' : '\0', cptr ? cptr : "");
return tmxr_open_master (&sim_con_tmxr, cmdbuf);      /* open master socket */
}

t_stat sim_show_cons_log (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr)
{
if (cptr && (*cptr != 0))
    return SCPE_2MARG;
if (sim_con_tmxr.ldsc->txlog)
    fprintf (st, "Log File being written to %s\n", sim_con_tmxr.ldsc->txlogname);
else
    fprintf (st, "No Logging\n");
return SCPE_OK;
}

t_stat sim_show_cons_buff (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr)
{
if (cptr && (*cptr != 0))
    return SCPE_2MARG;
if (!sim_con_tmxr.ldsc->txbfd)
    fprintf (st, "Unbuffered\n");
else
    fprintf (st, "Buffer Size = %d\n", sim_con_tmxr.ldsc->txbsz);
return SCPE_OK;
}

/* Set console Debug Mode */

t_stat sim_set_cons_debug (int32 flg, CONST char *cptr)
{
return set_dev_debug (&sim_con_telnet, &sim_con_unit, flg, cptr);
}

t_stat sim_show_cons_debug (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr)
{
if (cptr && (*cptr != 0))
    return SCPE_2MARG;
return show_dev_debug (st, &sim_con_telnet, &sim_con_unit, flag, cptr);
}

/* Set console to Serial port (and parameters) */

t_stat sim_set_serial (int32 flag, CONST char *cptr)
{
char *cvptr, gbuf[CBUFSIZE], ubuf[CBUFSIZE];
CTAB *ctptr;
t_stat r;

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_2FARG;
while (*cptr != 0) {                                    /* do all mods */
    cptr = get_glyph_nc (cptr, gbuf, ',');              /* get modifier */
    if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
        *cvptr++ = 0;
    get_glyph (gbuf, ubuf, 0);                          /* modifier to UC */
    if ((ctptr = find_ctab (set_con_serial_tab, ubuf))) { /* match? */
        r = ctptr->action (ctptr->arg, cvptr);          /* do the rest */
        if (r != SCPE_OK)
            return r;
        }
    else {
        SERHANDLE serport = sim_open_serial (gbuf, NULL, &r);
        if (serport != INVALID_HANDLE) {
            sim_close_serial (serport);
            if (r == SCPE_OK) {
                char cbuf[CBUFSIZE];
                if ((sim_con_tmxr.master) ||            /* already open? */
                    (sim_con_ldsc.serport))
                    sim_set_noserial (0, NULL);         /* close first */
                sprintf(cbuf, "Connect=%s", gbuf);
                r = tmxr_attach (&sim_con_tmxr, &sim_con_unit, cbuf);/* open master socket */
                sim_con_ldsc.rcve = 1;                  /* rcv enabled */
                if (r == SCPE_OK)
                    sim_activate_after(&sim_con_unit, 1000000); /* check for connection in 1 second */
                return r;
                }
            }
        return SCPE_ARG;
        }
    }
return SCPE_OK;
}

/* Close console Serial port */

t_stat sim_set_noserial (int32 flag, CONST char *cptr)
{
if (cptr && (*cptr != 0))                               /* too many arguments? */
    return SCPE_2MARG;
if (sim_con_ldsc.serport == 0)                          /* ignore if already closed */
    return SCPE_OK;
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)
{
return sim_exp_show (st, &sim_con_expect, cptr);
}

/* Log File Open/Close/Show Support */

/* Open log file */

t_stat sim_open_logfile (const char *filename, t_bool binary, FILE **pf, FILEREF **pref)
{
char gbuf[CBUFSIZE];
const char *tptr;

if ((filename == NULL) || (*filename == 0))             /* too few arguments? */
    return SCPE_2FARG;
tptr = get_glyph (filename, gbuf, 0);
if (*tptr != 0)                                         /* now eol? */
    return SCPE_2MARG;
sim_close_logfile (pref);
*pf = NULL;
if (strcmp (gbuf, "LOG") == 0) {                        /* output to log? */
    if (sim_log == NULL)                                /* any log? */
        return SCPE_ARG;
    *pf = sim_log;
    *pref = sim_log_ref;
    if (*pref)
        ++(*pref)->refcount;
    }
else if (strcmp (gbuf, "DEBUG") == 0) {                 /* output to debug? */
    if (sim_deb == NULL)                                /* any debug? */
        return SCPE_ARG;
    *pf = sim_deb;
    *pref = sim_deb_ref;
    if (*pref)
        ++(*pref)->refcount;
    }
else if (strcmp (gbuf, "STDOUT") == 0) {                /* output to stdout? */
    *pf = stdout;
    *pref = NULL;
    }
else if (strcmp (gbuf, "STDERR") == 0) {                /* output to stderr? */
    *pf = stderr;
    *pref = NULL;
    }
else {
    *pref = (FILEREF *)calloc (1, sizeof(**pref));
    if (!*pref)
        return SCPE_MEM;
    get_glyph_nc (filename, gbuf, 0);                   /* reparse */
    strncpy ((*pref)->name, gbuf, sizeof((*pref)->name)-1);
    if (sim_switches & SWMASK ('N'))                    /* if a new log file is requested */
        *pf = sim_fopen (gbuf, (binary ? "w+b" : "w+"));/*   then open an empty file */
    else                                                /* otherwise */
        *pf = sim_fopen (gbuf, (binary ? "a+b" : "a+"));/*   append to an existing file */
    if (*pf == NULL) {                                  /* error? */
        free (*pref);
        *pref = NULL;
        return SCPE_OPENERR;
        }
    setvbuf (*pf, NULL, _IOFBF, 65536);
    (*pref)->file = *pf;
    (*pref)->refcount = 1;                               /* need close */
    }
return SCPE_OK;
}

/* Close log file */

t_stat sim_close_logfile (FILEREF **pref)
{
if (NULL == *pref)
    return SCPE_OK;
(*pref)->refcount = (*pref)->refcount  - 1;
if ((*pref)->refcount > 0) {
    *pref = NULL;
    return SCPE_OK;
    }
fclose ((*pref)->file);
free (*pref);
*pref = NULL;
return SCPE_OK;
}

/* Show logfile support routine */

const char *sim_logfile_name (FILE *st, FILEREF *ref)
{
if (!st)
    return "";
if (st == stdout)
    return "STDOUT";
if (st == stderr)
    return "STDERR";
if (!ref)
    return "";
return ref->name;
}

/* Check connection before executing 
   (including a remote console which may be required in master mode) */

t_stat sim_check_console (int32 sec)
{
int32 c, trys = 0;

if (sim_rem_master_mode) {
    for (;trys < sec; ++trys) {
        sim_rem_con_poll_svc (&sim_rem_con_unit[0]);
        if (sim_rem_con_tmxr.ldsc[0].conn)
            break;
        if ((trys % 10) == 0) {                         /* Status every 10 sec */
            sim_printf ("Waiting for Remote Console connection\r\n");
            fflush (stdout);
            if (sim_log)                                /* log file? */
                fflush (sim_log);
            }
        sim_os_sleep (1);                               /* wait 1 second */
        }
    if ((sim_rem_con_tmxr.ldsc[0].conn) &&
        (!sim_con_ldsc.serport) &&
        (sim_con_tmxr.master == 0) &&
        (sim_con_console_port)) {
        tmxr_linemsgf (&sim_rem_con_tmxr.ldsc[0], "\r\nConsole port must be Telnet or Serial with Master Remote Console\r\n");
        tmxr_linemsgf (&sim_rem_con_tmxr.ldsc[0], "Goodbye\r\n");
        while (tmxr_send_buffered_data (&sim_rem_con_tmxr.ldsc[0]))
            sim_os_ms_sleep (100);
        sim_os_ms_sleep (100);
        tmxr_reset_ln (&sim_rem_con_tmxr.ldsc[0]);
        sim_printf ("Console port must be Telnet or Serial with Master Remote Console\r\n");
        return SCPE_EXIT;
        }
    }
if (trys == sec) {
    return SCPE_TTMO;                                   /* timed out */
    }
if (sim_con_ldsc.serport)
    if (tmxr_poll_conn (&sim_con_tmxr) >= 0) 
        sim_con_ldsc.rcve = 1;                          /* rcv enabled */
if ((sim_con_tmxr.master == 0) ||                       /* serial console or not Telnet? done */
    (sim_con_ldsc.serport))
    return SCPE_OK;
if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) {          /* connected or buffered ? */
    tmxr_poll_rx (&sim_con_tmxr);                       /* poll (check disconn) */
    if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) {      /* still connected? */
        if (!sim_con_ldsc.conn) {
            sim_printf ("Running with Buffered Console\r\n"); /* print transition */
            fflush (stdout);
            if (sim_log)                                /* log file? */
                fflush (sim_log);
            }
        return SCPE_OK;
        }
    }
for (; trys < sec; trys++) {                            /* loop */
    if (tmxr_poll_conn (&sim_con_tmxr) >= 0) {          /* poll connect */
        sim_con_ldsc.rcve = 1;                          /* rcv enabled */
        if (trys) {                                     /* if delayed */
            sim_printf ("Running\r\n");                 /* print transition */
            fflush (stdout);
            if (sim_log)                                /* log file? */
                fflush (sim_log);
            }
        return SCPE_OK;                                 /* ready to proceed */
        }
    c = sim_os_poll_kbd ();                             /* check for stop char */
    if ((c == SCPE_STOP) || stop_cpu)
        return SCPE_STOP;
    if ((trys % 10) == 0) {                             /* Status every 10 sec */
        sim_printf ("Waiting for console Telnet connection\r\n");
        fflush (stdout);
        if (sim_log)                                    /* log file? */
            fflush (sim_log);
        }
    sim_os_sleep (1);                                   /* wait 1 second */
    }
return SCPE_TTMO;                                       /* timed out */
}

/* Get Send object address for console */

SEND *sim_cons_get_send (void)
{
return &sim_con_send;
}

/* Get Expect object address for console */

EXPECT *sim_cons_get_expect (void)
{
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)
{
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 */
    c = sim_os_poll_kbd ();                                 /* get character */
    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? */
            sim_con_ldsc.rxnexttime =                       /* compute next input time */
                floor (sim_gtime () + ((sim_con_ldsc.rxdelta * sim_timer_inst_per_sec ())/sim_con_ldsc.rxbpsfactor));
        if (c)
            sim_debug (DBG_RCV, &sim_con_telnet, "sim_poll_kbd() returning: '%c' (0x%02X)\n", sim_isprint (c & 0xFF) ? c & 0xFF : '.', c);
        return c;                                           /* in-window */
        }
    if (!sim_con_ldsc.conn) {                               /* no telnet or serial connection? */
        if (!sim_con_ldsc.txbfd)                            /* unbuffered? */
            return SCPE_LOST;                               /* connection lost */
        if (tmxr_poll_conn (&sim_con_tmxr) >= 0)            /* poll connect */
            sim_con_ldsc.rcve = 1;                          /* rcv enabled */
        else                                                /* fall through to poll reception */
            return SCPE_OK;                                 /* unconnected and buffered - nothing to receive */
        }
    }
tmxr_poll_rx (&sim_con_tmxr);                               /* poll for input */
if ((c = (t_stat)tmxr_getc_ln (&sim_con_ldsc)))             /* any char? */ 
    return (c & (SCPE_BREAK | 0377)) | SCPE_KFLAG;
return SCPE_OK;
}

/* Output character */

t_stat sim_putchar (int32 c)
{
sim_exp_check (&sim_con_expect, c);
if ((sim_con_tmxr.master == 0) &&                       /* not Telnet? */
    (sim_con_ldsc.serport == 0)) {                      /* and not serial port */
    if (sim_log)                                        /* log file? */
        fputc (c, sim_log);
    sim_debug (DBG_XMT, &sim_con_telnet, "sim_putchar('%c' (0x%02X)\n", sim_isprint (c) ? c : '.', c);
    return sim_os_putchar (c);                          /* in-window version */
    }
if (!sim_con_ldsc.conn) {                               /* no Telnet or serial connection? */
    if (!sim_con_ldsc.txbfd)                            /* unbuffered? */
        return SCPE_LOST;                               /* connection lost */
    if (tmxr_poll_conn (&sim_con_tmxr) >= 0)            /* poll connect */
        sim_con_ldsc.rcve = 1;                          /* rcv enabled */
    }
tmxr_putc_ln (&sim_con_ldsc, c);                        /* output char */
tmxr_poll_tx (&sim_con_tmxr);                           /* poll xmt */
return SCPE_OK;
}

t_stat sim_putchar_s (int32 c)
{
t_stat r;

sim_exp_check (&sim_con_expect, c);
if ((sim_con_tmxr.master == 0) &&                       /* not Telnet? */
    (sim_con_ldsc.serport == 0)) {                      /* and not serial port */
    if (sim_log)                                        /* log file? */
        fputc (c, sim_log);
    sim_debug (DBG_XMT, &sim_con_telnet, "sim_putchar('%c' (0x%02X)\n", sim_isprint (c) ? c : '.', c);
    return sim_os_putchar (c);                          /* in-window version */
    }
if (!sim_con_ldsc.conn) {                               /* no Telnet or serial connection? */
    if (!sim_con_ldsc.txbfd)                            /* non-buffered Telnet connection? */
        return SCPE_LOST;                               /* lost */
    if (tmxr_poll_conn (&sim_con_tmxr) >= 0)            /* poll connect */
        sim_con_ldsc.rcve = 1;                          /* rcv enabled */
    }
if (sim_con_ldsc.xmte == 0)                             /* xmt disabled? */
    r = SCPE_STALL;
else r = tmxr_putc_ln (&sim_con_ldsc, c);               /* no, Telnet output */
tmxr_poll_tx (&sim_con_tmxr);                           /* poll xmt */
return r;                                               /* return status */
}

/* Input character processing */

int32 sim_tt_inpcvt (int32 c, uint32 mode)
{
uint32 md = mode & TTUF_M_MODE;

if (md != TTUF_MODE_8B) {
    uint32 par_mode = (mode >> TTUF_W_MODE) & TTUF_M_PAR;
    static int32 nibble_even_parity = 0x699600;   /* bit array indicating the even parity for each index (offset by 8) */

    c = c & 0177;
    if (md == TTUF_MODE_UC) {
        if (islower (c))
            c = toupper (c);
        if (mode & TTUF_KSR)
            c = c | 0200;
        }
    switch (par_mode) {
        case TTUF_PAR_EVEN:
            c |= (((nibble_even_parity >> ((c & 0xF) + 1)) ^ (nibble_even_parity >> (((c >> 4) & 0xF) + 1))) & 0x80);
            break;
        case TTUF_PAR_ODD:
            c |= ((~((nibble_even_parity >> ((c & 0xF) + 1)) ^ (nibble_even_parity >> (((c >> 4) & 0xF) + 1)))) & 0x80);
            break;
        case TTUF_PAR_MARK:
            c = c | 0x80;
            break;
        }
    }
else c = c & 0377;
return c;
}

/* Output character processing */

int32 sim_tt_outcvt (int32 c, uint32 mode)
{
uint32 md = mode & TTUF_M_MODE;

if (md != TTUF_MODE_8B) {
    c = c & 0177;
    if (md == TTUF_MODE_UC) {
        if (islower (c))
            c = toupper (c);
        if ((mode & TTUF_KSR) && (c >= 0140))
            return -1;
        }
    if (((md == TTUF_MODE_UC) || (md == TTUF_MODE_7P)) &&
        ((c == 0177) ||
         ((c < 040) && !((sim_tt_pchar >> c) & 1))))
        return -1;
    }
else c = c & 0377;
return c;
}

/* Tab stop array handling

   *desc points to a uint8 array of length val

   Columns with tabs set are non-zero; columns without tabs are 0 */

t_stat sim_tt_settabs (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
uint8 *temptabs, *tabs = (uint8 *) desc;
int32 i, d;
t_stat r;
char gbuf[CBUFSIZE];

if ((cptr == NULL) || (tabs == NULL) || (val <= 1))
    return SCPE_IERR;
if (*cptr == 0)
    return SCPE_2FARG;
if ((temptabs = (uint8 *)malloc (val)) == NULL)
    return SCPE_MEM;
for (i = 0; i < val; i++)
    temptabs[i] = 0;
do {
    cptr = get_glyph (cptr, gbuf, ';');
    d = (int32)get_uint (gbuf, 10, val, &r);
    if ((r != SCPE_OK) || (d == 0)) {
        free (temptabs);
        return SCPE_ARG;
        }
    temptabs[d - 1] = 1;
    } while (*cptr != 0);
for (i = 0; i < val; i++)
    tabs[i] = temptabs[i];
free (temptabs);
return SCPE_OK;
}

t_stat sim_tt_showtabs (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
const uint8 *tabs = (const uint8 *) desc;
int32 i, any;

if ((st == NULL) || (val == 0) || (desc == NULL))
    return SCPE_IERR;
for (i = any = 0; i < val; i++) {
    if (tabs[i] != 0) {
        fprintf (st, (any? ";%d": "%d"), i + 1);
        any = 1;
        }
    }
fprintf (st, (any? "\n": "no tabs set\n"));
return SCPE_OK;
}


#if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX)
extern pthread_mutex_t     sim_tmxr_poll_lock;
extern pthread_cond_t      sim_tmxr_poll_cond;
extern int32               sim_tmxr_poll_count;
extern t_bool              sim_tmxr_poll_running;
extern int32 sim_is_running;

pthread_t           sim_console_poll_thread;       /* Keyboard Polling Thread Id */
t_bool              sim_console_poll_running = FALSE;
pthread_cond_t      sim_console_startup_cond;

static void *
_console_poll(void *arg)
{
int wait_count = 0;
DEVICE *d;

/* Boost Priority for this I/O thread vs the CPU instruction execution 
   thread which, in general, won't be readily yielding the processor when 
   this thread needs to run */
sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);

sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - starting\n");

pthread_mutex_lock (&sim_tmxr_poll_lock);
pthread_cond_signal (&sim_console_startup_cond);   /* Signal we're ready to go */
while (sim_asynch_enabled) {

    if (!sim_is_running) {
        if (wait_count) {
            sim_debug (DBG_ASY, d, "_console_poll() - Removing interest in %s. Other interest: %d\n", d->name, sim_con_ldsc.uptr->a_poll_waiter_count);
            --sim_con_ldsc.uptr->a_poll_waiter_count;
            --sim_tmxr_poll_count;
            }
        break;
        }

    /* If we started something, let it finish before polling again */
    if (wait_count) {
        sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - waiting for %d units\n", wait_count);
        pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock);
        sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - continuing with after wait\n");
        }

    pthread_mutex_unlock (&sim_tmxr_poll_lock);
    wait_count = 0;
    if (sim_os_poll_kbd_ready (1000)) {
        sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - Keyboard Data available\n");
        pthread_mutex_lock (&sim_tmxr_poll_lock);
        ++wait_count;
        if (!sim_con_ldsc.uptr->a_polling_now) {
            sim_con_ldsc.uptr->a_polling_now = TRUE;
            sim_con_ldsc.uptr->a_poll_waiter_count = 1;
            d = find_dev_from_unit(sim_con_ldsc.uptr);
            sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - Activating %s\n", d->name);
            pthread_mutex_unlock (&sim_tmxr_poll_lock);
            _sim_activate (sim_con_ldsc.uptr, 0);
            pthread_mutex_lock (&sim_tmxr_poll_lock);
            }
        else {
            d = find_dev_from_unit(sim_con_ldsc.uptr);
            sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - Already Activated %s %d times\n", d->name, sim_con_ldsc.uptr->a_poll_waiter_count);
            ++sim_con_ldsc.uptr->a_poll_waiter_count;
            }
        }
    else
        pthread_mutex_lock (&sim_tmxr_poll_lock);

    sim_tmxr_poll_count += wait_count;
    }
pthread_mutex_unlock (&sim_tmxr_poll_lock);

sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - exiting\n");

return NULL;
}


#endif /* defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX) */


t_stat sim_ttinit (void)
{
sim_con_tmxr.ldsc->mp = &sim_con_tmxr;
sim_register_internal_device (&sim_con_telnet);
tmxr_startup ();
return sim_os_ttinit ();
}

t_stat sim_ttrun (void)
{
if (!sim_con_tmxr.ldsc->uptr) {                         /* If simulator didn't declare its input polling unit */
    sim_con_unit.dynflags &= ~UNIT_TM_POLL;             /* we can't poll asynchronously */
    sim_con_unit.dynflags |= TMUF_NOASYNCH;             /* disable asynchronous behavior */
    }
else {
#if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX)
    if (sim_asynch_enabled) {
        sim_con_tmxr.ldsc->uptr->dynflags |= UNIT_TM_POLL;/* flag console input device as a polling unit */
        sim_con_unit.dynflags |= UNIT_TM_POLL;         /* flag as polling unit */
        }
#endif
    }
#if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX)
pthread_mutex_lock (&sim_tmxr_poll_lock);
if (sim_asynch_enabled) {
    pthread_attr_t attr;

    pthread_cond_init (&sim_console_startup_cond, NULL);
    pthread_attr_init (&attr);
    pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
    pthread_create (&sim_console_poll_thread, &attr, _console_poll, NULL);
    pthread_attr_destroy( &attr);
    pthread_cond_wait (&sim_console_startup_cond, &sim_tmxr_poll_lock); /* Wait for thread to stabilize */
    pthread_cond_destroy (&sim_console_startup_cond);
    sim_console_poll_running = TRUE;
    }
pthread_mutex_unlock (&sim_tmxr_poll_lock);
#endif
tmxr_start_poll ();
return sim_os_ttrun ();
}

t_stat sim_ttcmd (void)
{
#if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX)
pthread_mutex_lock (&sim_tmxr_poll_lock);
if (sim_console_poll_running) {
    pthread_cond_signal (&sim_tmxr_poll_cond);
    pthread_mutex_unlock (&sim_tmxr_poll_lock);
    pthread_join (sim_console_poll_thread, NULL);
    sim_console_poll_running = FALSE;
    }
else
    pthread_mutex_unlock (&sim_tmxr_poll_lock);
#endif
tmxr_stop_poll ();
return sim_os_ttcmd ();
}

t_stat sim_ttclose (void)
{
tmxr_shutdown ();
return sim_os_ttclose ();
}

t_bool sim_ttisatty (void)
{
return sim_os_ttisatty ();
}


/* Platform specific routine definitions */

/* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */

#if defined (VMS)

#if defined(__VAX)
#define sys$assign SYS$ASSIGN
#define sys$qiow SYS$QIOW
#define sys$dassgn SYS$DASSGN
#endif

#include <descrip.h>
#include <ttdef.h>
#include <tt2def.h>
#include <iodef.h>
#include <ssdef.h>
#include <starlet.h>
#include <unistd.h>

#define EFN 0
uint32 tty_chan = 0;
int buffered_character = 0;

typedef struct {
    unsigned short sense_count;
    unsigned char sense_first_char;
    unsigned char sense_reserved;
    unsigned int stat;
    unsigned int stat2; } SENSE_BUF;

typedef struct {
    unsigned short status;
    unsigned short count;
    unsigned int dev_status; } IOSB;

SENSE_BUF cmd_mode = { 0 };
SENSE_BUF run_mode = { 0 };

static t_stat sim_os_ttinit (void)
{
unsigned int status;
IOSB iosb;
$DESCRIPTOR (terminal_device, "tt");

status = sys$assign (&terminal_device, &tty_chan, 0, 0);
if (status != SS$_NORMAL)
    return SCPE_TTIERR;
status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE, &iosb, 0, 0,
    &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
    return SCPE_TTIERR;
run_mode = cmd_mode;
run_mode.stat = cmd_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC);
run_mode.stat2 = cmd_mode.stat2 | TT2$M_PASTHRU;
return SCPE_OK;
}

static t_stat sim_os_ttrun (void)
{
unsigned int status;
IOSB iosb;

status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0,
    &run_mode, sizeof (run_mode), 0, 0, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
    return SCPE_TTIERR;
return SCPE_OK;
}

static t_stat sim_os_ttcmd (void)
{
unsigned int status;
IOSB iosb;

status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0,
    &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
    return SCPE_TTIERR;
return SCPE_OK;
}

static t_stat sim_os_ttclose (void)
{
sim_ttcmd ();
sys$dassgn (tty_chan);
return SCPE_OK;
}

static t_bool sim_os_ttisatty (void)
{
return isatty (fileno (stdin));
}

static t_stat sim_os_poll_kbd_data (void)
{
unsigned int status, term[2];
unsigned char buf[4];
IOSB iosb;
SENSE_BUF sense;

term[0] = 0; term[1] = 0;
status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb,
    0, 0, &sense, 8, 0, term, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
    return SCPE_TTIERR;
if (sense.sense_count == 0) return SCPE_OK;
term[0] = 0; term[1] = 0;
status = sys$qiow (EFN, tty_chan,
    IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO,
    &iosb, 0, 0, buf, 1, 0, term, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
    return SCPE_OK;
if (buf[0] == sim_int_char) return SCPE_STOP;
if (sim_brk_char && (buf[0] == sim_brk_char))
    return SCPE_BREAK;
return (buf[0] | SCPE_KFLAG);
}

static t_stat sim_os_poll_kbd (void)
{
t_stat response;

if (response = buffered_character) {
    buffered_character = 0;
    return response;
    }
return sim_os_poll_kbd_data ();
}

static t_bool sim_os_poll_kbd_ready (int ms_timeout)
{
unsigned int status, term[2];
unsigned char buf[4];
IOSB iosb;

term[0] = 0; term[1] = 0;
status = sys$qiow (EFN, tty_chan,
    IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO,
    &iosb, 0, 0, buf, 1, (ms_timeout+999)/1000, term, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
    return FALSE;
if (buf[0] == sim_int_char)
    buffered_character = SCPE_STOP;
else
    if (sim_brk_char && (buf[0] == sim_brk_char))
        buffered_character = SCPE_BREAK;
    else
        buffered_character = (buf[0] | SCPE_KFLAG);
return TRUE;
}


static t_stat sim_os_putchar (int32 out)
{
unsigned int status;
char c;
IOSB iosb;

c = out;
status = sys$qiow (EFN, tty_chan, IO$_WRITELBLK | IO$M_NOFORMAT,
    &iosb, 0, 0, &c, 1, 0, 0, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
    return SCPE_TTOERR;
return SCPE_OK;
}

/* Win32 routines */

#elif defined (_WIN32)

#include <fcntl.h>
#include <io.h>
#include <windows.h>
#define RAW_MODE 0
static HANDLE std_input;
static HANDLE std_output;
static DWORD saved_input_mode;
static DWORD saved_output_mode;
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
#endif
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif

/* Note: This routine catches all the potential events which some aspect 
         of the windows system can generate.  The CTRL_C_EVENT won't be 
         generated by a  user typing in a console session since that 
         session is in RAW mode.  In general, Ctrl-C on a simulator's
         console terminal is a useful character to be passed to the 
         simulator.  This code does nothing to disable or affect that. */

static BOOL WINAPI
ControlHandler(DWORD dwCtrlType)
    {
    DWORD Mode;
    extern void int_handler (int sig);

    switch (dwCtrlType)
        {
        case CTRL_BREAK_EVENT:      // Use CTRL-Break or CTRL-C to simulate 
        case CTRL_C_EVENT:          // SERVICE_CONTROL_STOP in debug mode
            int_handler(0);
            return TRUE;
        case CTRL_CLOSE_EVENT:      // Window is Closing
        case CTRL_LOGOFF_EVENT:     // User is logging off
            if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &Mode))
                return TRUE;        // Not our User, so ignore
        case CTRL_SHUTDOWN_EVENT:   // System is shutting down
            int_handler(0);
            return TRUE;
        }
    return FALSE;
    }

static t_stat sim_os_ttinit (void)
{
SetConsoleCtrlHandler( ControlHandler, TRUE );
std_input = GetStdHandle (STD_INPUT_HANDLE);
std_output = GetStdHandle (STD_OUTPUT_HANDLE);
if ((std_input) &&                                      /* Not Background process? */
    (std_input != INVALID_HANDLE_VALUE))
    GetConsoleMode (std_input, &saved_input_mode);      /* Save Input Mode */
if ((std_output) &&                                     /* Not Background process? */
    (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 ((std_input) &&                                      /* If Not Background process? */
    (std_input != INVALID_HANDLE_VALUE)) {
    if (!GetConsoleMode(std_input, &saved_input_mode))
        return SCPE_TTYERR;
    if ((!SetConsoleMode(std_input, ENABLE_VIRTUAL_TERMINAL_INPUT)) &&
        (!SetConsoleMode(std_input, RAW_MODE)))
        return SCPE_TTYERR;
    }
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 (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 ((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;
return SCPE_OK;
}

static t_stat sim_os_ttclose (void)
{
return SCPE_OK;
}

static t_bool sim_os_ttisatty (void)
{
DWORD Mode;

return (std_input) && (std_input != INVALID_HANDLE_VALUE) && GetConsoleMode (std_input, &Mode);
}

static t_stat sim_os_poll_kbd (void)
{
int c = -1;
DWORD nkbevents, nkbevent;
INPUT_RECORD rec;

sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_poll_kbd()\n");

if ((std_input == NULL) ||                              /* No keyboard for */
    (std_input == INVALID_HANDLE_VALUE))                /* background processes */
    return SCPE_OK;
if (!GetNumberOfConsoleInputEvents(std_input, &nkbevents))
    return SCPE_TTYERR;
while (c == -1) {
    if (0 == nkbevents)
        return SCPE_OK;
    if (!ReadConsoleInput(std_input, &rec, 1, &nkbevent))
        return SCPE_TTYERR;
    if (0 == nkbevent)
        return SCPE_OK;
    --nkbevents;
    if (rec.EventType == KEY_EVENT) {
        if (rec.Event.KeyEvent.bKeyDown) {
            if (0 == rec.Event.KeyEvent.uChar.UnicodeChar) {     /* Special Character/Keys? */
                if (rec.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) /* Pause/Break Key */
                    c = sim_brk_char | SCPE_BREAK;
                else
                    if (rec.Event.KeyEvent.wVirtualKeyCode == '2')  /* ^@ */
                        c = 0;                                      /* return NUL */
            } else
                c = rec.Event.KeyEvent.uChar.AsciiChar;
            }
      }
    }
if ((c & 0177) == sim_del_char)
    c = 0177;
if ((c & 0177) == sim_int_char)
    return SCPE_STOP;
if ((sim_brk_char && ((c & 0177) == sim_brk_char)) || (c & SCPE_BREAK))
    return SCPE_BREAK;
return c | SCPE_KFLAG;
}

static t_bool sim_os_poll_kbd_ready (int ms_timeout)
{
sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_poll_kbd_ready()\n");
if ((std_input == NULL) ||                              /* No keyboard for */
    (std_input == INVALID_HANDLE_VALUE)) {              /* background processes */
    Sleep (ms_timeout);
    return FALSE;
    }
return (WAIT_OBJECT_0 == WaitForSingleObject (std_input, ms_timeout));
}


#define BELL_CHAR           7       /* Bell Character */
#define BELL_INTERVAL_MS    500     /* No more than 2 Bell Characters Per Second */
#define ESC_CHAR            033     /* Escape Character */
#define CSI_CHAR            0233    /* Control Sequence Introducer */
#define NUL_CHAR            0000    /* NUL character */
#define ESC_HOLD_USEC_DELAY 8000    /* Escape hold interval */
#define ESC_HOLD_MAX        32      /* Maximum Escape hold buffer */

static uint8 out_buf[ESC_HOLD_MAX]; /* Buffered characters pending output */
static int32 out_ptr = 0;

static t_stat sim_out_hold_svc (UNIT *uptr)
{
DWORD unused;

WriteConsoleA(std_output, out_buf, out_ptr, &unused, NULL);
out_ptr = 0;
return SCPE_OK;
}

#define out_hold_unit sim_con_units[1]

static t_stat sim_os_putchar (int32 c)
{
DWORD unused;
uint32 now;
static uint32 last_bell_time;

if (c != 0177) {
    switch (c) {
        case BELL_CHAR:
            now = sim_os_msec ();
            if ((now - last_bell_time) > BELL_INTERVAL_MS) {
                WriteConsoleA(std_output, &c, 1, &unused, NULL);
                last_bell_time = now;
                }
            break;
        case NUL_CHAR:
            break;
        case CSI_CHAR:
        case ESC_CHAR:
            if (out_ptr) {
                WriteConsoleA(std_output, out_buf, out_ptr, &unused, NULL);
                out_ptr = 0;
                sim_cancel (&out_hold_unit);
                }
            out_buf[out_ptr++] = (uint8)c;
            sim_activate_after (&out_hold_unit, ESC_HOLD_USEC_DELAY);
            out_hold_unit.action = &sim_out_hold_svc;
            break;
        default:
            if (out_ptr) {
                if (out_ptr >= ESC_HOLD_MAX) {              /* Stop buffering if full */
                    WriteConsoleA(std_output, out_buf, out_ptr, &unused, NULL);
                    out_ptr = 0;
                    WriteConsoleA(std_output, &c, 1, &unused, NULL);
                    }
                else
                    out_buf[out_ptr++] = (uint8)c;
                }
            else
                WriteConsoleA(std_output, &c, 1, &unused, NULL);
        }
    }
return SCPE_OK;
}

/* OS/2 routines, from Bruce Ray and Holger Veit */

#elif defined (__OS2__)

#include <conio.h>

static t_stat sim_os_ttinit (void)
{
return SCPE_OK;
}

static t_stat sim_os_ttrun (void)
{
return SCPE_OK;
}

static t_stat sim_os_ttcmd (void)
{
return SCPE_OK;
}

static t_stat sim_os_ttclose (void)
{
return SCPE_OK;
}

static t_bool sim_os_ttisatty (void)
{
return 1;
}

static t_stat sim_os_poll_kbd (void)
{
int c;

#if defined (__EMX__)
switch (c = _read_kbd(0,0,0)) {                         /* EMX has _read_kbd */

    case -1:                                            /* no char*/
        return SCPE_OK;

    case 0:                                             /* char pending */
        c = _read_kbd(0,1,0);
        break;

    default:                                            /* got char */
        break;
        }
#else
if (!kbhit ())
    return SCPE_OK;
c = getch();
#endif
if ((c & 0177) == sim_del_char)
    c = 0177;
if ((c & 0177) == sim_int_char)
    return SCPE_STOP;
if (sim_brk_char && ((c & 0177) == sim_brk_char))
    return SCPE_BREAK;
return c | SCPE_KFLAG;
}

static t_bool sim_os_poll_kbd_ready (int ms_timeout)   /* Don't know how to do this on this platform */
{
sim_os_ms_sleep (MIN(20,ms_timeout));           /* Wait a little */
return TRUE;                                    /* force a poll */
}

static t_stat sim_os_putchar (int32 c)
{
if (c != 0177) {
#if defined (__EMX__)
    putchar (c);
#else
    putch (c);
#endif
    fflush (stdout);
    }
return SCPE_OK;
}

/* Metrowerks CodeWarrior Macintosh routines, from Louis Chretien and
   Peter Schorn */

#elif defined (__MWERKS__) && defined (macintosh)

#include <console.h>
#include <Mactypes.h>
#include <string.h>
#include <sioux.h>
#include <unistd.h>
#include <siouxglobals.h>
#include <Traps.h>
#include <LowMem.h>

/* function prototypes */

Boolean SIOUXIsAppWindow(WindowPtr window);
void SIOUXDoMenuChoice(long menuValue);
void SIOUXUpdateMenuItems(void);
void SIOUXUpdateScrollbar(void);
int ps_kbhit(void);
int ps_getch(void);

extern pSIOUXWin SIOUXTextWindow;
static CursHandle iBeamCursorH = NULL;                  /* contains the iBeamCursor */

static void updateCursor(void) {
    WindowPtr window;
    window = FrontWindow();
    if (SIOUXIsAppWindow(window)) {
        GrafPtr savePort;
        Point localMouse;
        GetPort(&savePort);
        SetPort(window);
#if TARGET_API_MAC_CARBON
        GetGlobalMouse(&localMouse);
#else
        localMouse = LMGetMouseLocation();
#endif
        GlobalToLocal(&localMouse);
        if (PtInRect(localMouse, &(*SIOUXTextWindow->edit)->viewRect) && iBeamCursorH) {
            SetCursor(*iBeamCursorH);
        }
        else {
            SetCursor(&qd.arrow);
        }
        TEIdle(SIOUXTextWindow->edit);
        SetPort(savePort);
    }
    else {
        SetCursor(&qd.arrow);
        TEIdle(SIOUXTextWindow->edit);
    }
    return;
}

int ps_kbhit(void) {
    EventRecord event;
    int c;
    updateCursor();
    SIOUXUpdateScrollbar();
    while (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask |
             highLevelEventMask | diskEvt, &event)) {
        SIOUXHandleOneEvent(&event);
    }
    if (SIOUXQuitting) {
        exit(1);
    }
    if (EventAvail(keyDownMask,&event)) {
        c = event.message&charCodeMask;
        if ((event.modifiers & cmdKey) && (c > 0x20)) {
            GetNextEvent(keyDownMask, &event);
            SIOUXHandleOneEvent(&event);
            if (SIOUXQuitting) {
                exit(1);
            }
            return false;
        }
        return true;
    }
    else {
        return false;
    }
}

int ps_getch(void) {
    int c;
    EventRecord event;
    fflush(stdout);
    updateCursor();
    while(!GetNextEvent(keyDownMask,&event)) {
        if (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask |
             highLevelEventMask | diskEvt, &event)) {
            SIOUXUpdateScrollbar();
            SIOUXHandleOneEvent(&event);
        }
    }
    if (SIOUXQuitting) {
        exit(1);
    }
    c = event.message&charCodeMask;
    if ((event.modifiers & cmdKey) && (c > 0x20)) {
        SIOUXUpdateMenuItems();
        SIOUXDoMenuChoice(MenuKey(c));
    }
    if (SIOUXQuitting) {
        exit(1);
    }
   return c;
}

/* Note that this only works if the call to sim_ttinit comes before any output to the console */

static t_stat sim_os_ttinit (void) {
    int i;
    /* this blank will later be replaced by the number of characters */
    char title[50] = " ";
    unsigned char ptitle[50];
    SIOUXSettings.autocloseonquit       = TRUE;
    SIOUXSettings.asktosaveonclose = FALSE;
    SIOUXSettings.showstatusline = FALSE;
    SIOUXSettings.columns = 80;
    SIOUXSettings.rows = 40;
    SIOUXSettings.toppixel = 42;
    SIOUXSettings.leftpixel     = 6;
    iBeamCursorH = GetCursor(iBeamCursor);
    strcat(title, sim_name);
    strcat(title, " Simulator");
    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;
}

static t_stat sim_os_ttrun (void)
{
return SCPE_OK;
}

static t_stat sim_os_ttcmd (void)
{
return SCPE_OK;
}

static t_stat sim_os_ttclose (void)
{
return SCPE_OK;
}

static t_bool sim_os_ttisatty (void)
{
return 1;
}

static t_stat sim_os_poll_kbd (void)
{
int c;

if (!ps_kbhit ())
    return SCPE_OK;
c = ps_getch();
if ((c & 0177) == sim_del_char)
    c = 0177;
if ((c & 0177) == sim_int_char) return SCPE_STOP;
if (sim_brk_char && ((c & 0177) == sim_brk_char))
    return SCPE_BREAK;
return c | SCPE_KFLAG;
}

static t_bool sim_os_poll_kbd_ready (int ms_timeout)   /* Don't know how to do this on this platform */
{
sim_os_ms_sleep (MIN(20,ms_timeout));           /* Wait a little */
return TRUE;                                    /* force a poll */
}

static t_stat sim_os_putchar (int32 c)
{
if (c != 0177) {
    putchar (c);
    fflush (stdout);
    }
return SCPE_OK;
}

/* BSD UNIX routines */

#elif defined (BSDTTY)

#include <sgtty.h>
#include <fcntl.h>
#include <unistd.h>

struct sgttyb cmdtty,runtty;                            /* V6/V7 stty data */
struct tchars cmdtchars,runtchars;                      /* V7 editing */
struct ltchars cmdltchars,runltchars;                   /* 4.2 BSD editing */
int cmdfl,runfl;                                        /* TTY flags */

static t_stat sim_os_ttinit (void)
{
cmdfl = fcntl (0, F_GETFL, 0);                          /* get old flags  and status */
runfl = cmdfl | FNDELAY;
if (ioctl (0, TIOCGETP, &cmdtty) < 0)
    return SCPE_TTIERR;
if (ioctl (0, TIOCGETC, &cmdtchars) < 0)
    return SCPE_TTIERR;
if (ioctl (0, TIOCGLTC, &cmdltchars) < 0)
    return SCPE_TTIERR;
runtty = cmdtty;                                        /* initial run state */
runtty.sg_flags = cmdtty.sg_flags & ~(ECHO|CRMOD) | CBREAK;
runtchars.t_intrc = sim_int_char;                       /* interrupt */
runtchars.t_quitc = 0xFF;                               /* no quit */
runtchars.t_startc = 0xFF;                              /* no host sync */
runtchars.t_stopc = 0xFF;
runtchars.t_eofc = 0xFF;
runtchars.t_brkc = 0xFF;
runltchars.t_suspc = 0xFF;                              /* no specials of any kind */
runltchars.t_dsuspc = 0xFF;
runltchars.t_rprntc = 0xFF;
runltchars.t_flushc = 0xFF;
runltchars.t_werasc = 0xFF;
runltchars.t_lnextc = 0xFF;
return SCPE_OK;                                         /* return success */
}

static t_stat sim_os_ttrun (void)
{
runtchars.t_intrc = sim_int_char;                       /* in case changed */
fcntl (0, F_SETFL, runfl);                              /* non-block mode */
if (ioctl (0, TIOCSETP, &runtty) < 0)
    return SCPE_TTIERR;
if (ioctl (0, TIOCSETC, &runtchars) < 0)
    return SCPE_TTIERR;
if (ioctl (0, TIOCSLTC, &runltchars) < 0)
    return SCPE_TTIERR;
sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL)l     /* lower priority */
return SCPE_OK;
}

static t_stat sim_os_ttcmd (void)
{
sim_os_set_thread_priority (PRIORITY_NORMAL);           /* restore priority */
fcntl (0, F_SETFL, cmdfl);                              /* block mode */
if (ioctl (0, TIOCSETP, &cmdtty) < 0)
    return SCPE_TTIERR;
if (ioctl (0, TIOCSETC, &cmdtchars) < 0)
    return SCPE_TTIERR;
if (ioctl (0, TIOCSLTC, &cmdltchars) < 0)
    return SCPE_TTIERR;
return SCPE_OK;
}

static t_stat sim_os_ttclose (void)
{
return sim_ttcmd ();
}

static t_bool sim_os_ttisatty (void)
{
return isatty (fileno (stdin));
}

static t_stat sim_os_poll_kbd (void)
{
int status;
unsigned char buf[1];

status = read (0, buf, 1);
if (status != 1) return SCPE_OK;
if (sim_brk_char && (buf[0] == sim_brk_char))
    return SCPE_BREAK;
if (sim_int_char && (buf[0] == sim_int_char))
    return SCPE_STOP;
return (buf[0] | SCPE_KFLAG);
}

static t_bool sim_os_poll_kbd_ready (int ms_timeout)
{
fd_set readfds;
struct timeval timeout;

if (!isatty (0)) {                           /* 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;
return (1 == select (1, &readfds, NULL, NULL, &timeout));
}

static t_stat sim_os_putchar (int32 out)
{
char c;

c = out;
write (1, &c, 1);
return SCPE_OK;
}

/* POSIX UNIX routines, from Leendert Van Doorn */

#else

#include <termios.h>
#include <unistd.h>

struct termios cmdtty, runtty;

static t_stat sim_os_ttinit (void)
{
if (!isatty (fileno (stdin)))                           /* skip if !tty */
    return SCPE_OK;
if (tcgetattr (0, &cmdtty) < 0)                         /* get old flags */
    return SCPE_TTIERR;
runtty = cmdtty;
runtty.c_lflag = runtty.c_lflag & ~(ECHO | ICANON);     /* no echo or edit */
runtty.c_oflag = runtty.c_oflag & ~OPOST;               /* no output edit */
runtty.c_iflag = runtty.c_iflag & ~ICRNL;               /* no cr conversion */
#if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL)
runtty.c_cc[VINTR] = 0;                                 /* OS X doesn't deliver SIGINT to main thread when enabled */
#else
runtty.c_cc[VINTR] = sim_int_char;                      /* interrupt */
#endif
runtty.c_cc[VQUIT] = 0;                                 /* no quit */
runtty.c_cc[VERASE] = 0;
runtty.c_cc[VKILL] = 0;
runtty.c_cc[VEOF] = 0;
runtty.c_cc[VEOL] = 0;
runtty.c_cc[VSTART] = 0;                                /* no host sync */
runtty.c_cc[VSUSP] = 0;
runtty.c_cc[VSTOP] = 0;
#if defined (VREPRINT)
runtty.c_cc[VREPRINT] = 0;                              /* no specials */
#endif
#if defined (VDISCARD)
runtty.c_cc[VDISCARD] = 0;
#endif
#if defined (VWERASE)
runtty.c_cc[VWERASE] = 0;
#endif
#if defined (VLNEXT)
runtty.c_cc[VLNEXT] = 0;
#endif
runtty.c_cc[VMIN] = 0;                                  /* no waiting */
runtty.c_cc[VTIME] = 0;
#if defined (VDSUSP)
runtty.c_cc[VDSUSP] = 0;
#endif
#if defined (VSTATUS)
runtty.c_cc[VSTATUS] = 0;
#endif
return SCPE_OK;
}

static t_stat sim_os_ttrun (void)
{
if (!isatty (fileno (stdin)))                           /* skip if !tty */
    return SCPE_OK;
#if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL)
runtty.c_cc[VINTR] = 0;                                 /* OS X doesn't deliver SIGINT to main thread when enabled */
#else
runtty.c_cc[VINTR] = sim_int_char;                      /* in case changed */
#endif
if (tcsetattr (0, TCSAFLUSH, &runtty) < 0)
    return SCPE_TTIERR;
sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL);     /* try to lower pri */
return SCPE_OK;
}

static t_stat sim_os_ttcmd (void)
{
if (!isatty (fileno (stdin)))                           /* skip if !tty */
    return SCPE_OK;
sim_os_set_thread_priority (PRIORITY_NORMAL);           /* try to raise pri */
if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0)
    return SCPE_TTIERR;
return SCPE_OK;
}

static t_stat sim_os_ttclose (void)
{
return sim_ttcmd ();
}

static t_bool sim_os_ttisatty (void)
{
return isatty (fileno (stdin));
}

static t_stat sim_os_poll_kbd (void)
{
int status;
unsigned char buf[1];

status = read (0, buf, 1);
if (status != 1) return SCPE_OK;
if (sim_brk_char && (buf[0] == sim_brk_char))
    return SCPE_BREAK;
if (sim_int_char && (buf[0] == sim_int_char))
    return SCPE_STOP;
return (buf[0] | SCPE_KFLAG);
}

static t_bool sim_os_poll_kbd_ready (int ms_timeout)
{
fd_set readfds;
struct timeval timeout;

if (!sim_os_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;
return (1 == select (1, &readfds, NULL, NULL, &timeout));
}

static t_stat sim_os_putchar (int32 out)
{
char c;

c = out;
(void)write (1, &c, 1);
return SCPE_OK;
}

#endif

/* Decode a string.

   A string containing encoded control characters is decoded into the equivalent
   character string.  Escape targets @, A-Z, and [\]^_ form control characters
   000-037.
*/
#define ESCAPE_CHAR '~'

static void decode (char *decoded, const char *encoded)
{
char c;

while ((c = *decoded++ = *encoded++))                   /* copy the character */
    if (c == ESCAPE_CHAR) {                             /* does it start an escape? */
        if ((isalpha (*encoded)) ||                     /* is next character "A-Z" or "a-z"? */
            (*encoded == '@') ||                        /*   or "@"? */
            ((*encoded >= '[') && (*encoded <= '_')))   /*   or "[\]^_"? */

            *(decoded - 1) = *encoded++ & 037;          /* convert back to control character */
        else {
            if ((*encoded == '\0') ||                   /* single escape character at EOL? */
                 (*encoded++ != ESCAPE_CHAR))           /*   or not followed by another escape? */
                decoded--;                              /* drop the encoding */
            }
        }
return;
}

/* Set console halt */

static t_stat sim_set_halt (int32 flag, CONST char *cptr)
{
if (flag == 0)                                              /* no halt? */
    sim_exp_clrall (&sim_con_expect);                       /* disable halt checks */
else {
    char *mbuf;
    char *mbuf2;

    if (cptr == NULL || *cptr == 0)                         /* no match string? */
        return SCPE_2FARG;                                  /* need an argument */

    sim_exp_clrall (&sim_con_expect);                       /* make sure that none currently exist */

    mbuf = (char *)malloc (1 + strlen (cptr));
    decode (mbuf, cptr);                                    /* save decoded match string */

    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_exp_set (&sim_con_expect, mbuf, 0, sim_con_expect.after, EXP_TYP_PERSIST, NULL);
    free (mbuf);
    free (mbuf2);
    }

return SCPE_OK;
}


/* Set console response */

static t_stat sim_set_response (int32 flag, CONST char *cptr)
{
if (flag == 0)                                          /* no response? */
    sim_send_clear (&sim_con_send);
else {
    uint8 *rbuf;

    if (cptr == NULL || *cptr == 0)
        return SCPE_2FARG;                              /* need arg */

    rbuf = (uint8 *)malloc (1 + strlen(cptr));

    decode ((char *)rbuf, cptr);                        /* decod 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? */
    sim_con_expect.after = val;                         /* save the delay value */

return r;
}
Added src/sim_console.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_console.h: simulator console I/O library headers

   Copyright (c) 1993-2014, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   02-Jan-14    RMS     Added tab stop routines
   17-Jan-11    MP      Added buffered line capabilities
   22-Jun-06    RMS     Implemented SET/SHOW PCHAR
   22-Nov-05    RMS     Added central input/output conversion support
   05-Nov-04    RMS     Moved SET/SHOW DEBUG under CONSOLE hierarchy
   28-May-04    RMS     Added SET/SHOW CONSOLE
   02-Jan-04    RMS     Removed timer routines, added Telnet console routines
*/

#ifndef SIM_CONSOLE_H_
#define SIM_CONSOLE_H_ 0

#ifdef  __cplusplus
extern "C" {
#endif

#define TTUF_V_MODE     (UNIT_V_UF + 0)
#define TTUF_W_MODE     2
#define  TTUF_MODE_7B   0
#define  TTUF_MODE_8B   1
#define  TTUF_MODE_UC   2
#define  TTUF_MODE_7P   3
#define TTUF_M_MODE     ((1u << TTUF_W_MODE) - 1)
#define TTUF_V_PAR      (TTUF_V_MODE + TTUF_W_MODE)
#define TTUF_W_PAR      2
#define  TTUF_PAR_SPACE 0
#define  TTUF_PAR_MARK  1
#define  TTUF_PAR_EVEN  2
#define  TTUF_PAR_ODD   3
#define TTUF_M_PAR      ((1u << TTUF_W_PAR) - 1)
#define  TTUF_KSR       (1u << (TTUF_W_MODE + TTUF_W_PAR))
#define TTUF_V_UF       (TTUF_V_MODE + TTUF_W_MODE + TTUF_W_PAR)
#define TT_MODE         (TTUF_M_MODE << TTUF_V_MODE)
#define  TT_MODE_7B     (TTUF_MODE_7B << TTUF_V_MODE)
#define  TT_MODE_8B     (TTUF_MODE_8B << TTUF_V_MODE)
#define  TT_MODE_UC     (TTUF_MODE_UC << TTUF_V_MODE)
#define  TT_MODE_7P     (TTUF_MODE_7P << TTUF_V_MODE)
#define  TT_MODE_KSR    (TT_MODE_UC)
/* 7 bit modes allow for an 8th bit parity mode */
#define TT_PAR          (TTUF_M_PAR << TTUF_V_PAR)
#define  TT_PAR_SPACE   (TTUF_PAR_SPACE << TTUF_V_PAR)
#define  TT_PAR_MARK    (TTUF_PAR_MARK  << TTUF_V_PAR)
#define  TT_PAR_EVEN    (TTUF_PAR_EVEN  << TTUF_V_PAR)
#define  TT_PAR_ODD     (TTUF_PAR_ODD   << TTUF_V_PAR)
/* TT_GET_MODE returns both the TT_MODE and TT_PAR fields 
   since they together are passed into sim_tt_inpcvt() */
#define TT_GET_MODE(x)  (((x) >> TTUF_V_MODE) & (TTUF_M_MODE | (TTUF_M_PAR << TTUF_W_MODE)))

t_stat sim_set_console (int32 flag, CONST char *cptr);
t_stat sim_set_remote_console (int32 flag, CONST char *cptr);
void sim_remote_process_command (void);
t_stat sim_set_kmap (int32 flag, CONST char *cptr);
t_stat sim_set_telnet (int32 flag, CONST char *cptr);
t_stat sim_set_notelnet (int32 flag, CONST char *cptr);
t_stat sim_set_serial (int32 flag, CONST char *cptr);
t_stat sim_set_noserial (int32 flag, CONST char *cptr);
t_stat sim_set_logon (int32 flag, CONST char *cptr);
t_stat sim_set_logoff (int32 flag, CONST char *cptr);
t_stat sim_set_debon (int32 flag, CONST char *cptr);
t_stat sim_set_cons_debug (int32 flg, CONST char *cptr);
t_stat sim_set_cons_buff (int32 flg, CONST char *cptr);
t_stat sim_set_cons_unbuff (int32 flg, CONST char *cptr);
t_stat sim_set_cons_log (int32 flg, CONST char *cptr);
t_stat sim_set_cons_nolog (int32 flg, CONST char *cptr);
t_stat sim_set_deboff (int32 flag, CONST char *cptr);
t_stat sim_set_cons_expect (int32 flg, CONST char *cptr);
t_stat sim_set_cons_noexpect (int32 flg, CONST char *cptr);
t_stat sim_debug_flush (void);
t_stat sim_set_pchar (int32 flag, CONST char *cptr);
t_stat sim_set_cons_speed (int32 flag, CONST char *cptr);
t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_remote_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_telnet (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_cons_speed (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_cons_buff (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_cons_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_cons_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_show_cons_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_check_console (int32 sec);
t_stat sim_open_logfile (const char *filename, t_bool binary, FILE **pf, FILEREF **pref);
t_stat sim_close_logfile (FILEREF **pref);
const char *sim_logfile_name (FILE *st, FILEREF *ref);
SEND *sim_cons_get_send (void);
EXPECT *sim_cons_get_expect (void);
t_stat sim_show_cons_send_input (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_stat sim_set_noconsole_port (void);
t_stat sim_poll_kbd (void);
t_stat sim_putchar (int32 c);
t_stat sim_putchar_s (int32 c);
t_stat sim_ttinit (void);
t_stat sim_ttrun (void);
t_stat sim_ttcmd (void);
t_stat sim_ttclose (void);
t_bool sim_ttisatty (void);
int32 sim_tt_inpcvt (int32 c, uint32 mode);
int32 sim_tt_outcvt (int32 c, uint32 mode);
t_stat sim_tt_settabs (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat sim_tt_showtabs (FILE *st, UNIT *uptr, int32 val, CONST void *desc);

extern int32 sim_rem_cmd_active_line;                       /* command in progress on line # */

extern int32 sim_int_char;                                  /* interrupt character */
extern int32 sim_brk_char;                                  /* break character */
extern int32 sim_tt_pchar;                                  /* printable character mask */
extern int32 sim_del_char;                                  /* delete character */

#ifdef  __cplusplus
}
#endif

#endif
Added src/sim_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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_defs.h: simulator definitions

   Copyright (c) 1993-2008, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   05-Jan-11    MP      Added Asynch I/O support
   18-Jan-11    MP      Added log file reference count support
   21-Jul-08    RMS     Removed inlining support
   28-May-08    RMS     Added inlining support
   28-Jun-07    RMS     Added IA64 VMS support (from Norm Lastovica)
   18-Jun-07    RMS     Added UNIT_IDLE flag
   18-Mar-07    RMS     Added UNIT_TEXT flag
   07-Mar-07    JDB     Added DEBUG_PRJ macro
   18-Oct-06    RMS     Added limit check for clock synchronized keyboard waits
   13-Jul-06    RMS     Guarantee CBUFSIZE is at least 256
   07-Jan-06    RMS     Added support for breakpoint spaces
                        Added REG_FIT flag
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   11-Mar-05    RMS     Moved 64b data type definitions outside USE_INT64
   07-Feb-05    RMS     Added assertion fail stop
   05-Nov-04    RMS     Added support for SHOW opt=val
   20-Oct-04    RMS     Converted all base types to typedefs
   21-Sep-04    RMS     Added switch to flag stop message printout
   06-Feb-04    RMS     Moved device and unit user flags fields (V3.2)
                RMS     Added REG_VMAD
   29-Dec-03    RMS     Added output stall status
   15-Jun-03    RMS     Added register flag REG_VMIO
   23-Apr-03    RMS     Revised for 32b/64b t_addr
   14-Mar-03    RMS     Lengthened default serial output wait
   31-Mar-03    RMS     Added u5, u6 fields
   18-Mar-03    RMS     Added logical name support
                        Moved magtape definitions to sim_tape.h
                        Moved breakpoint definitions from scp.c
   03-Mar-03    RMS     Added sim_fsize
   08-Feb-03    RMS     Changed sim_os_sleep to void, added match_ext
   05-Jan-03    RMS     Added hidden switch definitions, device dyn memory support,
                        parameters for function pointers, case sensitive SET support
   22-Dec-02    RMS     Added break flag
   08-Oct-02    RMS     Increased simulator error code space
                        Added Telnet errors
                        Added end of medium support
                        Added help messages to CTAB
                        Added flag and context fields to DEVICE
                        Added restore flag masks
                        Revised 64b definitions
   02-May-02    RMS     Removed log status codes
   22-Apr-02    RMS     Added magtape record length error
   30-Dec-01    RMS     Generalized timer package, added circular arrays
   07-Dec-01    RMS     Added breakpoint package
   01-Dec-01    RMS     Added read-only unit support, extended SET/SHOW features,
                        improved error messages
   24-Nov-01    RMS     Added unit-based registers
   27-Sep-01    RMS     Added queue count prototype
   17-Sep-01    RMS     Removed multiple console support
   07-Sep-01    RMS     Removed conditional externs on function prototypes
   31-Aug-01    RMS     Changed int64 to t_int64 for Windoze
   17-Jul-01    RMS     Added additional function prototypes
   27-May-01    RMS     Added multiple console support
   15-May-01    RMS     Increased string buffer size
   25-Feb-01    RMS     Revisions for V2.6
   15-Oct-00    RMS     Editorial revisions for V2.5
   11-Jul-99    RMS     Added unsigned int data types
   14-Apr-99    RMS     Converted t_addr to unsigned
   04-Oct-98    RMS     Additional definitions for V2.4

   The interface between the simulator control package (SCP) and the
   simulator consists of the following routines and data structures

        sim_name                simulator name string
        sim_devices[]           array of pointers to simulated devices
        sim_PC                  pointer to saved PC register descriptor
        sim_interval            simulator interval to next event
        sim_stop_messages[]     array of pointers to stop messages
        sim_instr()             instruction execution routine
        sim_load()              binary loader routine
        sim_emax                maximum number of words in an instruction

   In addition, the simulator must supply routines to print and parse
   architecture specific formats

        print_sym               print symbolic output
        parse_sym               parse symbolic input
*/

#ifndef SIM_DEFS_H_
#define SIM_DEFS_H_    0

#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
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <limits.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>
#endif

#ifdef USE_REGEX
#undef USE_REGEX
#endif
#if defined(HAVE_PCREPOSIX_H)
#include <pcreposix.h>
#define USE_REGEX 1
#elif defined(HAVE_REGEX_H)
#include <regex.h>
#define USE_REGEX 1
#endif

#ifdef  __cplusplus
extern "C" {
#endif

/* avoid macro names collisions */
#ifdef MAX
#undef MAX
#endif
#ifdef MIN
#undef MIN
#endif
#ifdef PMASK
#undef PMASK
#endif
#ifdef RS
#undef RS
#endif
#ifdef PAGESIZE
#undef PAGESIZE
#endif


#ifndef TRUE
#define TRUE            1
#define FALSE           0
#endif

/* SCP API shim.

   The SCP API for version 4.0 introduces a number of "pointer-to-const"
   parameter qualifiers that were not present in the 3.x versions.  To maintain
   compatibility with the earlier versions, the new qualifiers are expressed as
   "CONST" rather than "const".  This allows macro removal of the qualifiers
   when compiling for SIMH 3.x.
*/
#ifndef CONST
#define CONST const
#endif

/* Length specific integer declarations */

#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
typedef int             t_stat;                         /* status */
typedef int             t_bool;                         /* boolean */

/* 64b integers */

#if defined (__GNUC__)                                  /* GCC */
typedef signed long long        t_int64;
typedef unsigned long long      t_uint64;
#elif defined (_WIN32)                                  /* Windows */
typedef signed __int64          t_int64;
typedef unsigned __int64        t_uint64;
#elif (defined (__ALPHA) || defined (__ia64)) && defined (VMS) /* 64b VMS */
typedef signed __int64          t_int64;
typedef unsigned __int64        t_uint64;
#elif defined (__ALPHA) && defined (__unix__)           /* Alpha UNIX */
typedef signed long             t_int64;
typedef unsigned long           t_uint64;
#else                                                   /* default */
#define t_int64                 signed long long
#define t_uint64                unsigned long long
#endif                                                  /* end 64b */
#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 */
#else                                                   /* 32b data */
typedef int32           t_svalue;
typedef uint32          t_value;
#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 */
typedef uint32          t_addr;
#define T_ADDR_W        32
#define T_ADDR_FMT      ""
#endif                                                  /* end 64b address */

#if defined (_WIN32)
#define vsnprintf _vsnprintf
#endif
#if defined (__DECC) && defined (__VMS) && (defined (__VAX) || (__CRTL_VER <= 70311000))
#define NO_vsnprintf
#endif
#if defined( NO_vsnprintf)
#define STACKBUFSIZE 16384
#else
#define STACKBUFSIZE 2048
#endif

#if defined (_WIN32) /* Actually, a GCC issue */
#define LL_FMT "I64"
#else
#define LL_FMT "ll"
#endif

#if defined (VMS) && (defined (__ia64) || defined (__ALPHA))
#define HAVE_GLOB
#endif

#if defined (__linux) || defined (VMS) || defined (__APPLE__)
#define HAVE_C99_STRFTIME 1
#endif

#if defined (_WIN32)
#define NULL_DEVICE "NUL:"
#elif defined (_VMS)
#define NULL_DEVICE "NL:"
#else
#define NULL_DEVICE "/dev/null"
#endif

/* Stubs for inlining */

#if defined(_MSC_VER)
#define SIM_INLINE _inline
#define SIM_NOINLINE _declspec (noinline)
#elif defined(__GNUC__)
#define SIM_INLINE inline
#define SIM_NOINLINE  __attribute__ ((noinline))
#else
#define SIM_INLINE 
#define SIM_NOINLINE
#endif

/* Storage class modifier for weak link definition for sim_vm_init() */

#if defined(__cplusplus)
#if defined(__GNUC__)
#define WEAK __attribute__((weak))
#elif defined(_MSC_VER)
#define WEAK __declspec(selectany) 
#else
#define WEAK extern 
#endif
#else
#define WEAK 
#endif

/* System independent definitions */

#define FLIP_SIZE       (1 << 16)                       /* flip buf size */
#if !defined (PATH_MAX)                                 /* usually in limits */
#define PATH_MAX        512
#endif
#if (PATH_MAX >= 128)
#define CBUFSIZE        (128 + PATH_MAX)                /* string buf size */
#else
#define CBUFSIZE        256
#endif

/* Breakpoint spaces definitions */

#define SIM_BKPT_N_SPC  (1 << (32 - SIM_BKPT_V_SPC))    /* max number spaces */
#define SIM_BKPT_V_SPC  (BRK_TYP_MAX + 1)               /* location in arg */

/* Extended switch definitions (bits >= 26) */

#define SIM_SW_HIDE     (1u << 26)                      /* enable hiding */
#define SIM_SW_REST     (1u << 27)                      /* attach/restore */
#define SIM_SW_REG      (1u << 28)                      /* register value */
#define SIM_SW_STOP     (1u << 29)                      /* stop message */
#define SIM_SW_SHUT     (1u << 30)                      /* shutdown */

/* Simulator status codes

   0                    ok
   1 - (SCPE_BASE - 1)  simulator specific
   SCPE_BASE - n        general
*/

#define SCPE_OK         0                               /* normal return */
#define SCPE_BASE       64                              /* base for messages */
#define SCPE_NXM        (SCPE_BASE + 0)                 /* nxm */
#define SCPE_UNATT      (SCPE_BASE + 1)                 /* no file */
#define SCPE_IOERR      (SCPE_BASE + 2)                 /* I/O error */
#define SCPE_CSUM       (SCPE_BASE + 3)                 /* loader cksum */
#define SCPE_FMT        (SCPE_BASE + 4)                 /* loader format */
#define SCPE_NOATT      (SCPE_BASE + 5)                 /* not attachable */
#define SCPE_OPENERR    (SCPE_BASE + 6)                 /* open error */
#define SCPE_MEM        (SCPE_BASE + 7)                 /* alloc error */
#define SCPE_ARG        (SCPE_BASE + 8)                 /* argument error */
#define SCPE_STEP       (SCPE_BASE + 9)                 /* step expired */
#define SCPE_UNK        (SCPE_BASE + 10)                /* unknown command */
#define SCPE_RO         (SCPE_BASE + 11)                /* read only */
#define SCPE_INCOMP     (SCPE_BASE + 12)                /* incomplete */
#define SCPE_STOP       (SCPE_BASE + 13)                /* sim stopped */
#define SCPE_EXIT       (SCPE_BASE + 14)                /* sim exit */
#define SCPE_TTIERR     (SCPE_BASE + 15)                /* console tti err */
#define SCPE_TTOERR     (SCPE_BASE + 16)                /* console tto err */
#define SCPE_EOF        (SCPE_BASE + 17)                /* end of file */
#define SCPE_REL        (SCPE_BASE + 18)                /* relocation error */
#define SCPE_NOPARAM    (SCPE_BASE + 19)                /* no parameters */
#define SCPE_ALATT      (SCPE_BASE + 20)                /* already attached */
#define SCPE_TIMER      (SCPE_BASE + 21)                /* hwre timer err */
#define SCPE_SIGERR     (SCPE_BASE + 22)                /* signal err */
#define SCPE_TTYERR     (SCPE_BASE + 23)                /* tty setup err */
#define SCPE_SUB        (SCPE_BASE + 24)                /* subscript err */
#define SCPE_NOFNC      (SCPE_BASE + 25)                /* func not imp */
#define SCPE_UDIS       (SCPE_BASE + 26)                /* unit disabled */
#define SCPE_NORO       (SCPE_BASE + 27)                /* rd only not ok */
#define SCPE_INVSW      (SCPE_BASE + 28)                /* invalid switch */
#define SCPE_MISVAL     (SCPE_BASE + 29)                /* missing value */
#define SCPE_2FARG      (SCPE_BASE + 30)                /* too few arguments */
#define SCPE_2MARG      (SCPE_BASE + 31)                /* too many arguments */
#define SCPE_NXDEV      (SCPE_BASE + 32)                /* nx device */
#define SCPE_NXUN       (SCPE_BASE + 33)                /* nx unit */
#define SCPE_NXREG      (SCPE_BASE + 34)                /* nx register */
#define SCPE_NXPAR      (SCPE_BASE + 35)                /* nx parameter */
#define SCPE_NEST       (SCPE_BASE + 36)                /* nested DO */
#define SCPE_IERR       (SCPE_BASE + 37)                /* internal error */
#define SCPE_MTRLNT     (SCPE_BASE + 38)                /* tape rec lnt error */
#define SCPE_LOST       (SCPE_BASE + 39)                /* Telnet conn lost */
#define SCPE_TTMO       (SCPE_BASE + 40)                /* Telnet conn timeout */
#define SCPE_STALL      (SCPE_BASE + 41)                /* Telnet conn stall */
#define SCPE_AFAIL      (SCPE_BASE + 42)                /* assert failed */
#define SCPE_INVREM     (SCPE_BASE + 43)                /* invalid remote console command */
#define SCPE_NOTATT     (SCPE_BASE + 44)                /* not attached */
#define SCPE_EXPECT     (SCPE_BASE + 45)                /* expect matched */
#define SCPE_REMOTE     (SCPE_BASE + 46)                /* remote console command */

#define SCPE_MAX_ERR    (SCPE_BASE + 47)                /* Maximum SCPE Error Value */
#define SCPE_KFLAG      0x1000                          /* tti data flag */
#define SCPE_BREAK      0x2000                          /* tti break flag */
#define SCPE_NOMESSAGE  0x10000000                      /* message display supression flag */
#define SCPE_BARE_STATUS(stat) ((stat) & ~(SCPE_NOMESSAGE|SCPE_KFLAG|SCPE_BREAK))

/* Print value format codes */

#define PV_RZRO         0                               /* right, zero fill */
#define PV_RSPC         1                               /* right, space fill */
#define PV_RCOMMA       2                               /* right, space fill. Comma separate every 3 */
#define PV_LEFT         3                               /* left justify */

/* Default timing parameters */

#define KBD_POLL_WAIT   5000                            /* keyboard poll */
#define KBD_MAX_WAIT    500000
#define SERIAL_IN_WAIT  100                             /* serial in time */
#define SERIAL_OUT_WAIT 100                             /* serial output */
#define NOQUEUE_WAIT    1000000                         /* min check time */
#define KBD_LIM_WAIT(x) (((x) > KBD_MAX_WAIT)? KBD_MAX_WAIT: (x))
#define KBD_WAIT(w,s)   ((w)? w: KBD_LIM_WAIT (s))

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

/* 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)   */
#define QUEUE_LIST_END ((UNIT *)1)

/* Typedefs for principal structures */

typedef struct DEVICE DEVICE;
typedef struct UNIT UNIT;
typedef struct REG REG;
typedef struct CTAB CTAB;
typedef struct C1TAB C1TAB;
typedef struct SHTAB SHTAB;
typedef struct MTAB MTAB;
typedef struct SCHTAB SCHTAB;
typedef struct BRKTAB BRKTAB;
typedef struct BRKTYPTAB BRKTYPTAB;
typedef struct EXPTAB EXPTAB;
typedef struct EXPECT EXPECT;
typedef struct SEND SEND;
typedef struct DEBTAB DEBTAB;
typedef struct FILEREF FILEREF;
typedef struct MEMFILE MEMFILE;
typedef struct BITFIELD BITFIELD;

typedef t_stat (*ACTIVATE_API)(UNIT *unit, int32 interval);

/* Device data structure */

struct DEVICE {
    const char          *name;                          /* name */
    UNIT                *units;                         /* units */
    REG                 *registers;                     /* registers */
    MTAB                *modifiers;                     /* modifiers */
    uint32              numunits;                       /* #units */
    uint32              aradix;                         /* address radix */
    uint32              awidth;                         /* address width */
    uint32              aincr;                          /* addr increment */
    uint32              dradix;                         /* data radix */
    uint32              dwidth;                         /* data width */
    t_stat              (*examine)(t_value *v, t_addr a, UNIT *up,
                            int32 sw);                  /* examine routine */
    t_stat              (*deposit)(t_value v, t_addr a, UNIT *up,
                            int32 sw);                  /* deposit routine */
    t_stat              (*reset)(DEVICE *dp);           /* reset routine */
    t_stat              (*boot)(int32 u, DEVICE *dp);
                                                        /* boot routine */
    t_stat              (*attach)(UNIT *up, CONST char *cp);
                                                        /* attach routine */
    t_stat              (*detach)(UNIT *up);            /* detach routine */
    void                *ctxt;                          /* context */
    uint32              flags;                          /* flags */
    uint32              dctrl;                          /* debug control */
    DEBTAB              *debflags;                      /* debug flags */
    t_stat              (*msize)(UNIT *up, int32 v, CONST char *cp, void *dp);
                                                        /* mem size routine */
    char                *lname;                         /* logical name */
    t_stat              (*help)(FILE *st, DEVICE *dptr,
                            UNIT *uptr, int32 flag, const char *cptr); 
                                                        /* help */
    t_stat              (*attach_help)(FILE *st, DEVICE *dptr,
                            UNIT *uptr, int32 flag, const char *cptr);
                                                        /* attach help */
    void *help_ctx;                                     /* Context available to help routines */
    const char          *(*description)(DEVICE *dptr);  /* Device Description */
    BRKTYPTAB           *brk_types;                     /* Breakpoint types */
    };

/* Device flags */

#define DEV_V_DIS       0                               /* dev disabled */
#define DEV_V_DISABLE   1                               /* dev disable-able */
#define DEV_V_DYNM      2                               /* mem size dynamic */
#define DEV_V_DEBUG     3                               /* debug capability */
#define DEV_V_TYPE      4                               /* Attach type */
#define DEV_S_TYPE      3                               /* Width of Type Field */
#define DEV_V_SECTORS   7                               /* Unit Capacity is in 512byte sectors */
#define DEV_V_DONTAUTO  8                               /* Do not auto detach already attached units */
#define DEV_V_FLATHELP  9                               /* Use traditional (unstructured) help */
#define DEV_V_NOSAVE    10                              /* Don't save device state */
#define DEV_V_UF_31     12                              /* user flags, V3.1 */
#define DEV_V_UF        16                              /* user flags */
#define DEV_V_RSV       31                              /* reserved */

#define DEV_DIS         (1 << DEV_V_DIS)                /* device is currently disabled */
#define DEV_DISABLE     (1 << DEV_V_DISABLE)            /* device can be set enabled or disabled */
#define DEV_DYNM        (1 << DEV_V_DYNM)               /* device requires call on msize routine to change memory size */
#define DEV_DEBUG       (1 << DEV_V_DEBUG)              /* device supports SET DEBUG command */
#define DEV_SECTORS     (1 << DEV_V_SECTORS)            /* capacity is 512 byte sectors */
#define DEV_DONTAUTO    (1 << DEV_V_DONTAUTO)           /* Do not auto detach already attached units */
#define DEV_FLATHELP    (1 << DEV_V_FLATHELP)           /* Use traditional (unstructured) help */
#define DEV_NOSAVE      (1 << DEV_V_NOSAVE)             /* Don't save device state */
#define DEV_NET         0                               /* Deprecated - meaningless */


#define DEV_TYPEMASK    (((1 << DEV_S_TYPE) - 1) << DEV_V_TYPE)
#define DEV_DISK        (1 << DEV_V_TYPE)               /* sim_disk Attach */
#define DEV_TAPE        (2 << DEV_V_TYPE)               /* sim_tape Attach */
#define DEV_MUX         (3 << DEV_V_TYPE)               /* sim_tmxr Attach */
#define DEV_ETHER       (4 << DEV_V_TYPE)               /* Ethernet Device */
#define DEV_DISPLAY     (5 << DEV_V_TYPE)               /* Display Device */
#define DEV_TYPE(dptr)  ((dptr)->flags & DEV_TYPEMASK)

#define DEV_UFMASK_31   (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF_31) - 1))
#define DEV_UFMASK      (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF) - 1))
#define DEV_RFLAGS      (DEV_UFMASK|DEV_DIS)            /* restored flags */

/* Unit data structure

   Parts of the unit structure are device specific, that is, they are
   not referenced by the simulator control package and can be freely
   used by device simulators.  Fields starting with 'buf', and flags
   starting with 'UF', are device specific.  The definitions given here
   are for a typical sequential device.
*/

struct UNIT {
    UNIT                *next;                          /* next active */
    t_stat              (*action)(UNIT *up);            /* action routine */
    char                *filename;                      /* open file name */
    FILE                *fileref;                       /* file reference */
    void                *filebuf;                       /* memory buffer */
    uint32              hwmark;                         /* high water mark */
    int32               time;                           /* time out */
    uint32              flags;                          /* flags */
    uint32              dynflags;                       /* dynamic flags */
    t_addr              capac;                          /* capacity */
    t_addr              pos;                            /* file position */
    void                (*io_flush)(UNIT *up);          /* io flush routine */
    uint32              iostarttime;                    /* I/O start time */
    int32               buf;                            /* buffer */
    int32               wait;                           /* wait */
    int32               u3;                             /* device specific */
    int32               u4;                             /* device specific */
    int32               u5;                             /* device specific */
    int32               u6;                             /* device specific */
    void                *up7;                           /* device specific */
    void                *up8;                           /* device specific */
    void                *tmxr;                          /* TMXR linkage */
    t_bool              (*cancel)(UNIT *);
    double              usecs_remaining;                /* time balance for long delays */
#ifdef SIM_ASYNCH_IO
    void                (*a_check_completion)(UNIT *);
    t_bool              (*a_is_active)(UNIT *);
    UNIT                *a_next;                        /* next asynch active */
    int32               a_event_time;
    ACTIVATE_API        a_activate_call;
    /* Asynchronous Polling control */
    /* These fields should only be referenced when holding the sim_tmxr_poll_lock */
    t_bool              a_polling_now;                  /* polling active flag */
    int32               a_poll_waiter_count;            /* count of polling threads */
                                                        /* waiting for this unit */
    /* Asynchronous Timer control */
    double              a_due_time;                     /* due time for timer event */
    double              a_due_gtime;                    /* due time (in instructions) for timer event */
    double              a_usec_delay;                   /* time delay for timer event */
#endif
    };

/* Unit flags */

#define UNIT_V_UF_31    12              /* dev spec, V3.1 */
#define UNIT_V_UF       16              /* device specific */
#define UNIT_V_RSV      31              /* reserved!! */

#define UNIT_ATTABLE    0000001         /* attachable */
#define UNIT_RO         0000002         /* read only */
#define UNIT_FIX        0000004         /* fixed capacity */
#define UNIT_SEQ        0000010         /* sequential */
#define UNIT_ATT        0000020         /* attached */
#define UNIT_BINK       0000040         /* K = power of 2 */
#define UNIT_BUFABLE    0000100         /* bufferable */
#define UNIT_MUSTBUF    0000200         /* must buffer */
#define UNIT_BUF        0000400         /* buffered */
#define UNIT_ROABLE     0001000         /* read only ok */
#define UNIT_DISABLE    0002000         /* disable-able */
#define UNIT_DIS        0004000         /* disabled */
#define UNIT_IDLE       0040000         /* idle eligible */

/* Unused/meaningless flags */
#define UNIT_TEXT       0000000         /* text mode - no effect */

#define UNIT_UFMASK_31  (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF_31) - 1))
#define UNIT_UFMASK     (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF) - 1))
#define UNIT_RFLAGS     (UNIT_UFMASK|UNIT_DIS)          /* restored flags */

/* Unit dynamic flags (dynflags) */

/* These flags are only set dynamically */

#define UNIT_ATTMULT    0000001         /* Allow multiple attach commands */
#define UNIT_TM_POLL    0000002         /* TMXR Polling unit */
#define UNIT_NO_FIO     0000004         /* fileref is NOT a FILE * */
#define UNIT_DISK_CHK   0000010         /* disk data debug checking (sim_disk) */
#define UNIT_TMR_UNIT   0000020         /* Unit registered as a calibrated timer */
#define UNIT_V_DF_TAPE  5               /* Bit offset for Tape Density reservation */
#define UNIT_S_DF_TAPE  3               /* Bits Reserved for Tape Density */

struct BITFIELD {
    const char      *name;                              /* field name */
    uint32          offset;                             /* starting bit */
    uint32          width;                              /* width */
    const char      **valuenames;                       /* map of values to strings */
    const char      *format;                            /* value format string */
    };

/* Register data structure */

struct REG {
    CONST char          *name;                          /* name */
    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 */
    };

/* Register flags */

#define REG_FMT         00003                           /* see PV_x */
#define REG_RO          00004                           /* read only */
#define REG_HIDDEN      00010                           /* hidden */
#define REG_NZ          00020                           /* must be non-zero */
#define REG_UNIT        00040                           /* in unit struct */
#define REG_STRUCT      00100                           /* in structure array */
#define REG_CIRC        00200                           /* circular array */
#define REG_VMIO        00400                           /* use VM data print/parse */
#define REG_VMAD        01000                           /* use VM addr print/parse */
#define REG_FIT         02000                           /* fit access to size */
#define REG_HRO         (REG_RO | REG_HIDDEN)           /* hidden, read only */

#define REG_V_UF        16                              /* device specific */
#define REG_UFMASK      (~((1u << REG_V_UF) - 1))       /* user flags mask */
#define REG_VMFLAGS     (REG_VMIO | REG_UFMASK)         /* call VM routine if any of these are set */

/* Command tables, base and alternate formats */

struct CTAB {
    const char          *name;                          /* name */
    t_stat              (*action)(int32 flag, CONST char *cptr);
                                                        /* action routine */
    int32               arg;                            /* argument */
    const char          *help;                          /* help string/structured locator */
    const char          *help_base;                     /* structured help base*/
    void                (*message)(const char *unechoed_cmdline, t_stat stat);
                                                        /* message printing routine */
    };

struct C1TAB {
    const char          *name;                          /* name */
    t_stat              (*action)(DEVICE *dptr, UNIT *uptr,
                            int32 flag, CONST char *cptr);/* action routine */
    int32               arg;                            /* argument */
    const char          *help;                          /* help string */
    };

struct SHTAB {
    const char          *name;                          /* name */
    t_stat              (*action)(FILE *st, DEVICE *dptr,
                            UNIT *uptr, int32 flag, CONST char *cptr);
    int32               arg;                            /* argument */
    const char          *help;                          /* help string */
    };

/* Modifier table - only extended entries have disp, reg, or flags */

struct MTAB {
    uint32              mask;                           /* mask */
    uint32              match;                          /* match */
    const char          *pstring;                       /* print string */
    const char          *mstring;                       /* match string */
    t_stat              (*valid)(UNIT *up, int32 v, CONST char *cp, void *dp);
                                                        /* validation routine */
    t_stat              (*disp)(FILE *st, UNIT *up, int32 v, CONST void *dp);
                                                        /* display routine */
    void                *desc;                          /* value descriptor */
                                                        /* REG * if MTAB_VAL */
                                                        /* int * if not */
    const char          *help;                          /* help string */
    };


/* mtab mask flag bits */
/* NOTE: MTAB_VALR and MTAB_VALO are only used to display help */
#define MTAB_XTD        (1u << UNIT_V_RSV)              /* ext entry flag */
#define MTAB_VDV        (0001 | MTAB_XTD)               /* valid for dev */
#define MTAB_VUN        (0002 | MTAB_XTD)               /* valid for unit */
#define MTAB_VALR       (0004 | MTAB_XTD)               /* takes a value (required) */
#define MTAB_VALO       (0010 | MTAB_XTD)               /* takes a value (optional) */
#define MTAB_NMO        (0020 | MTAB_XTD)               /* only if named */
#define MTAB_NC         (0040 | MTAB_XTD)               /* no UC conversion */
#define MTAB_QUOTE      (0100 | MTAB_XTD)               /* quoted string */
#define MTAB_SHP        (0200 | MTAB_XTD)               /* show takes parameter */
#define MODMASK(mptr,flag) (((mptr)->mask & (uint32)(flag)) == (uint32)(flag))/* flag mask test */

/* Search table */

struct SCHTAB {
    int32               logic;                          /* logical operator */
    int32               boolop;                         /* boolean operator */
    uint32              count;                          /* value count in mask and comp arrays */
    t_value             *mask;                          /* mask for logical */
    t_value             *comp;                          /* comparison for boolean */
    };

/* Breakpoint table */

struct BRKTAB {
    t_addr              addr;                           /* address */
    uint32              typ;                            /* mask of types */
#define BRK_TYP_USR_TYPES       ((1 << ('Z'-'A'+1)) - 1)/* all types A-Z */
#define BRK_TYP_DYN_STEPOVER    (SWMASK ('Z'+1))
#define BRK_TYP_DYN_USR         (SWMASK ('Z'+2))
#define BRK_TYP_DYN_ALL         (BRK_TYP_DYN_USR|BRK_TYP_DYN_STEPOVER) /* Mask of All Dynamic types */
#define BRK_TYP_TEMP            (SWMASK ('Z'+3))        /* Temporary (one-shot) */
#define BRK_TYP_MAX             (('Z'-'A')+3)           /* Maximum breakpoint type */
    int32               cnt;                            /* proceed count */
    char                *act;                           /* action string */
    double              time_fired[SIM_BKPT_N_SPC];     /* instruction count when match occurred */
    BRKTAB *next;                                       /* list with same address value */
    };

/* Breakpoint table */

struct BRKTYPTAB {
    uint32      btyp;                                   /* type mask */
    const char *desc;                                   /* description */
    };
#define BRKTYPE(typ,descrip) {SWMASK(typ), descrip}

/* Expect rule */

struct EXPTAB {
    uint8               *match;                         /* match string */
    uint32              size;                           /* match string size */
    char                *match_pattern;                 /* match pattern for format */
    int32               cnt;                            /* proceed count */
    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 */
    };

/* Send Context */

struct SEND {
    uint32              delay;                          /* instruction delay between sent data */
#define SEND_DEFAULT_DELAY  1000                        /* default delay instruction count */
    DEVICE              *dptr;                          /* Device (for Debug) */
    uint32              dbit;                           /* Debugging Bit */
    uint32              after;                          /* instruction delay before sending any data */
    double              next_time;                      /* execution time when next data can be sent */
    uint8               *buffer;                        /* buffer */
    size_t              bufsize;                        /* buffer size */
    int32               insoff;                         /* insert offset */
    int32               extoff;                         /* extra offset */
    };

/* Debug table */

struct DEBTAB {
    const char          *name;                          /* control name */
    uint32              mask;                           /* control bit */
    const char          *desc;                          /* description */
    };

/* Deprecated Debug macros.  Use sim_debug() */

#define DEBUG_PRS(d)    (sim_deb && d.dctrl)
#define DEBUG_PRD(d)    (sim_deb && d->dctrl)
#define DEBUG_PRI(d,m)  (sim_deb && (d.dctrl & (m)))
#define DEBUG_PRJ(d,m)  (sim_deb && ((d)->dctrl & (m)))

#define SIM_DBG_EVENT       0x10000
#define SIM_DBG_ACTIVATE    0x20000
#define SIM_DBG_AIO_QUEUE   0x40000

/* Open File Reference */
struct FILEREF {
    char                name[CBUFSIZE];                 /* file name */
    FILE                *file;                          /* file handle */
    int32               refcount;                       /* reference count */
    };

struct MEMFILE {
    char                *buf;                           /* buffered data */
    size_t              size;                        /* size */
    size_t              pos;                         /* data used */
    };
/* 
   The following macros exist to help populate structure contents

   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

#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 */
#define REGDATA(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \
    #nm, &(loc), (rdx), (wd), (off), (dep), (desc), (flds), (fl), (qptr), (siz)
/* Right Justified Octal Register Data */
#define ORDATA(nm,loc,wd) #nm, &(loc), 8, (wd), 0, 1, NULL, NULL
/* Right Justified Decimal Register Data */
#define DRDATA(nm,loc,wd) #nm, &(loc), 10, (wd), 0, 1, NULL, NULL
/* Right Justified Hexadecimal Register Data */
#define HRDATA(nm,loc,wd) #nm, &(loc), 16, (wd), 0, 1, NULL, NULL
/* Right Justified Binary Register Data */
#define BINRDATA(nm,loc,wd) #nm, &(loc), 2, (wd), 0, 1, NULL, NULL
/* 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
/* Arbitrary location and Radix Register */
#define GRDATA(nm,loc,rdx,wd,pos) #nm, &(loc), (rdx), (wd), (pos), 1, NULL, NULL
/* 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
/* 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
/* 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)
#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 # */
#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
#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 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"

/* Macro to ALWAYS execute the specified expression and fail if it evaluates to false. */
/* This replaces any references to "assert()" which should never be invoked */
/* with an expression which causes side effects (i.e. must be executed for */
/* the program to work correctly) */
#define ASSURE(_Expression) while (!(_Expression)) {fprintf(stderr, "%s failed at %s line %d\n", #_Expression, __FILE__, __LINE__);  \
                                                    sim_printf("%s failed at %s line %d\n", #_Expression, __FILE__, __LINE__);       \
                                                    abort();}

/* Asynch/Threaded I/O support */

#if defined (SIM_ASYNCH_IO)
#include <pthread.h>

#define SIM_ASYNCH_CLOCKS 1

extern pthread_mutex_t sim_asynch_lock;
extern pthread_cond_t sim_asynch_wake;
extern pthread_mutex_t sim_timer_lock;
extern pthread_cond_t sim_timer_wake;
extern t_bool sim_timer_event_canceled;
extern int32 sim_tmxr_poll_count;
extern pthread_cond_t sim_tmxr_poll_cond;
extern pthread_mutex_t sim_tmxr_poll_lock;
extern pthread_t sim_asynch_main_threadid;
extern UNIT * volatile sim_asynch_queue;
extern volatile t_bool sim_idle_wait;
extern int32 sim_asynch_check;
extern int32 sim_asynch_latency;
extern int32 sim_asynch_inst_latency;

/* Thread local storage */
#if defined(__GNUC__) && !defined(__APPLE__) && !defined(__hpux) && !defined(__OpenBSD__) && !defined(_AIX)
#define AIO_TLS __thread
#elif defined(_MSC_VER)
#define AIO_TLS __declspec(thread)
#else
/* Other compiler environment, then don't worry about thread local storage. */
/* It is primarily used only used in debugging messages */
#define AIO_TLS
#endif
#define AIO_QUEUE_CHECK(que, lock)                              \
    do {                                                        \
        UNIT *_cptr;                                            \
        if (lock)                                               \
            pthread_mutex_lock (lock);                          \
        for (_cptr = que;                                       \
            (_cptr != QUEUE_LIST_END);                          \
            _cptr = _cptr->next)                                \
            if (!_cptr->next) {                                 \
                if (sim_deb) {                                  \
                    sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Corruption detected\n");\
                    fclose(sim_deb);                            \
                    }                                           \
                sim_printf("Queue Corruption detected\n");      \
                abort();                                        \
                }                                               \
        if (lock)                                               \
            pthread_mutex_unlock (lock);                        \
        } while (0)
#define AIO_MAIN_THREAD (pthread_equal ( pthread_self(), sim_asynch_main_threadid ))
#define AIO_LOCK                                                  \
    pthread_mutex_lock(&sim_asynch_lock)
#define AIO_UNLOCK                                                \
    pthread_mutex_unlock(&sim_asynch_lock)
#define AIO_IS_ACTIVE(uptr) (((uptr)->a_is_active ? (uptr)->a_is_active (uptr) : FALSE) || ((uptr)->a_next))
#if defined(SIM_ASYNCH_MUX)
#define AIO_CANCEL(uptr)                                      \
    if (((uptr)->dynflags & UNIT_TM_POLL) &&                  \
        !((uptr)->next) && !((uptr)->a_next)) {               \
        (uptr)->a_polling_now = FALSE;                        \
        sim_tmxr_poll_count -= (uptr)->a_poll_waiter_count;   \
        (uptr)->a_poll_waiter_count = 0;                      \
        }
#endif /* defined(SIM_ASYNCH_MUX) */
#if !defined(AIO_CANCEL)
#define AIO_CANCEL(uptr)
#endif /* !defined(AIO_CANCEL) */
#define AIO_EVENT_BEGIN(uptr)                                     \
    do {                                                          \
        int __was_poll = uptr->dynflags & UNIT_TM_POLL
#define AIO_EVENT_COMPLETE(uptr, reason)                          \
        if (__was_poll) {                                         \
            pthread_mutex_lock (&sim_tmxr_poll_lock);             \
            uptr->a_polling_now = FALSE;                          \
            if (uptr->a_poll_waiter_count) {                      \
                sim_tmxr_poll_count -= uptr->a_poll_waiter_count; \
                uptr->a_poll_waiter_count = 0;                    \
                if (0 == sim_tmxr_poll_count)                     \
                    pthread_cond_broadcast (&sim_tmxr_poll_cond); \
                }                                                 \
            pthread_mutex_unlock (&sim_tmxr_poll_lock);           \
            }                                                     \
        AIO_UPDATE_QUEUE;                                         \
        } while (0)

#if defined(__DECC_VER)
#include <builtins>
#if defined(__IA64)
#define USE_AIO_INTRINSICS 1
#endif
#endif
#if defined(_WIN32) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)
#define USE_AIO_INTRINSICS 1
#endif
/* Provide a way to test both Intrinsic and Lock based queue manipulations  */
/* when both are available on a particular platform                         */
#if defined(DONT_USE_AIO_INTRINSICS) && defined(USE_AIO_INTRINSICS)
#undef USE_AIO_INTRINSICS
#endif
#ifdef USE_AIO_INTRINSICS
/* This approach uses intrinsics to manage access to the link list head     */
/* sim_asynch_queue.  This implementation is a completely lock free design  */
/* which avoids the potential ABA issues.                                   */
#define AIO_QUEUE_MODE "Lock free asynchronous event queue access"
#define AIO_INIT                                                  \
    do {                                                          \
      int tmr;                                                    \
      sim_asynch_main_threadid = pthread_self();                  \
      /* Empty list/list end uses the point value (void *)1.      \
         This allows NULL in an entry's a_next pointer to         \
         indicate that the entry is not currently in any list */  \
      sim_asynch_queue = QUEUE_LIST_END;                          \
      for (tmr=0; tmr<SIM_NTIMERS; tmr++)                         \
          sim_clock_cosched_queue[tmr] = QUEUE_LIST_END;          \
      } while (0)
#define AIO_CLEANUP                                               \
    do {                                                          \
      pthread_mutex_destroy(&sim_asynch_lock);                    \
      pthread_cond_destroy(&sim_asynch_wake);                     \
      pthread_mutex_destroy(&sim_timer_lock);                     \
      pthread_cond_destroy(&sim_timer_wake);                      \
      pthread_mutex_destroy(&sim_tmxr_poll_lock);                 \
      pthread_cond_destroy(&sim_tmxr_poll_cond);                  \
      } while (0)
#ifdef _WIN32
#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)
#define InterlockedCompareExchangePointer(Destination, Exchange, Comparand) __sync_val_compare_and_swap(Destination, Comparand, Exchange)
#elif defined(__DECC_VER)
#define InterlockedCompareExchangePointer(Destination, Exchange, Comparand) (void *)((int32)_InterlockedCompareExchange64(Destination, Exchange, Comparand))
#else
#error "Implementation of function InterlockedCompareExchangePointer() is needed to build with USE_AIO_INTRINSICS"
#endif
#define AIO_ILOCK AIO_LOCK
#define AIO_IUNLOCK AIO_UNLOCK
#define AIO_QUEUE_VAL (UNIT *)(InterlockedCompareExchangePointer((void * volatile *)&sim_asynch_queue, (void *)sim_asynch_queue, NULL))
#define AIO_QUEUE_SET(val, queue) (UNIT *)(InterlockedCompareExchangePointer((void * volatile *)&sim_asynch_queue, (void *)val, queue))
#define AIO_UPDATE_QUEUE sim_aio_update_queue ()
#define AIO_ACTIVATE(caller, uptr, event_time)                                   \
    if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) {           \
      sim_aio_activate ((ACTIVATE_API)caller, uptr, event_time);                 \
      return SCPE_OK;                                                            \
    } else (void)0
#else /* !USE_AIO_INTRINSICS */
/* This approach uses a pthread mutex to manage access to the link list     */
/* head sim_asynch_queue.  It will always work, but may be slower than the  */
/* lock free approach when using USE_AIO_INTRINSICS                         */
#define AIO_QUEUE_MODE "Lock based asynchronous event queue access"
#define AIO_INIT                                                  \
    do {                                                          \
      int tmr;                                                    \
      pthread_mutexattr_t attr;                                   \
                                                                  \
      pthread_mutexattr_init (&attr);                             \
      pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);  \
      pthread_mutex_init (&sim_asynch_lock, &attr);               \
      pthread_mutexattr_destroy (&attr);                          \
      sim_asynch_main_threadid = pthread_self();                  \
      /* Empty list/list end uses the point value (void *)1.      \
         This allows NULL in an entry's a_next pointer to         \
         indicate that the entry is not currently in any list */  \
      sim_asynch_queue = QUEUE_LIST_END;                          \
      for (tmr=0; tmr<SIM_NTIMERS; tmr++)                         \
          sim_clock_cosched_queue[tmr] = QUEUE_LIST_END;          \
      } while (0)
#define AIO_CLEANUP                                               \
    do {                                                          \
      pthread_mutex_destroy(&sim_asynch_lock);                    \
      pthread_cond_destroy(&sim_asynch_wake);                     \
      pthread_mutex_destroy(&sim_timer_lock);                     \
      pthread_cond_destroy(&sim_timer_wake);                      \
      pthread_mutex_destroy(&sim_tmxr_poll_lock);                 \
      pthread_cond_destroy(&sim_tmxr_poll_cond);                  \
      } while (0)
#define AIO_ILOCK AIO_LOCK
#define AIO_IUNLOCK AIO_UNLOCK
#define AIO_QUEUE_VAL sim_asynch_queue
#define AIO_QUEUE_SET(val, queue) (sim_asynch_queue = val)
#define AIO_UPDATE_QUEUE sim_aio_update_queue ()
#define AIO_ACTIVATE(caller, uptr, event_time)                         \
    if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \
      sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch event for %s after %d instructions\n", sim_uname(uptr), event_time);\
      AIO_LOCK;                                                        \
      if (uptr->a_next) {                       /* already queued? */  \
        uptr->a_activate_call = sim_activate_abs;                      \
      } else {                                                         \
        uptr->a_next = sim_asynch_queue;                               \
        uptr->a_event_time = event_time;                               \
        uptr->a_activate_call = (ACTIVATE_API)&caller;                 \
        sim_asynch_queue = uptr;                                       \
      }                                                                \
      if (sim_idle_wait) {                                             \
        sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(uptr), event_time);\
        pthread_cond_signal (&sim_asynch_wake);                        \
        }                                                              \
      AIO_UNLOCK;                                                      \
      sim_asynch_check = 0;                                            \
      return SCPE_OK;                                                  \
    } else (void)0
#endif /* USE_AIO_INTRINSICS */
#define AIO_VALIDATE if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) {sim_printf("Improper thread context for operation\n"); abort();}
#define AIO_CHECK_EVENT                                                \
    if (0 > --sim_asynch_check) {                                      \
      AIO_UPDATE_QUEUE;                                                \
      sim_asynch_check = sim_asynch_inst_latency;                      \
      } else (void)0
#define AIO_SET_INTERRUPT_LATENCY(instpersec)                                                   \
    do {                                                                                        \
      sim_asynch_inst_latency = (int32)((((double)(instpersec))*sim_asynch_latency)/1000000000);\
      if (sim_asynch_inst_latency == 0)                                                         \
        sim_asynch_inst_latency = 1;                                                            \
      } while (0)
#else /* !SIM_ASYNCH_IO */
#define AIO_QUEUE_MODE "Asynchronous I/O is not available"
#define AIO_UPDATE_QUEUE
#define AIO_ACTIVATE(caller, uptr, event_time)
#define AIO_VALIDATE
#define AIO_CHECK_EVENT
#define AIO_INIT
#define AIO_MAIN_THREAD TRUE
#define AIO_LOCK
#define AIO_UNLOCK
#define AIO_CLEANUP
#define AIO_EVENT_BEGIN(uptr)
#define AIO_EVENT_COMPLETE(uptr, reason)
#define AIO_IS_ACTIVE(uptr) FALSE
#define AIO_CANCEL(uptr)
#define AIO_SET_INTERRUPT_LATENCY(instpersec)
#define AIO_TLS
#endif /* SIM_ASYNCH_IO */

#ifdef  __cplusplus
}
#endif

#endif
Added src/sim_disk.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_disk.c: simulator disk support library

   Copyright (c) 2011, 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:

   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
   ROBERT M SUPNIK 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 Mark Pizzolato shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Mark Pizzolato.



   This is the place which hides processing of various disk formats,
   as well as OS-specific direct hardware access.

   25-Jan-11    MP      Initial Implemementation

Public routines:

   sim_disk_attach           attach disk unit
   sim_disk_detach           detach disk unit
   sim_disk_attach_help      help routine for attaching disks
   sim_disk_rdsect           read disk sectors
   sim_disk_rdsect_a         read disk sectors asynchronously
   sim_disk_wrsect           write disk sectors
   sim_disk_wrsect_a         write disk sectors asynchronously
   sim_disk_unload           unload or detach a disk as needed
   sim_disk_reset            reset unit
   sim_disk_wrp              TRUE if write protected
   sim_disk_isavailable      TRUE if available for I/O
   sim_disk_size             get disk size
   sim_disk_set_fmt          set disk format
   sim_disk_show_fmt         show disk format
   sim_disk_set_capac        set disk capacity
   sim_disk_show_capac       show disk capacity
   sim_disk_set_async        enable asynchronous operation
   sim_disk_clr_async        disable asynchronous operation
   sim_disk_data_trace       debug support

Internal routines:

   sim_os_disk_open_raw      platform specific open raw device
   sim_os_disk_close_raw     platform specific close raw device
   sim_os_disk_size_raw      platform specific raw device size
   sim_os_disk_unload_raw    platform specific disk unload/eject
   sim_os_disk_rdsect        platform specific read sectors
   sim_os_disk_wrsect        platform specific write sectors

   sim_vhd_disk_open         platform independent open virtual disk file
   sim_vhd_disk_create       platform independent create virtual disk file
   sim_vhd_disk_create_diff  platform independent create differencing virtual disk file
   sim_vhd_disk_close        platform independent close virtual disk file
   sim_vhd_disk_size         platform independent virtual disk size
   sim_vhd_disk_rdsect       platform independent read virtual disk sectors
   sim_vhd_disk_wrsect       platform independent write virtual disk sectors


*/

#define _FILE_OFFSET_BITS 64    /* 64 bit file offset for raw I/O operations  */

#include "sim_defs.h"
#include "sim_disk.h"
#include "sim_ether.h"
#include <ctype.h>
#include <sys/stat.h>

#ifdef _WIN32
#include <windows.h>
#endif
#if defined SIM_ASYNCH_IO
#include <pthread.h>
#endif

struct disk_context {
    DEVICE              *dptr;              /* Device for unit (access to debug flags) */
    uint32              dbit;               /* debugging bit */
    uint32              sector_size;        /* Disk Sector Size (of the pseudo disk) */
    uint32              capac_factor;       /* Units of Capacity (2 = word, 1 = byte) */
    uint32              xfer_element_size;  /* Disk Bus Transfer size (1 - byte, 2 - word, 4 - longword) */
    uint32              storage_sector_size;/* Sector size of the containing storage */
    uint32              removable;          /* Removable device flag */
    uint32              auto_format;        /* Format determined dynamically */
#if defined _WIN32
    HANDLE              disk_handle;        /* OS specific Raw device handle */
#endif
#if defined SIM_ASYNCH_IO
    int                 asynch_io;          /* Asynchronous Interrupt scheduling enabled */
    int                 asynch_io_latency;  /* instructions to delay pending interrupt */
    pthread_mutex_t     lock;
    pthread_t           io_thread;          /* I/O Thread Id */
    pthread_mutex_t     io_lock;
    pthread_cond_t      io_cond;
    pthread_cond_t      io_done;
    pthread_cond_t      startup_cond;
    int                 io_dop;
    uint8               *buf;
    t_seccnt            *rsects;
    t_seccnt            sects;
    t_lba               lba;
    DISK_PCALLBACK      callback;
    t_stat              io_status;
#endif
    };

#define disk_ctx up8                        /* Field in Unit structure which points to the disk_context */

#if defined SIM_ASYNCH_IO
#define AIO_CALLSETUP                                               \
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;   \
                                                                    \
if ((!callback) || !ctx->asynch_io)

#define AIO_CALL(op, _lba, _buf, _rsects, _sects,  _callback)   \
    if (ctx->asynch_io) {                                       \
        struct disk_context *ctx =                              \
                      (struct disk_context *)uptr->disk_ctx;    \
                                                                \
        pthread_mutex_lock (&ctx->io_lock);                     \
                                                                \
        sim_debug (ctx->dbit, ctx->dptr,                        \
      "sim_disk AIO_CALL(op=%d, unit=%d, lba=0x%X, sects=%d)\n",\
                op, (int)(uptr-ctx->dptr->units), _lba, _sects);\
                                                                \
        if (ctx->callback)                                      \
            abort(); /* horrible mistake, stop */               \
        ctx->io_dop = op;                                       \
        ctx->lba = _lba;                                        \
        ctx->buf = _buf;                                        \
        ctx->sects = _sects;                                    \
        ctx->rsects = _rsects;                                  \
        ctx->callback = _callback;                              \
        pthread_cond_signal (&ctx->io_cond);                    \
        pthread_mutex_unlock (&ctx->io_lock);                   \
        }                                                       \
    else                                                        \
        if (_callback)                                          \
            (_callback) (uptr, r);


#define DOP_DONE  0             /* close */
#define DOP_RSEC  1             /* sim_disk_rdsect_a */
#define DOP_WSEC  2             /* sim_disk_wrsect_a */
#define DOP_IAVL  3             /* sim_disk_isavailable_a */

static void *
_disk_io(void *arg)
{
UNIT* volatile uptr = (UNIT*)arg;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

/* Boost Priority for this I/O thread vs the CPU instruction execution
   thread which in general won't be readily yielding the processor when
   this thread needs to run */
sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);

sim_debug (ctx->dbit, ctx->dptr, "_disk_io(unit=%d) starting\n", (int)(uptr-ctx->dptr->units));

pthread_mutex_lock (&ctx->io_lock);
pthread_cond_signal (&ctx->startup_cond);   /* Signal we're ready to go */
while (ctx->asynch_io) {
    pthread_cond_wait (&ctx->io_cond, &ctx->io_lock);
    if (ctx->io_dop == DOP_DONE)
        break;
    pthread_mutex_unlock (&ctx->io_lock);
    switch (ctx->io_dop) {
        case DOP_RSEC:
            ctx->io_status = sim_disk_rdsect (uptr, ctx->lba, ctx->buf, ctx->rsects, ctx->sects);
            break;
        case DOP_WSEC:
            ctx->io_status = sim_disk_wrsect (uptr, ctx->lba, ctx->buf, ctx->rsects, ctx->sects);
            break;
        case DOP_IAVL:
            ctx->io_status = sim_disk_isavailable (uptr);
            break;
        }
    pthread_mutex_lock (&ctx->io_lock);
    ctx->io_dop = DOP_DONE;
    pthread_cond_signal (&ctx->io_done);
    sim_activate (uptr, ctx->asynch_io_latency);
    }
pthread_mutex_unlock (&ctx->io_lock);

sim_debug (ctx->dbit, ctx->dptr, "_disk_io(unit=%d) exiting\n", (int)(uptr-ctx->dptr->units));

return NULL;
}

/* This routine is called in the context of the main simulator thread before
   processing events for any unit. It is only called when an asynchronous
   thread has called sim_activate() to activate a unit.  The job of this
   routine is to put the unit in proper condition to digest what may have
   occurred in the asynchrconous thread.
  
   Since disk processing only handles a single I/O at a time to a
   particular disk device (due to using stdio for the SimH Disk format
   and stdio doesn't have an atomic seek+(read|write) operation),
   we have the opportunity to possibly detect improper attempts to
   issue multiple concurrent I/O requests. */
static void _disk_completion_dispatch (UNIT *uptr)
{
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
DISK_PCALLBACK callback = ctx->callback;

sim_debug (ctx->dbit, ctx->dptr, "_disk_completion_dispatch(unit=%d, dop=%d, callback=%p)\n", (int)(uptr-ctx->dptr->units), ctx->io_dop, ctx->callback);

if (ctx->io_dop != DOP_DONE)
    abort();                                            /* horribly wrong, stop */

if (ctx->callback && ctx->io_dop == DOP_DONE) {
    ctx->callback = NULL;
    callback (uptr, ctx->io_status);
    }
}

static t_bool _disk_is_active (UNIT *uptr)
{
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

if (ctx) {
    sim_debug (ctx->dbit, ctx->dptr, "_disk_is_active(unit=%d, dop=%d)\n", (int)(uptr-ctx->dptr->units), ctx->io_dop);
    return (ctx->io_dop != DOP_DONE);
    }
return FALSE;
}

static t_bool _disk_cancel (UNIT *uptr)
{
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

if (ctx) {
    sim_debug (ctx->dbit, ctx->dptr, "_disk_cancel(unit=%d, dop=%d)\n", (int)(uptr-ctx->dptr->units), ctx->io_dop);
    if (ctx->asynch_io) {
        pthread_mutex_lock (&ctx->io_lock);
        while (ctx->io_dop != DOP_DONE)
            pthread_cond_wait (&ctx->io_done, &ctx->io_lock);
        pthread_mutex_unlock (&ctx->io_lock);
        }
    }
return FALSE;
}
#else
#define AIO_CALLSETUP
#define AIO_CALL(op, _lba, _buf, _rsects, _sects,  _callback)   \
    if (_callback)                                              \
        (_callback) (uptr, r);
#endif

/* Forward declarations */

static t_stat sim_vhd_disk_implemented (void);
static FILE *sim_vhd_disk_open (const char *rawdevicename, const char *openmode);
static FILE *sim_vhd_disk_create (const char *szVHDPath, t_offset desiredsize);
static FILE *sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath);
static FILE *sim_vhd_disk_merge (const char *szVHDPath, char **ParentVHD);
static int sim_vhd_disk_close (FILE *f);
static void sim_vhd_disk_flush (FILE *f);
static t_offset sim_vhd_disk_size (FILE *f);
static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects);
static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects);
static t_stat sim_vhd_disk_clearerr (UNIT *uptr);
static t_stat sim_vhd_disk_set_dtype (FILE *f, const char *dtype);
static const char *sim_vhd_disk_get_dtype (FILE *f);
static t_stat sim_os_disk_implemented_raw (void);
static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode);
static int sim_os_disk_close_raw (FILE *f);
static void sim_os_disk_flush_raw (FILE *f);
static t_offset sim_os_disk_size_raw (FILE *f);
static t_stat sim_os_disk_unload_raw (FILE *f);
static t_bool sim_os_disk_isavailable_raw (FILE *f);
static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects);
static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects);
static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable);
static t_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec);
static char *HostPathToVhdPath (const char *szHostPath, char *szVhdPath, size_t VhdPathSize);
static char *VhdPathToHostPath (const char *szVhdPath, char *szHostPath, size_t HostPathSize);

struct sim_disk_fmt {
    const char          *name;                          /* name */
    int32               uflags;                         /* unit flags */
    int32               fmtval;                         /* Format type value */
    t_stat              (*impl_fnc)(void);              /* Implemented Test Function */
    };

static struct sim_disk_fmt fmts[DKUF_N_FMT] = {
    { "SIMH", 0, DKUF_F_STD, NULL},
    { "RAW",  0, DKUF_F_RAW, sim_os_disk_implemented_raw},
    { "VHD",  0, DKUF_F_VHD, sim_vhd_disk_implemented},
    { NULL,   0, 0}
    };

/* Set disk format */

t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
uint32 f;

if (uptr == NULL)
    return SCPE_IERR;
if (cptr == NULL)
    return SCPE_ARG;
for (f = 0; f < DKUF_N_FMT && fmts[f].name; f++) {
    if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) {
        if ((fmts[f].impl_fnc) && (fmts[f].impl_fnc() != SCPE_OK))
            return SCPE_NOFNC;
        uptr->flags = (uptr->flags & ~DKUF_FMT) |
            (fmts[f].fmtval << DKUF_V_FMT) | fmts[f].uflags;
        return SCPE_OK;
        }
    }
return SCPE_ARG;
}

/* Show disk format */

t_stat sim_disk_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 f = DK_GET_FMT (uptr);
size_t i;

for (i = 0; i < DKUF_N_FMT; i++)
    if (fmts[i].fmtval == f) {
        fprintf (st, "%s format", fmts[i].name);
        return SCPE_OK;
        }
fprintf (st, "invalid format");
return SCPE_OK;
}

/* Set disk capacity */

t_stat sim_disk_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
t_offset cap;
t_stat r;
DEVICE *dptr = find_dev_from_unit (uptr);

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_ARG;
if (uptr->flags & UNIT_ATT)
    return SCPE_ALATT;
cap = (t_offset) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r);
if (r != SCPE_OK)
    return SCPE_ARG;
uptr->capac = (t_addr)((cap * ((t_offset) 1000000))/((dptr->flags & DEV_SECTORS) ? 512 : 1));
return SCPE_OK;
}

/* Show disk capacity */

t_stat sim_disk_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
const char *cap_units = "B";
DEVICE *dptr = find_dev_from_unit (uptr);
t_offset capac = ((t_offset)uptr->capac)*((dptr->flags & DEV_SECTORS) ? 512 : 1);

if ((dptr->dwidth / dptr->aincr) == 16)
    cap_units = "W";
if (capac) {
    if (capac >= (t_offset) 1000000)
        fprintf (st, "capacity=%dM%s", (uint32) (capac / ((t_offset) 1000000)), cap_units);
    else if (uptr->capac >= (t_addr) 1000)
        fprintf (st, "capacity=%dK%s", (uint32) (capac / ((t_offset) 1000)), cap_units);
    else fprintf (st, "capacity=%d%s", (uint32) capac, cap_units);
    }
else fprintf (st, "undefined capacity");
return SCPE_OK;
}

/* Test for available */

t_bool sim_disk_isavailable (UNIT *uptr)
{
if (!(uptr->flags & UNIT_ATT))                          /* attached? */
    return FALSE;
switch (DK_GET_FMT (uptr)) {                            /* case on format */
    case DKUF_F_STD:                                    /* SIMH format */
        return TRUE;
    case DKUF_F_VHD:                                    /* VHD format */
        return TRUE;
        break;
    case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
        return sim_os_disk_isavailable_raw (uptr->fileref);
        break;
    default:
        return FALSE;
    }
}

t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback)
{
t_bool r = FALSE;
AIO_CALLSETUP
    r = sim_disk_isavailable (uptr);
AIO_CALL(DOP_IAVL, 0, NULL, NULL, 0, callback);
return r;
}

/* Test for write protect */

t_bool sim_disk_wrp (UNIT *uptr)
{
return (uptr->flags & DKUF_WRP)? TRUE: FALSE;
}

/* Get Disk size */

t_offset sim_disk_size (UNIT *uptr)
{
switch (DK_GET_FMT (uptr)) {                            /* case on format */
    case DKUF_F_STD:                                    /* SIMH format */
        return sim_fsize_ex (uptr->fileref);
    case DKUF_F_VHD:                                    /* VHD format */
        return sim_vhd_disk_size (uptr->fileref);
        break;
    case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
        return sim_os_disk_size_raw (uptr->fileref);
        break;
    default:
        return (t_offset)-1;
    }
}

/* Enable asynchronous operation */

t_stat sim_disk_set_async (UNIT *uptr, int latency)
{
#if !defined(SIM_ASYNCH_IO)
char *msg = "Disk: can't operate asynchronously\r\n";
sim_printf ("%s", msg);
return SCPE_NOFNC;
#else
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
pthread_attr_t attr;

sim_debug (ctx->dbit, ctx->dptr, "sim_disk_set_async(unit=%d)\n", (int)(uptr-ctx->dptr->units));

ctx->asynch_io = sim_asynch_enabled;
ctx->asynch_io_latency = latency;
if (ctx->asynch_io) {
    pthread_mutex_init (&ctx->io_lock, NULL);
    pthread_cond_init (&ctx->io_cond, NULL);
    pthread_cond_init (&ctx->io_done, NULL);
    pthread_cond_init (&ctx->startup_cond, NULL);
    pthread_attr_init(&attr);
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    pthread_mutex_lock (&ctx->io_lock);
    pthread_create (&ctx->io_thread, &attr, _disk_io, (void *)uptr);
    pthread_attr_destroy(&attr);
    pthread_cond_wait (&ctx->startup_cond, &ctx->io_lock); /* Wait for thread to stabilize */
    pthread_mutex_unlock (&ctx->io_lock);
    pthread_cond_destroy (&ctx->startup_cond);
    }
uptr->a_check_completion = _disk_completion_dispatch;
uptr->a_is_active = _disk_is_active;
uptr->cancel = _disk_cancel;
return SCPE_OK;
#endif
}

/* Disable asynchronous operation */

t_stat sim_disk_clr_async (UNIT *uptr)
{
#if !defined(SIM_ASYNCH_IO)
return SCPE_NOFNC;
#else
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

/* make sure device exists */
if (!ctx) return SCPE_UNATT;

sim_debug (ctx->dbit, ctx->dptr, "sim_disk_clr_async(unit=%d)\n", (int)(uptr-ctx->dptr->units));

if (ctx->asynch_io) {
    pthread_mutex_lock (&ctx->io_lock);
    ctx->asynch_io = 0;
    pthread_cond_signal (&ctx->io_cond);
    pthread_mutex_unlock (&ctx->io_lock);
    pthread_join (ctx->io_thread, NULL);
    pthread_mutex_destroy (&ctx->io_lock);
    pthread_cond_destroy (&ctx->io_cond);
    pthread_cond_destroy (&ctx->io_done);
    }
return SCPE_OK;
#endif
}

/* Read Sectors */

static t_stat _sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
{
t_offset da;
uint32 err, tbc;
size_t i;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects);

da = ((t_offset)lba) * ctx->sector_size;
tbc = sects * ctx->sector_size;
if (sectsread)
    *sectsread = 0;
err = sim_fseeko (uptr->fileref, da, SEEK_SET);          /* set pos */
if (!err) {
    i = sim_fread (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref);
    if (i < tbc/ctx->xfer_element_size)                 /* fill */
        memset (&buf[i*ctx->xfer_element_size], 0, tbc-(i*ctx->xfer_element_size));
    err = ferror (uptr->fileref);
    if ((!err) && (sectsread))
        *sectsread = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size);
    }
return err;
}

t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
{
t_stat r;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
t_seccnt sread = 0;

sim_debug (ctx->dbit, ctx->dptr, "sim_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects);

if ((sects == 1) &&                                     /* Single sector reads */
    (lba >= (uptr->capac*ctx->capac_factor)/(ctx->sector_size/((ctx->dptr->flags & DEV_SECTORS) ? 512 : 1)))) {/* beyond the end of the disk */
    memset (buf, '\0', ctx->sector_size);               /* are bad block management efforts - zero buffer */
    if (sectsread)
        *sectsread = 1;
    return SCPE_OK;                                     /* return success */
    }

if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) ||   /* Sector Aligned & whole sector transfers */
    ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) &&
     (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) {
    switch (DK_GET_FMT (uptr)) {                        /* case on format */
        case DKUF_F_STD:                                /* SIMH format */
            return _sim_disk_rdsect (uptr, lba, buf, sectsread, sects);
        case DKUF_F_VHD:                                /* VHD format */
            r = sim_vhd_disk_rdsect (uptr, lba, buf, &sread, sects);
            break;
        case DKUF_F_RAW:                                /* Raw Physical Disk Access */
            r = sim_os_disk_rdsect (uptr, lba, buf, &sread, sects);
            break;
        default:
            return SCPE_NOFNC;
        }
    if (sectsread)
        *sectsread = sread;
    if (r != SCPE_OK)
        return r;
    sim_buf_swap_data (buf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size);
    return r;
    }
else { /* Unaligned and/or partial sector transfers */
    uint8 *tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size);
    t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */
    t_lba tlba = lba & ~(sspsts - 1);
    t_seccnt tsects = sects + (lba - tlba);

    tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1);
    if (sectsread)
        *sectsread = 0;
    if (tbuf == NULL)
        return SCPE_MEM;
    switch (DK_GET_FMT (uptr)) {                        /* case on format */
        case DKUF_F_STD:                                /* SIMH format */
            r = _sim_disk_rdsect (uptr, tlba, tbuf, &sread, tsects);
            break;
        case DKUF_F_VHD:                                /* VHD format */
            r = sim_vhd_disk_rdsect (uptr, tlba, tbuf, &sread, tsects);
            if (r == SCPE_OK)
                sim_buf_swap_data (tbuf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size);
            break;
        case DKUF_F_RAW:                                /* Raw Physical Disk Access */
            r = sim_os_disk_rdsect (uptr, tlba, tbuf, &sread, tsects);
            if (r == SCPE_OK)
                sim_buf_swap_data (tbuf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size);
            break;
        default:
            free (tbuf);
            return SCPE_NOFNC;
        }
    if (r == SCPE_OK) {
        memcpy (buf, tbuf + ((lba - tlba) * ctx->sector_size), sects * ctx->sector_size);
        if (sectsread) {
            *sectsread = sread - (lba - tlba);
            if (*sectsread > sects)
                *sectsread = sects;
            }
        }
    free (tbuf);
    return r;
    }
}

t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback)
{
t_stat r = SCPE_OK;
AIO_CALLSETUP
    r = sim_disk_rdsect (uptr, lba, buf, sectsread, sects);
AIO_CALL(DOP_RSEC, lba, buf, sectsread, sects, callback);
return r;
}

/* Write Sectors */

static t_stat _sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
{
t_offset da;
uint32 err, tbc;
size_t i;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects);

da = ((t_offset)lba) * ctx->sector_size;
tbc = sects * ctx->sector_size;
if (sectswritten)
    *sectswritten = 0;
err = sim_fseeko (uptr->fileref, da, SEEK_SET);          /* set pos */
if (!err) {
    i = sim_fwrite (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref);
    err = ferror (uptr->fileref);
    if ((!err) && (sectswritten))
        *sectswritten = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size);
    }
return err;
}

t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
{
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
uint32 f = DK_GET_FMT (uptr);
t_stat r;
uint8 *tbuf = NULL;

sim_debug (ctx->dbit, ctx->dptr, "sim_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects);

if (uptr->dynflags & UNIT_DISK_CHK) {
    DEVICE *dptr = find_dev_from_unit (uptr);
    uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */
    t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(ctx->sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1)));
    t_lba sect;

    for (sect = 0; sect < sects; sect++) {
        t_lba offset;
        t_bool sect_error = FALSE;

        for (offset = 0; offset < ctx->sector_size; offset += sizeof(uint32)) {
            if (*((uint32 *)&buf[sect*ctx->sector_size + offset]) != (uint32)(lba + sect)) {
                sect_error = TRUE;
                break;
                }
            }
        if (sect_error) {
            uint32 save_dctrl = dptr->dctrl;
            FILE *save_sim_deb = sim_deb;

            sim_printf ("\n%s%d: Write Address Verification Error on lbn %d(0x%X) of %d(0x%X).\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(lba+sect), (int)(lba+sect), (int)total_sectors, (int)total_sectors);
            dptr->dctrl = 0xFFFFFFFF;
            sim_deb = save_sim_deb ? save_sim_deb : stdout;
            sim_disk_data_trace (uptr, buf+sect*ctx->sector_size, lba+sect, ctx->sector_size,    "Found", TRUE, 1);
            dptr->dctrl = save_dctrl;
            sim_deb = save_sim_deb;
            }
        }
    }
if (f == DKUF_F_STD)
    return _sim_disk_wrsect (uptr, lba, buf, sectswritten, sects);
if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) ||   /* Sector Aligned & whole sector transfers */
    ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) &&
     (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) {

    if (sim_end || (ctx->xfer_element_size == sizeof (char)))
        switch (DK_GET_FMT (uptr)) {                            /* case on format */
            case DKUF_F_VHD:                                    /* VHD format */
                return sim_vhd_disk_wrsect  (uptr, lba, buf, sectswritten, sects);
            case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
                return sim_os_disk_wrsect  (uptr, lba, buf, sectswritten, sects);
            default:
                return SCPE_NOFNC;
            }

    tbuf = (uint8*) malloc (sects * ctx->sector_size);
    if (NULL == tbuf)
        return SCPE_MEM;
    sim_buf_copy_swapped (tbuf, buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size);

    switch (DK_GET_FMT (uptr)) {                            /* case on format */
        case DKUF_F_VHD:                                    /* VHD format */
            r = sim_vhd_disk_wrsect (uptr, lba, tbuf, sectswritten, sects);
            break;
        case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
            r = sim_os_disk_wrsect (uptr, lba, tbuf, sectswritten, sects);
            break;
        default:
            r = SCPE_NOFNC;
            break;
        }
    }
else { /* Unaligned and/or partial sector transfers */
    t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */
    t_lba tlba = lba & ~(sspsts - 1);
    t_seccnt tsects = sects + (lba - tlba);

    tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size);
    tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1);
    if (sectswritten)
        *sectswritten = 0;
    if (tbuf == NULL)
        return SCPE_MEM;
    /* Partial Sector writes require a read-modify-write sequence for the partial sectors */
    if ((lba & (sspsts - 1)) ||
        (sects < sspsts))
        switch (DK_GET_FMT (uptr)) {                            /* case on format */
            case DKUF_F_VHD:                                    /* VHD format */
                sim_vhd_disk_rdsect (uptr, tlba, tbuf, NULL, sspsts);
                break;
            case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
                sim_os_disk_rdsect (uptr, tlba, tbuf, NULL, sspsts);
                break;
            default:
                r = SCPE_NOFNC;
                break;
            }
    if ((tsects > sspsts) &&
        ((sects + lba - tlba) & (sspsts - 1)))
        switch (DK_GET_FMT (uptr)) {                            /* case on format */
            case DKUF_F_VHD:                                    /* VHD format */
                sim_vhd_disk_rdsect (uptr, tlba + tsects - sspsts,
                                     tbuf + (tsects - sspsts) * ctx->sector_size,
                                     NULL, sspsts);
                break;
            case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
                sim_os_disk_rdsect (uptr, tlba + tsects - sspsts,
                                    tbuf + (tsects - sspsts) * ctx->sector_size,
                                    NULL, sspsts);
                break;
            default:
                r = SCPE_NOFNC;
                break;
            }
    sim_buf_copy_swapped (tbuf + (lba & (sspsts - 1)) * ctx->sector_size,
                          buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size);
    switch (DK_GET_FMT (uptr)) {                            /* case on format */
        case DKUF_F_VHD:                                    /* VHD format */
            r = sim_vhd_disk_wrsect (uptr, tlba, tbuf, sectswritten, tsects);
            break;
        case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
            r = sim_os_disk_wrsect (uptr, tlba, tbuf, sectswritten, tsects);
            break;
        default:
            r = SCPE_NOFNC;
            break;
        }
    if ((r == SCPE_OK) && sectswritten) {
        *sectswritten -= (lba - tlba);
        if (*sectswritten > sects)
            *sectswritten = sects;
        }
    }
free (tbuf);
return r;
}

t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback)
{
t_stat r = SCPE_OK;
AIO_CALLSETUP
    r =  sim_disk_wrsect (uptr, lba, buf, sectswritten, sects);
AIO_CALL(DOP_WSEC, lba, buf, sectswritten, sects, callback);
return r;
}

t_stat sim_disk_unload (UNIT *uptr)
{
switch (DK_GET_FMT (uptr)) {                            /* case on format */
    case DKUF_F_STD:                                    /* Simh */
    case DKUF_F_VHD:                                    /* VHD format */
        return sim_disk_detach (uptr);
    case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
        return sim_os_disk_unload_raw (uptr->fileref);  /* remove/eject disk */
        break;
    default:
        return SCPE_NOFNC;
    }
}

/*
   This routine is called when the simulator stops and any time
   the asynch mode is changed (enabled or disabled)
*/
static void _sim_disk_io_flush (UNIT *uptr)
{
uint32 f = DK_GET_FMT (uptr);

#if defined (SIM_ASYNCH_IO)
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

sim_disk_clr_async (uptr);
if (sim_asynch_enabled)
    sim_disk_set_async (uptr, ctx->asynch_io_latency);
#endif
switch (f) {                                            /* case on format */
    case DKUF_F_STD:                                    /* Simh */
        fflush (uptr->fileref);
        break;
    case DKUF_F_VHD:                                    /* Virtual Disk */
        sim_vhd_disk_flush (uptr->fileref);
        break;
    case DKUF_F_RAW:                                    /* Physical */
        sim_os_disk_flush_raw (uptr->fileref);
        break;
        }
}

static t_stat _err_return (UNIT *uptr, t_stat stat)
{
free (uptr->filename);
uptr->filename = NULL;
free (uptr->disk_ctx);
uptr->disk_ctx = NULL;
return stat;
}

#pragma pack(push,1)
typedef struct _ODS2_HomeBlock
    {
    uint32 hm2_l_homelbn;
    uint32 hm2_l_alhomelbn;
    uint32 hm2_l_altidxlbn;
    uint8  hm2_b_strucver;
    uint8  hm2_b_struclev;
    uint16 hm2_w_cluster;
    uint16 hm2_w_homevbn;
    uint16 hm2_w_alhomevbn;
    uint16 hm2_w_altidxvbn;
    uint16 hm2_w_ibmapvbn;
    uint32 hm2_l_ibmaplbn;
    uint32 hm2_l_maxfiles;
    uint16 hm2_w_ibmapsize;
    uint16 hm2_w_resfiles;
    uint16 hm2_w_devtype;
    uint16 hm2_w_rvn;
    uint16 hm2_w_setcount;
    uint16 hm2_w_volchar;
    uint32 hm2_l_volowner;
    uint32 hm2_l_reserved;
    uint16 hm2_w_protect;
    uint16 hm2_w_fileprot;
    uint16 hm2_w_reserved;
    uint16 hm2_w_checksum1;
    uint32 hm2_q_credate[2];
    uint8  hm2_b_window;
    uint8  hm2_b_lru_lim;
    uint16 hm2_w_extend;
    uint32 hm2_q_retainmin[2];
    uint32 hm2_q_retainmax[2];
    uint32 hm2_q_revdate[2];
    uint8  hm2_r_min_class[20];
    uint8  hm2_r_max_class[20];
    uint8  hm2_r_reserved[320];
    uint32 hm2_l_serialnum;
    uint8  hm2_t_strucname[12];
    uint8  hm2_t_volname[12];
    uint8  hm2_t_ownername[12];
    uint8  hm2_t_format[12];
    uint16 hm2_w_reserved2;
    uint16 hm2_w_checksum2;
    } ODS2_HomeBlock;

typedef struct _ODS2_FileHeader
    {
    uint8  fh2_b_idoffset;
    uint8  fh2_b_mpoffset;
    uint8  fh2_b_acoffset;
    uint8  fh2_b_rsoffset;
    uint16 fh2_w_seg_num;
    uint16 fh2_w_structlev;
    uint16 fh2_w_fid[3];
    uint16 fh2_w_ext_fid[3];
    uint16 fh2_w_recattr[16];
    uint32 fh2_l_filechar;
    uint16 fh2_w_remaining[228];
    } ODS2_FileHeader;

typedef union _ODS2_Retreval
    {
        struct 
            {
            unsigned fm2___fill   : 14;       /* type specific data               */
            unsigned fm2_v_format : 2;        /* format type code                 */
            } fm2_r_word0_bits;
        struct
            {
            unsigned fm2_v_exact    : 1;      /* exact placement specified        */
            unsigned fm2_v_oncyl    : 1;      /* on cylinder allocation desired   */
            unsigned fm2___fill     : 10;
            unsigned fm2_v_lbn      : 1;      /* use LBN of next map pointer      */
            unsigned fm2_v_rvn      : 1;      /* place on specified RVN           */
            unsigned fm2_v_format0  : 2;
            } fm2_r_map_bits0;
        struct
            {
            unsigned fm2_b_count1   : 8;      /* low byte described below         */
            unsigned fm2_v_highlbn1 : 6;      /* high order LBN                   */
            unsigned fm2_v_format1  : 2;
            unsigned fm2_w_lowlbn1  : 16;     /* low order LBN                    */
            } fm2_r_map_bits1;
        struct
            {
            struct
                {
                unsigned fm2_v_count2   : 14; /* count field                      */
                unsigned fm2_v_format2  : 2;
                unsigned fm2_l_lowlbn2  : 16; /* low order LBN                    */
                } fm2_r_map2_long0;
            uint16 fm2_l_highlbn2;            /* high order LBN                   */
            } fm2_r_map_bits2;
        struct
            {
            struct
                {
                unsigned fm2_v_highcount3 : 14; /* low order count field          */
                unsigned fm2_v_format3  : 2;
                unsigned fm2_w_lowcount3 : 16;  /* high order count field         */
                } fm2_r_map3_long0;
            uint32 fm2_l_lbn3;
            } fm2_r_map_bits3;
    } ODS2_Retreval;

typedef struct _ODS2_StorageControlBlock
    {
    uint8  scb_b_strucver;   /* 1 */
    uint8  scb_b_struclev;   /* 2 */
    uint16 scb_w_cluster;
    uint32 scb_l_volsize;
    uint32 scb_l_blksize;
    uint32 scb_l_sectors;
    uint32 scb_l_tracks;
    uint32 scb_l_cylinder;
    uint32 scb_l_status;
    uint32 scb_l_status2;
    uint16 scb_w_writecnt;
    uint8  scb_t_volockname[12];
    uint32 scb_q_mounttime[2];
    uint16 scb_w_backrev;
    uint32 scb_q_genernum[2];
    uint8  scb_b_reserved[446];
    uint16 scb_w_checksum;
    } ODS2_SCB;
#pragma pack(pop)

static uint16
ODS2Checksum (void *Buffer, uint16 WordCount)
    {
    int i;
    uint16 Sum = 0;
    uint16 CheckSum = 0;
    uint16 *Buf = (uint16 *)Buffer;

    for (i=0; i<WordCount; i++)
        CheckSum += Buf[i];
    return CheckSum;
    }


static t_offset get_filesystem_size (UNIT *uptr)
{
DEVICE *dptr;
t_addr saved_capac;
t_offset temp_capac = 512 * (t_offset)0xFFFFFFFFu;  /* Make sure we can access the largest sector */
uint32 capac_factor;
ODS2_HomeBlock Home;
ODS2_FileHeader Header;
ODS2_Retreval *Retr;
ODS2_SCB Scb;
uint16 CheckSum1, CheckSum2;
uint32 ScbLbn;
t_offset ret_val = (t_offset)-1;

if ((dptr = find_dev_from_unit (uptr)) == NULL)
    return ret_val;
capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* save capacity units (word: 2, byte: 1) */
saved_capac = uptr->capac;
uptr->capac = (t_addr)(temp_capac/(capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
if (sim_disk_rdsect (uptr, 1, (uint8 *)&Home, NULL, 1))
    goto Return_Cleanup;
CheckSum1 = ODS2Checksum (&Home, (uint16)((((char *)&Home.hm2_w_checksum1)-((char *)&Home.hm2_l_homelbn))/2));
CheckSum2 = ODS2Checksum (&Home, (uint16)((((char *)&Home.hm2_w_checksum2)-((char *)&Home.hm2_l_homelbn))/2));
if ((Home.hm2_l_homelbn == 0) || 
    (Home.hm2_l_alhomelbn == 0) || 
    (Home.hm2_l_altidxlbn == 0) || 
    ((Home.hm2_b_struclev != 2) && (Home.hm2_b_struclev != 5)) || 
    (Home.hm2_b_strucver == 0) || 
    (Home.hm2_w_cluster == 0) || 
    (Home.hm2_w_homevbn == 0) || 
    (Home.hm2_w_alhomevbn == 0) || 
    (Home.hm2_w_ibmapvbn == 0) || 
    (Home.hm2_l_ibmaplbn == 0) || 
    (Home.hm2_w_resfiles >= Home.hm2_l_maxfiles) || 
    (Home.hm2_w_ibmapsize == 0) || 
    (Home.hm2_w_resfiles < 5) || 
    (Home.hm2_w_checksum1 != CheckSum1) ||
    (Home.hm2_w_checksum2 != CheckSum2))
    goto Return_Cleanup;
if (sim_disk_rdsect (uptr, Home.hm2_l_ibmaplbn+Home.hm2_w_ibmapsize+1, (uint8 *)&Header, NULL, 1))
    goto Return_Cleanup;
CheckSum1 = ODS2Checksum (&Header, 255);
if (CheckSum1 != *(((uint16 *)&Header)+255)) /* Verify Checksum on BITMAP.SYS file header */
    goto Return_Cleanup;
Retr = (ODS2_Retreval *)(((uint16*)(&Header))+Header.fh2_b_mpoffset);
/* The BitMap File has a single extent, which may be preceeded by a placement descriptor */
if (Retr->fm2_r_word0_bits.fm2_v_format == 0)
    Retr = (ODS2_Retreval *)(((uint16 *)Retr)+1); /* skip placement descriptor */
switch (Retr->fm2_r_word0_bits.fm2_v_format)
    {
    case 1:
        ScbLbn = (Retr->fm2_r_map_bits1.fm2_v_highlbn1<<16)+Retr->fm2_r_map_bits1.fm2_w_lowlbn1;
        break;
    case 2:
        ScbLbn = (Retr->fm2_r_map_bits2.fm2_l_highlbn2<<16)+Retr->fm2_r_map_bits2.fm2_r_map2_long0.fm2_l_lowlbn2;
        break;
    case 3:
        ScbLbn = Retr->fm2_r_map_bits3.fm2_l_lbn3;
        break;
    }
Retr = (ODS2_Retreval *)(((uint16 *)Retr)+Retr->fm2_r_word0_bits.fm2_v_format+1);
if (sim_disk_rdsect (uptr, ScbLbn, (uint8 *)&Scb, NULL, 1))
    goto Return_Cleanup;
CheckSum1 = ODS2Checksum (&Scb, 255);
if (CheckSum1 != *(((uint16 *)&Scb)+255)) /* Verify Checksum on Storage Control Block */
    goto Return_Cleanup;
if ((Scb.scb_w_cluster != Home.hm2_w_cluster) || 
    (Scb.scb_b_strucver != Home.hm2_b_strucver) ||
    (Scb.scb_b_struclev != Home.hm2_b_struclev))
    goto Return_Cleanup;
if (!sim_quiet) {
    sim_printf ("%s%d: '%s' Contains ODS%d File system:\n", sim_dname (dptr), (int)(uptr-dptr->units), uptr->filename, Home.hm2_b_struclev);
    sim_printf ("%s%d: Volume Name: %12.12s ", sim_dname (dptr), (int)(uptr-dptr->units), Home.hm2_t_volname);
    sim_printf ("Format: %12.12s ", Home.hm2_t_format);
    sim_printf ("SectorsInVolume: %d\n", Scb.scb_l_volsize);
    }
ret_val = ((t_offset)Scb.scb_l_volsize) * 512;

Return_Cleanup:
uptr->capac = saved_capac;
return ret_val;
}

t_stat sim_disk_attach (UNIT *uptr, const char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize,
                        uint32 dbit, const char *dtype, uint32 pdp11tracksize, int completion_delay)
{
struct disk_context *ctx;
DEVICE *dptr;
char tbuf[4*CBUFSIZE];
FILE *(*open_function)(const char *filename, const char *mode) = sim_fopen;
FILE *(*create_function)(const char *filename, t_offset desiredsize) = NULL;
t_offset (*size_function)(FILE *file);
t_stat (*storage_function)(FILE *file, uint32 *sector_size, uint32 *removable) = NULL;
t_bool created = FALSE, copied = FALSE;
t_bool auto_format = FALSE;
t_offset capac, filesystem_capac;

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;
if (sim_switches & SWMASK ('F')) {                      /* format spec? */
    char gbuf[CBUFSIZE];
    cptr = get_glyph (cptr, gbuf, 0);                   /* get spec */
    if (*cptr == 0)                                     /* must be more */
        return SCPE_2FARG;
    if (sim_disk_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK)
        return sim_messagef (SCPE_ARG, "Invalid Disk Format: %s\n", gbuf);
    sim_switches = sim_switches & ~(SWMASK ('F'));      /* Record Format specifier already processed */
    auto_format = TRUE;
    }
if (sim_switches & SWMASK ('D')) {                      /* create difference disk? */
    char gbuf[CBUFSIZE];
    FILE *vhd;

    sim_switches = sim_switches & ~(SWMASK ('D'));
    cptr = get_glyph_nc (cptr, gbuf, 0);                /* get spec */
    if (*cptr == 0)                                     /* must be more */
        return SCPE_2FARG;
    vhd = sim_vhd_disk_create_diff (gbuf, cptr);
    if (vhd) {
        sim_vhd_disk_close (vhd);
        return sim_disk_attach (uptr, gbuf, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize, completion_delay);
        }
    return sim_messagef (SCPE_ARG, "Unable to create differencing VHD: %s\n", gbuf);
    }
if (sim_switches & SWMASK ('C')) {                      /* create vhd disk & copy contents? */
    char gbuf[CBUFSIZE];
    FILE *vhd;
    int saved_sim_switches = sim_switches;
    int32 saved_sim_quiet = sim_quiet;
    uint32 capac_factor;
    t_stat r;

    sim_switches = sim_switches & ~(SWMASK ('C'));
    cptr = get_glyph_nc (cptr, gbuf, 0);                /* get spec */
    if (*cptr == 0)                                     /* must be more */
        return SCPE_2FARG;
    sim_switches |= SWMASK ('R') | SWMASK ('E');
    sim_quiet = TRUE;
    /* 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);
        }
    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);
            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));
            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_switches & SWMASK ('V'))) {
            uint8 *verify_buf = (uint8*) malloc (1024*1024);

            if (!verify_buf) {
                sim_vhd_disk_close(vhd);
                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));
                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_rdsect (uptr, lba, verify_buf, NULL, sects);
                    uptr->fileref = save_unit_fileref;
                    uptr->flags = saved_unit_flags;
                    if (r == SCPE_OK) {
                        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));
                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))
                            break;
                    sim_printf ("\n%s%d: Verification Error on lbn %d.\n", sim_dname (dptr), (int)(uptr-dptr->units), lba+i);
                    dptr->dctrl = 0xFFFFFFFF;
                    sim_deb = stdout;
                    sim_disk_data_trace (uptr,   copy_buf+i*sector_size, lba+i, sector_size, "Expected", TRUE, 1);
                    sim_disk_data_trace (uptr, verify_buf+i*sector_size, lba+i, sector_size,    "Found", TRUE, 1);
                    dptr->dctrl = save_dctrl;
                    sim_deb = save_sim_deb;
                    }
                }
            free (verify_buf);
            }
        free (copy_buf);
        sim_vhd_disk_close (vhd);
        sim_disk_detach (uptr);
        if (r == SCPE_OK) {
            created = TRUE;
            copied = TRUE;
            tbuf[sizeof(tbuf)-1] = '\0';
            strncpy (tbuf, gbuf, sizeof(tbuf)-1);
            cptr = tbuf;
            sim_disk_set_fmt (uptr, 0, "VHD", NULL);
            sim_switches = saved_sim_switches;
            }
        else
            return r;
        /* fall through and open/return the newly created & copied vhd */
        }
    }
else
    if (sim_switches & SWMASK ('M')) {                 /* merge difference disk? */
        char gbuf[CBUFSIZE], *Parent = NULL;
        FILE *vhd;

        sim_switches = sim_switches & ~(SWMASK ('M'));
        get_glyph_nc (cptr, gbuf, 0);                  /* get spec */
        vhd = sim_vhd_disk_merge (gbuf, &Parent);
        if (vhd) {
            t_stat r;

            sim_vhd_disk_close (vhd);
            r = sim_disk_attach (uptr, Parent, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize, completion_delay);
            free (Parent);
            return r;
            }
        return SCPE_ARG;
        }

switch (DK_GET_FMT (uptr)) {                            /* case on format */
    case DKUF_F_STD:                                    /* SIMH format */
        if (NULL == (uptr->fileref = sim_vhd_disk_open (cptr, "rb"))) {
            if (errno == EBADF)                        /* VHD but broken */
                return SCPE_OPENERR;
            open_function = sim_fopen;
            size_function = sim_fsize_ex;
            break;
            }
        sim_disk_set_fmt (uptr, 0, "VHD", NULL);        /* set file format to VHD */
        sim_vhd_disk_close (uptr->fileref);             /* close vhd file*/
        auto_format = TRUE;
        uptr->fileref = NULL;
        /* Fall through to normal VHD processing */
    case DKUF_F_VHD:                                    /* VHD format */
        open_function = sim_vhd_disk_open;
        create_function = sim_vhd_disk_create;
        size_function = sim_vhd_disk_size;
        break;
    case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
        open_function = sim_os_disk_open_raw;
        size_function = sim_os_disk_size_raw;
        storage_function = sim_os_disk_info_raw;
        break;
    default:
        return SCPE_IERR;
    }
uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char));/* alloc name buf */
uptr->disk_ctx = ctx = (struct disk_context *)calloc(1, sizeof(struct disk_context));
if ((uptr->filename == NULL) || (uptr->disk_ctx == NULL))
    return _err_return (uptr, SCPE_MEM);
strncpy (uptr->filename, cptr, CBUFSIZE);               /* save name */
ctx->sector_size = (uint32)sector_size;                 /* save sector_size */
ctx->capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* save capacity units (word: 2, byte: 1) */
ctx->xfer_element_size = (uint32)xfer_element_size;     /* save xfer_element_size */
ctx->dptr = dptr;                                       /* save DEVICE pointer */
ctx->dbit = dbit;                                       /* save debug bit */
sim_debug (ctx->dbit, ctx->dptr, "sim_disk_attach(unit=%d,filename='%s')\n", (int)(uptr-ctx->dptr->units), uptr->filename);
ctx->auto_format = auto_format;                         /* save that we auto selected format */
ctx->storage_sector_size = (uint32)sector_size;         /* Default */
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 _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));
        }
    }
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));
            }
        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));
            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);
    if (dtype && strcmp (dtype, sim_vhd_disk_get_dtype (uptr->fileref))) {
        char cmd[32];

        sprintf (cmd, "%s%d %s", dptr->name, (int)(uptr-dptr->units), sim_vhd_disk_get_dtype (uptr->fileref));
        set_cmd (0, cmd);
        }
    }
uptr->flags = uptr->flags | UNIT_ATT;
uptr->pos = 0;

/* Get Device attributes if they are available */
if (storage_function)
    storage_function (uptr->fileref, &ctx->storage_sector_size, &ctx->removable);

if ((created) && (!copied)) {
    t_stat r = SCPE_OK;
    uint8 *secbuf = (uint8 *)calloc (1, ctx->sector_size);       /* alloc temp sector buf */

    /*
       On a newly created disk, we write a zero sector to the last and the
       first sectors.  This serves 3 purposes:
         1) it avoids strange allocation delays writing newly allocated
            storage at the end of the disk during simulator operation
         2) it allocates storage for the whole disk at creation time to
            avoid strange failures which may happen during simulator execution
            if the containing disk is full
         3) it leaves a Sinh Format disk at the intended size so it may
            subsequently be autosized with the correct size.
    */
    if (secbuf == NULL)
        r = SCPE_MEM;
    if (r == SCPE_OK)
        r = sim_disk_wrsect (uptr, (t_lba)(((((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)) - ctx->sector_size)/ctx->sector_size), secbuf, NULL, 1); /* Write Last Sector */
    if (r == SCPE_OK)
        r = sim_disk_wrsect (uptr, (t_lba)(0), secbuf, NULL, 1); /* Write First Sector */
    free (secbuf);
    if (r != SCPE_OK) {
        sim_disk_detach (uptr);                         /* report error now */
        remove (cptr);                                  /* remove the create file */
        return SCPE_OPENERR;
        }
    if (sim_switches & SWMASK ('I')) {                  /* Initialize To Sector Address */
        uint8 *init_buf = (uint8*) malloc (1024*1024);
        t_lba lba, sect;
        uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */
        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 (!init_buf) {
            sim_disk_detach (uptr);                         /* report error now */
            remove (cptr);
            return SCPE_MEM;
            }
        for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) {
            sects = sectors_per_buffer;
            if (lba + sects > total_sectors)
                sects = total_sectors - lba;
            for (sect = 0; sect < sects; sect++) {
                t_lba offset;
                for (offset = 0; offset < sector_size; offset += sizeof(uint32))
                    *((uint32 *)&init_buf[sect*sector_size + offset]) = (uint32)(lba + sect);
                }
            r = sim_disk_wrsect (uptr, lba, init_buf, NULL, sects);
            if (r != SCPE_OK) {
                free (init_buf);
                sim_disk_detach (uptr);                         /* report error now */
                remove (cptr);                                  /* remove the create 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));
            }
        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));
        }
    if (pdp11tracksize)
        sim_disk_pdp11_bad_block (uptr, pdp11tracksize);
    }

if (sim_switches & SWMASK ('K')) {
    t_stat r = SCPE_OK;
    t_lba lba, sect;
    uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */
    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;
    uint8 *verify_buf = (uint8*) malloc (1024*1024);

    if (!verify_buf) {
        sim_disk_detach (uptr);                         /* report error now */
        return SCPE_MEM;
        }
    for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) {
        sects = sectors_per_buffer;
        if (lba + sects > total_sectors)
            sects = total_sectors - lba;
        r = sim_disk_rdsect (uptr, lba, verify_buf, NULL, sects);
        if (r == SCPE_OK) {
            for (sect = 0; sect < sects; sect++) {
                t_lba offset;
                t_bool sect_error = FALSE;

                for (offset = 0; offset < sector_size; offset += sizeof(uint32)) {
                    if (*((uint32 *)&verify_buf[sect*sector_size + offset]) != (uint32)(lba + sect)) {
                        sect_error = TRUE;
                        break;
                        }
                    }
                if (sect_error) {
                    uint32 save_dctrl = dptr->dctrl;
                    FILE *save_sim_deb = sim_deb;

                    sim_printf ("\n%s%d: Verification Error on lbn %d(0x%X) of %d(0x%X).\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(lba+sect), (int)(lba+sect), (int)total_sectors, (int)total_sectors);
                    dptr->dctrl = 0xFFFFFFFF;
                    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));
        }
    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));
    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)) {
    if (dontautosize) {
        t_addr saved_capac = uptr->capac;

        if ((filesystem_capac != (t_offset)-1) &&
            (filesystem_capac > (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)))) {
            if (!sim_quiet) {
                uptr->capac = (t_addr)(filesystem_capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
                sim_printf ("%s%d: The file system on the disk %s is larger than simulated device (%s > ", sim_dname (dptr), (int)(uptr-dptr->units), cptr, sprint_capac (dptr, uptr));
                uptr->capac = saved_capac;
                sim_printf ("%s)\n", sprint_capac (dptr, uptr));
                }
            sim_disk_detach (uptr);
            return SCPE_OPENERR;
            }
        if ((capac < (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))) && (DKUF_F_STD != DK_GET_FMT (uptr))) {
            if (!sim_quiet) {
                uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
                sim_printf ("%s%d: non expandable disk %s is smaller than simulated device (%s < ", sim_dname (dptr), (int)(uptr-dptr->units), cptr, sprint_capac (dptr, uptr));
                uptr->capac = saved_capac;
                sim_printf ("%s)\n", sprint_capac (dptr, uptr));
                }
            sim_disk_detach (uptr);
            return SCPE_OPENERR;
            }
        }
    else {
        if ((filesystem_capac != (t_offset)-1) &&
            (filesystem_capac > capac))
            capac = filesystem_capac;
        if ((capac != (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))) || 
            (DKUF_F_STD != DK_GET_FMT (uptr)))
            uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
        }
    }

#if defined (SIM_ASYNCH_IO)
sim_disk_set_async (uptr, completion_delay);
#endif
uptr->io_flush = _sim_disk_io_flush;

return SCPE_OK;
}

t_stat sim_disk_detach (UNIT *uptr)
{
struct disk_context *ctx;
int (*close_function)(FILE *f);
FILE *fileref;
t_bool auto_format;

if ((uptr == NULL) || !(uptr->flags & UNIT_ATT))
    return SCPE_NOTATT;

ctx = (struct disk_context *)uptr->disk_ctx;
fileref = uptr->fileref;

sim_debug (ctx->dbit, ctx->dptr, "sim_disk_detach(unit=%d,filename='%s')\n", (int)(uptr-ctx->dptr->units), uptr->filename);

switch (DK_GET_FMT (uptr)) {                            /* case on format */
    case DKUF_F_STD:                                    /* Simh */
        close_function = fclose;
        break;
    case DKUF_F_VHD:                                    /* Virtual Disk */
        close_function = sim_vhd_disk_close;
        break;
    case DKUF_F_RAW:                                    /* Physical */
        close_function = sim_os_disk_close_raw;
        break;
    default:
        return SCPE_IERR;
        }
if (!(uptr->flags & UNIT_ATTABLE))                      /* attachable? */
    return SCPE_NOATT;
if (!(uptr->flags & UNIT_ATT))                          /* attached? */
    return SCPE_OK;
if (NULL == find_dev_from_unit (uptr))
    return SCPE_OK;
auto_format = ctx->auto_format;

if (uptr->io_flush)
    uptr->io_flush (uptr);                              /* flush buffered data */

sim_disk_clr_async (uptr);

uptr->flags &= ~(UNIT_ATT | UNIT_RO);
uptr->dynflags &= ~(UNIT_NO_FIO | UNIT_DISK_CHK);
free (uptr->filename);
uptr->filename = NULL;
uptr->fileref = NULL;
free (uptr->disk_ctx);
uptr->disk_ctx = NULL;
uptr->io_flush = NULL;
if (auto_format)
    sim_disk_set_fmt (uptr, 0, "SIMH", NULL);           /* restore file format */
if (close_function (fileref) == EOF)
    return SCPE_IOERR;
return SCPE_OK;
}

t_stat sim_disk_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "%s Disk Attach Help\n\n", dptr->name);

fprintf (st, "Disk container files can be one of 3 different types:\n\n");
fprintf (st, "    SIMH   A disk is an unstructured binary file of the size appropriate\n");
fprintf (st, "           for the disk drive being simulated\n");
fprintf (st, "    VHD    Virtual Disk format which is described in the \"Microsoft\n");
fprintf (st, "           Virtual Hard Disk (VHD) Image Format Specification\".  The\n");
fprintf (st, "           VHD implementation includes support for 1) Fixed (Preallocated)\n");
fprintf (st, "           disks, 2) Dynamically Expanding disks, and 3) Differencing disks.\n");
fprintf (st, "    RAW    platform specific access to physical disk or CDROM drives\n\n");
fprintf (st, "Virtual (VHD) Disks  supported conform to \"Virtual Hard Disk Image Format\n");
fprintf (st, "Specification\", Version 1.0 October 11, 2006.\n");
fprintf (st, "Dynamically expanding disks never change their \"Virtual Size\", but they don't\n");
fprintf (st, "consume disk space on the containing storage until the virtual sectors in the\n");
fprintf (st, "disk are actually written to (i.e. a 2GB Dynamic disk container file with only\n");
fprintf (st, "30MB of data will initially be about 30MB in size and this size will grow up to\n");
fprintf (st, "2GB as different sectors are written to.  The VHD format contains metadata\n");
fprintf (st, "which describes the drive size and the simh device type in use when the VHD\n");
fprintf (st, "was created.  This metadata is therefore available whenever that VHD is\n");
fprintf (st, "attached to an emulated disk device in the future so the device type and\n");
fprintf (st, "size can be automatically be configured.\n\n");

if (0 == (uptr-dptr->units)) {
    if (dptr->numunits > 1) {
        uint32 i;

        for (i=0; i < dptr->numunits; ++i)
            if (dptr->units[i].flags & UNIT_ATTABLE)
                fprintf (st, "  sim> ATTACH {switches} %s%d diskfile\n", dptr->name, i);
        }
    else
        fprintf (st, "  sim> ATTACH {switches} %s diskfile\n", dptr->name);
    }
else
    fprintf (st, "  sim> ATTACH {switches} %s diskfile\n\n", dptr->name);
fprintf (st, "\n%s attach command switches\n", dptr->name);
fprintf (st, "    -R          Attach Read Only.\n");
fprintf (st, "    -E          Must Exist (if not specified an attempt to create the indicated\n");
fprintf (st, "                disk container will be attempted).\n");
fprintf (st, "    -F          Open the indicated disk container in a specific format (default\n");
fprintf (st, "                is to autodetect VHD defaulting to simh if the indicated\n");
fprintf (st, "                container is not a VHD).\n");
fprintf (st, "    -I          Initialize newly created disk so that each sector contains its\n");
fprintf (st, "                sector address\n");
fprintf (st, "    -K          Verify that the disk contents contain the sector address in each\n");
fprintf (st, "                sector.  Whole disk checked at attach time and each sector is\n");
fprintf (st, "                checked when written.\n");
fprintf (st, "    -C          Create a VHD and copy its contents from another disk (simh, VHD,\n");
fprintf (st, "                or RAW format). Add a -V switch to verify a copy operation.\n");
fprintf (st, "    -V          Perform a verification pass to confirm successful data copy\n");
fprintf (st, "                operation.\n");
fprintf (st, "    -X          When creating a VHD, create a fixed sized VHD (vs a Dynamically\n");
fprintf (st, "                expanding one).\n");
fprintf (st, "    -D          Create a Differencing VHD (relative to an already existing VHD\n");
fprintf (st, "                disk)\n");
fprintf (st, "    -M          Merge a Differencing VHD into its parent VHD disk\n");
fprintf (st, "    -O          Override consistency checks when attaching differencing disks\n");
fprintf (st, "                which have unexpected parent disk GUID or timestamps\n\n");
fprintf (st, "    -U          Fix inconsistencies which are overridden by the -O switch\n");
fprintf (st, "    -Y          Answer Yes to prompt to overwrite last track (on disk create)\n");
fprintf (st, "    -N          Answer No to prompt to overwrite last track (on disk create)\n");
fprintf (st, "Examples:\n");
fprintf (st, "  sim> show rq\n");
fprintf (st, "    RQ, address=20001468-2000146B*, no vector, 4 units\n");
fprintf (st, "    RQ0, 159MB, not attached, write enabled, RD54, autosize, SIMH format\n");
fprintf (st, "    RQ1, 159MB, not attached, write enabled, RD54, autosize, SIMH format\n");
fprintf (st, "    RQ2, 159MB, not attached, write enabled, RD54, autosize, SIMH format\n");
fprintf (st, "    RQ3, 409KB, not attached, write enabled, RX50, autosize, SIMH format\n");
fprintf (st, "  sim> atta rq0 RA81.vhd\n");
fprintf (st, "  sim> show rq0\n");
fprintf (st, "  RQ0, 456MB, attached to RA81.vhd, write enabled, RA81, autosize, VHD format\n");
fprintf (st, "  sim> set rq2 ra92\n");
fprintf (st, "  sim> att rq2 -f vhd RA92.vhd\n");
fprintf (st, "  RQ2: creating new file\n");
fprintf (st, "  sim> sho rq2\n");
fprintf (st, "  RQ2, 1505MB, attached to RA92.vhd, write enabled, RA92, autosize, VHD format\n");
fprintf (st, "  sim> ! dir RA92.vhd\n");
fprintf (st, "   Volume in drive H is New Volume\n");
fprintf (st, "   Volume Serial Number is F8DE-510C\n\n");
fprintf (st, "   Directory of H:\\Data\n\n");
fprintf (st, "  04/14/2011  12:57 PM             5,120 RA92.vhd\n");
fprintf (st, "                 1 File(s)          5,120 bytes\n");
fprintf (st, "  sim> atta rq3 -d RA92-1-Diff.vhd RA92.vhd\n");
fprintf (st, "  sim> atta rq3 -c RA92-1.vhd RA92.vhd\n");
fprintf (st, "  RQ3: creating new virtual disk 'RA92-1.vhd'\n");
fprintf (st, "  RQ3: Copied 1505MB.  99%% complete.\n");
fprintf (st, "  RQ3: Copied 1505MB. Done.\n");
fprintf (st, "  sim> sh rq3\n");
fprintf (st, "  RQ3, 1505MB, attached to RA92-1.vhd, write enabled, RA92, autosize, VHD format\n");
fprintf (st, "  sim>  ! dir RA92*\n");
fprintf (st, "   Volume in drive H is New Volume\n");
fprintf (st, "   Volume Serial Number is F8DE-510C\n\n");
fprintf (st, "   Directory of H:\\Data\n\n");
fprintf (st, "  04/14/2011  01:12 PM             5,120 RA92-1.vhd\n");
fprintf (st, "  04/14/2011  12:58 PM             5,120 RA92.vhd\n");
fprintf (st, "                 2 File(s)         10,240 bytes\n");
fprintf (st, "  sim> sho rq2\n");
fprintf (st, "  RQ2, 1505MB, not attached, write enabled, RA92, autosize, VHD format\n");
fprintf (st, "  sim> set rq2 ra81\n");
fprintf (st, "  sim> set rq2 noauto\n");
fprintf (st, "  sim> sho rq2\n");
fprintf (st, "  RQ2, 456MB, not attached, write enabled, RA81, noautosize, VHD format\n");
fprintf (st, "  sim> set rq2 format=simh\n");
fprintf (st, "  sim> sho rq2\n");
fprintf (st, "  RQ2, 456MB, not attached, write enabled, RA81, noautosize, SIMH format\n");
fprintf (st, "  sim> atta rq2 -c RA81-Copy.vhd VMS055.dsk\n");
fprintf (st, "  RQ2: creating new virtual disk 'RA81-Copy.vhd'\n");
fprintf (st, "  RQ2: Copied 456MB.  99%% complete.\n");
fprintf (st, "  RQ2: Copied 456MB. Done.\n");
fprintf (st, "  sim> sho rq2\n");
fprintf (st, "  RQ2, 456MB, attached to RA81-Copy.vhd, write enabled, RA81, noautosize, VHD format\n");
return SCPE_OK;
}

t_bool sim_disk_vhd_support (void)
{
return SCPE_OK == sim_vhd_disk_implemented ();
}

t_bool sim_disk_raw_support (void)
{
return SCPE_OK == sim_os_disk_implemented_raw ();
}

t_stat sim_disk_reset (UNIT *uptr)
{
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

if (!(uptr->flags & UNIT_ATT))                          /* attached? */
    return SCPE_OK;

sim_debug (ctx->dbit, ctx->dptr, "sim_disk_reset(unit=%d)\n", (int)(uptr-ctx->dptr->units));

_sim_disk_io_flush(uptr);
AIO_VALIDATE;
AIO_UPDATE_QUEUE;
return SCPE_OK;
}

t_stat sim_disk_perror (UNIT *uptr, const char *msg)
{
int saved_errno = errno;

if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
    return SCPE_NOATT;
switch (DK_GET_FMT (uptr)) {                            /* case on format */
    case DKUF_F_STD:                                    /* SIMH format */
    case DKUF_F_VHD:                                    /* VHD format */
    case DKUF_F_RAW:                                    /* Raw Physical Disk Access */
        perror (msg);
        sim_printf ("%s %s: %s\n", sim_uname(uptr), msg, strerror(saved_errno));
    default:
        ;
    }
return SCPE_OK;
}

t_stat sim_disk_clearerr (UNIT *uptr)
{
if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
    return SCPE_NOATT;
switch (DK_GET_FMT (uptr)) {                            /* case on format */
    case DKUF_F_STD:                                    /* SIMH format */
        clearerr (uptr->fileref);
        break;
    case DKUF_F_VHD:                                    /* VHD format */
        sim_vhd_disk_clearerr (uptr);
        break;
    default:
        ;
    }
return SCPE_OK;
}


/* Factory bad block table creation routine

   This routine writes a DEC standard 144 compliant bad block table on the
   last track of the specified unit as described in: 
      EL-00144_B_DEC_STD_144_Disk_Standard_for_Recording_and_Handling_Bad_Sectors_Nov76.pdf
   The bad block table consists of 10 repetitions of the same table, 
   formatted as follows:

        words 0-1       pack id number
        words 2-3       cylinder/sector/surface specifications
         :
        words n-n+1     end of table (-1,-1)

   Inputs:
        uptr    =       pointer to unit
        sec     =       number of sectors per surface
   Outputs:
        sta     =       status code
*/

static t_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec)
{
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
int32 i;
t_addr da;
int32 wds = ctx->sector_size/sizeof (uint16);
uint16 *buf;
DEVICE *dptr;
char *namebuf, *c;
uint32 packid;

if ((sec < 2) || (wds < 16))
    return SCPE_ARG;
if ((uptr->flags & UNIT_ATT) == 0)
    return SCPE_UNATT;
if ((dptr = find_dev_from_unit (uptr)) == NULL)
    return SCPE_NOATT;
if (uptr->flags & UNIT_RO)
    return SCPE_RO;
if (ctx->capac_factor != 2)                  /* Must be Word oriented Capacity */
    return SCPE_IERR;
if (!get_yn ("Overwrite last track? [N]", FALSE))
    return SCPE_OK;
if ((buf = (uint16 *) malloc (wds * sizeof (uint16))) == NULL)
    return SCPE_MEM;
namebuf = uptr->filename;
if ((c = strrchr (namebuf, '/')))
    namebuf = c+1;
if ((c = strrchr (namebuf, '\\')))
    namebuf = c+1;
if ((c = strrchr (namebuf, ']')))
    namebuf = c+1;
packid = eth_crc32(0, namebuf, strlen (namebuf));
buf[0] = (uint16)packid;
buf[1] = (uint16)(packid >> 16) & 0x7FFF;   /* Make sure MSB is clear */
buf[2] = buf[3] = 0;
for (i = 4; i < wds; i++)
    buf[i] = 0177777u;
da = (uptr->capac*((dptr->flags & DEV_SECTORS) ? 512 : 1)) - (sec * wds);
for (i = 0; (i < sec) && (i < 10); i++, da += wds)
    if (sim_disk_wrsect (uptr, (t_lba)(da/wds), (uint8 *)buf, NULL, 1)) {
        free (buf);
        return SCPE_IOERR;
        }
free (buf);
return SCPE_OK;
}

void sim_disk_data_trace(UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason)
{
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

if (sim_deb && (ctx->dptr->dctrl & reason)) {
    char pos[32];

    sprintf (pos, "lbn: %08X ", (unsigned int)lba);
    sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), pos, len, txt, reason);
    }
}


/* OS Specific RAW Disk I/O support */

#if defined _WIN32

static void _set_errno_from_status (DWORD dwStatus)
{
switch (dwStatus) {
    case ERROR_FILE_NOT_FOUND:    case ERROR_PATH_NOT_FOUND:
    case ERROR_INVALID_DRIVE:     case ERROR_NO_MORE_FILES:
    case ERROR_BAD_NET_NAME:      case ERROR_BAD_NETPATH:
    case ERROR_BAD_PATHNAME:      case ERROR_FILENAME_EXCED_RANGE:
        errno = ENOENT;
        return;
    case ERROR_INVALID_ACCESS:    case ERROR_INVALID_DATA:
    case ERROR_INVALID_FUNCTION:  case ERROR_INVALID_PARAMETER:
    case ERROR_NEGATIVE_SEEK:
        errno = EINVAL;
        return;
    case ERROR_ARENA_TRASHED:     case ERROR_NOT_ENOUGH_MEMORY:
    case ERROR_INVALID_BLOCK:     case ERROR_NOT_ENOUGH_QUOTA:
        errno = ENOMEM;
        return;
    case ERROR_TOO_MANY_OPEN_FILES:
        errno = EMFILE;
        return;
    case ERROR_ACCESS_DENIED:     case ERROR_CURRENT_DIRECTORY:
    case ERROR_LOCK_VIOLATION:    case ERROR_NETWORK_ACCESS_DENIED:
    case ERROR_CANNOT_MAKE:       case ERROR_FAIL_I24:
    case ERROR_DRIVE_LOCKED:      case ERROR_SEEK_ON_DEVICE:
    case ERROR_NOT_LOCKED:        case ERROR_LOCK_FAILED:
        errno = EACCES;
        return;
    case ERROR_ALREADY_EXISTS:    case ERROR_FILE_EXISTS:
        errno = EEXIST;
        return;
    case ERROR_INVALID_HANDLE:    case ERROR_INVALID_TARGET_HANDLE:
    case ERROR_DIRECT_ACCESS_HANDLE:
        errno = EBADF;
        return;
    case ERROR_DIR_NOT_EMPTY:
        errno = ENOTEMPTY;
        return;
    case ERROR_BAD_ENVIRONMENT:
        errno = E2BIG;
        return;
    case ERROR_BAD_FORMAT:
        errno = ENOEXEC;
        return;
    case ERROR_NOT_SAME_DEVICE:
        errno = EXDEV;
        return;
    case ERROR_BROKEN_PIPE:
        errno = EPIPE;
        return;
    case ERROR_DISK_FULL:
        errno = ENOSPC;
        return;
    case ERROR_WAIT_NO_CHILDREN:  case ERROR_CHILD_NOT_COMPLETE:
        errno = ECHILD;
        return;
    case ERROR_NO_PROC_SLOTS:     case ERROR_MAX_THRDS_REACHED:
    case ERROR_NESTING_NOT_ALLOWED:
        errno = EAGAIN;
        return;
    }
if ((dwStatus >= ERROR_WRITE_PROTECT) && (dwStatus <= ERROR_SHARING_BUFFER_EXCEEDED)) {
    errno = EACCES;
    return;
    }
if ((dwStatus >= ERROR_INVALID_STARTING_CODESEG) && (dwStatus <= ERROR_INFLOOP_IN_RELOC_CHAIN)) {
    errno = ENOEXEC;
    return;
    }
errno = EINVAL;
}
#if defined(__GNUC__)
#include <ddk/ntddstor.h>
#include <ddk/ntdddisk.h>
#else
#include <winioctl.h>
#endif

#if defined(__cplusplus)
extern "C" {
#endif
WINBASEAPI BOOL WINAPI GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize);
#if defined(__cplusplus)
    }
#endif

struct _device_type {
    int32 Type;
    const char *desc;
    } DeviceTypes[] = {
        {FILE_DEVICE_8042_PORT,             "8042_PORT"},
        {FILE_DEVICE_ACPI,                  "ACPI"},
        {FILE_DEVICE_BATTERY,               "BATTERY"},
        {FILE_DEVICE_BEEP,                  "BEEP"},
#ifdef FILE_DEVICE_BLUETOOTH
        {FILE_DEVICE_BLUETOOTH,             "BLUETOOTH"},
#endif
        {FILE_DEVICE_BUS_EXTENDER,          "BUS_EXTENDER"},
        {FILE_DEVICE_CD_ROM,                "CD_ROM"},
        {FILE_DEVICE_CD_ROM_FILE_SYSTEM,    "CD_ROM_FILE_SYSTEM"},
        {FILE_DEVICE_CHANGER,               "CHANGER"},
        {FILE_DEVICE_CONTROLLER,            "CONTROLLER"},
#ifdef FILE_DEVICE_CRYPT_PROVIDER
        {FILE_DEVICE_CRYPT_PROVIDER,        "CRYPT_PROVIDER"},
#endif
        {FILE_DEVICE_DATALINK,              "DATALINK"},
        {FILE_DEVICE_DFS,                   "DFS"},
        {FILE_DEVICE_DFS_FILE_SYSTEM,       "DFS_FILE_SYSTEM"},
        {FILE_DEVICE_DFS_VOLUME,            "DFS_VOLUME"},
        {FILE_DEVICE_DISK,                  "DISK"},
        {FILE_DEVICE_DISK_FILE_SYSTEM,      "DISK_FILE_SYSTEM"},
        {FILE_DEVICE_DVD,                   "DVD"},
        {FILE_DEVICE_FILE_SYSTEM,           "FILE_SYSTEM"},
#ifdef FILE_DEVICE_FIPS
        {FILE_DEVICE_FIPS,                  "FIPS"},
#endif
        {FILE_DEVICE_FULLSCREEN_VIDEO,      "FULLSCREEN_VIDEO"},
#ifdef FILE_DEVICE_INFINIBAND
        {FILE_DEVICE_INFINIBAND,            "INFINIBAND"},
#endif
        {FILE_DEVICE_INPORT_PORT,           "INPORT_PORT"},
        {FILE_DEVICE_KEYBOARD,              "KEYBOARD"},
        {FILE_DEVICE_KS,                    "KS"},
        {FILE_DEVICE_KSEC,                  "KSEC"},
        {FILE_DEVICE_MAILSLOT,              "MAILSLOT"},
        {FILE_DEVICE_MASS_STORAGE,          "MASS_STORAGE"},
        {FILE_DEVICE_MIDI_IN,               "MIDI_IN"},
        {FILE_DEVICE_MIDI_OUT,              "MIDI_OUT"},
        {FILE_DEVICE_MODEM,                 "MODEM"},
        {FILE_DEVICE_MOUSE,                 "MOUSE"},
        {FILE_DEVICE_MULTI_UNC_PROVIDER,    "MULTI_UNC_PROVIDER"},
        {FILE_DEVICE_NAMED_PIPE,            "NAMED_PIPE"},
        {FILE_DEVICE_NETWORK,               "NETWORK"},
        {FILE_DEVICE_NETWORK_BROWSER,       "NETWORK_BROWSER"},
        {FILE_DEVICE_NETWORK_FILE_SYSTEM,   "NETWORK_FILE_SYSTEM"},
        {FILE_DEVICE_NETWORK_REDIRECTOR,    "NETWORK_REDIRECTOR"},
        {FILE_DEVICE_NULL,                  "NULL"},
        {FILE_DEVICE_PARALLEL_PORT,         "PARALLEL_PORT"},
        {FILE_DEVICE_PHYSICAL_NETCARD,      "PHYSICAL_NETCARD"},
        {FILE_DEVICE_PRINTER,               "PRINTER"},
        {FILE_DEVICE_SCANNER,               "SCANNER"},
        {FILE_DEVICE_SCREEN,                "SCREEN"},
        {FILE_DEVICE_SERENUM,               "SERENUM"},
        {FILE_DEVICE_SERIAL_MOUSE_PORT,     "SERIAL_MOUSE_PORT"},
        {FILE_DEVICE_SERIAL_PORT,           "SERIAL_PORT"},
        {FILE_DEVICE_SMARTCARD,             "SMARTCARD"},
        {FILE_DEVICE_SMB,                   "SMB"},
        {FILE_DEVICE_SOUND,                 "SOUND"},
        {FILE_DEVICE_STREAMS,               "STREAMS"},
        {FILE_DEVICE_TAPE,                  "TAPE"},
        {FILE_DEVICE_TAPE_FILE_SYSTEM,      "TAPE_FILE_SYSTEM"},
        {FILE_DEVICE_TERMSRV,               "TERMSRV"},
        {FILE_DEVICE_TRANSPORT,             "TRANSPORT"},
        {FILE_DEVICE_UNKNOWN,               "UNKNOWN"},
        {FILE_DEVICE_VDM,                   "VDM"},
        {FILE_DEVICE_VIDEO,                 "VIDEO"},
        {FILE_DEVICE_VIRTUAL_DISK,          "VIRTUAL_DISK"},
#ifdef FILE_DEVICE_VMBUS
        {FILE_DEVICE_VMBUS,                 "VMBUS"},
#endif
        {FILE_DEVICE_WAVE_IN,               "WAVE_IN"},
        {FILE_DEVICE_WAVE_OUT,              "WAVE_OUT"},
#ifdef FILE_DEVICE_WPD
        {FILE_DEVICE_WPD,                   "WPD"},
#endif
        {0,                                 NULL}};

static const char *_device_type_name (int DeviceType)
{
int i;

for (i=0; DeviceTypes[i].desc; i++)
    if (DeviceTypes[i].Type == DeviceType)
        return DeviceTypes[i].desc;
return "Unknown";
}

static t_stat sim_os_disk_implemented_raw (void)
{
return sim_toffset_64 ? SCPE_OK : SCPE_NOFNC;
}

static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode)
{
HANDLE Handle;
DWORD DesiredAccess = 0;

if (strchr (openmode, 'r'))
    DesiredAccess |= GENERIC_READ;
if (strchr (openmode, 'w') || strchr (openmode, '+'))
    DesiredAccess |= GENERIC_WRITE;
/* SCP Command Line parsing replaces \\ with \ presuming this is an 
   escape sequence.  This only affecdts RAW device names and UNC paths.
   We handle the RAW device name case here by prepending paths beginning 
   with \.\ with an extra \. */
if (!memcmp ("\\.\\", rawdevicename, 3)) {
    char *tmpname = (char *)malloc (2 + strlen (rawdevicename));

    if (tmpname == NULL)
        return NULL;
    *tmpname = '\\';
    strcpy (tmpname + 1, rawdevicename);
    Handle = CreateFileA (tmpname, DesiredAccess, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS|FILE_FLAG_WRITE_THROUGH, NULL);
    free (tmpname);
    if (Handle != INVALID_HANDLE_VALUE)
        return (FILE *)Handle;
    }
Handle = CreateFileA (rawdevicename, DesiredAccess, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS|FILE_FLAG_WRITE_THROUGH, NULL);
if (Handle == INVALID_HANDLE_VALUE) {
    _set_errno_from_status (GetLastError ());
    return NULL;
    }
return (FILE *)Handle;
}

static int sim_os_disk_close_raw (FILE *f)
{
if (!CloseHandle ((HANDLE)f)) {
    _set_errno_from_status (GetLastError ());
    return EOF;
    }
return 0;
}

static void sim_os_disk_flush_raw (FILE *f)
{
FlushFileBuffers ((HANDLE)f);
}

static t_offset sim_os_disk_size_raw (FILE *Disk)
{
DWORD IoctlReturnSize;
LARGE_INTEGER Size;

if (GetFileSizeEx((HANDLE)Disk, &Size))
    return (t_offset)(Size.QuadPart);
#ifdef IOCTL_STORAGE_READ_CAPACITY
if (1) {
    STORAGE_READ_CAPACITY S;

    ZeroMemory (&S, sizeof (S));
    S.Version = sizeof (STORAGE_READ_CAPACITY);
    if (DeviceIoControl((HANDLE)Disk,                      /* handle to volume */
                         IOCTL_STORAGE_READ_CAPACITY,      /* dwIoControlCode */
                         NULL,                             /* lpInBuffer */
                         0,                                /* nInBufferSize */
                         (LPVOID) &S,                      /* output buffer */
                         (DWORD) sizeof(S),                /* size of output buffer */
                         (LPDWORD) &IoctlReturnSize,       /* number of bytes returned */
                         (LPOVERLAPPED) NULL))             /* OVERLAPPED structure */
        return (t_offset)(S.DiskLength.QuadPart);
    }
#endif
#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
if (1) {
    DISK_GEOMETRY_EX G;

    ZeroMemory (&G, sizeof (G));
    if (DeviceIoControl((HANDLE)Disk,                      /* handle to volume */
                         IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, /* dwIoControlCode */
                         NULL,                             /* lpInBuffer */
                         0,                                /* nInBufferSize */
                         (LPVOID) &G,                      /* output buffer */
                         (DWORD) sizeof(G),                /* size of output buffer */
                         (LPDWORD) &IoctlReturnSize,       /* number of bytes returned */
                         (LPOVERLAPPED) NULL))             /* OVERLAPPED structure */
        return (t_offset)(G.DiskSize.QuadPart);
    }
#endif
#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY
if (1) {
    DISK_GEOMETRY G;

    if (DeviceIoControl((HANDLE)Disk,                      /* handle to volume */
                         IOCTL_DISK_GET_DRIVE_GEOMETRY,    /* dwIoControlCode */
                         NULL,                             /* lpInBuffer */
                         0,                                /* nInBufferSize */
                         (LPVOID) &G,                      /* output buffer */
                         (DWORD) sizeof(G),                /* size of output buffer */
                         (LPDWORD) &IoctlReturnSize,       /* number of bytes returned */
                         (LPOVERLAPPED) NULL))             /* OVERLAPPED structure */
        return (t_offset)(G.Cylinders.QuadPart*G.TracksPerCylinder*G.SectorsPerTrack*G.BytesPerSector);
    }
#endif
_set_errno_from_status (GetLastError ());
return (t_offset)-1;
}

static t_stat sim_os_disk_unload_raw (FILE *Disk)
{
#ifdef IOCTL_STORAGE_EJECT_MEDIA
DWORD BytesReturned;
uint32 Removable = FALSE;

sim_os_disk_info_raw (Disk, NULL, &Removable);
if (Removable) {
    if (!DeviceIoControl((HANDLE)Disk,                  /* handle to disk */
                         IOCTL_STORAGE_EJECT_MEDIA,     /* dwIoControlCode */
                         NULL,                          /* lpInBuffer */
                         0,                             /* nInBufferSize */
                         NULL,                          /* lpOutBuffer */
                         0,                             /* nOutBufferSize */
                         (LPDWORD) &BytesReturned,      /* number of bytes returned */
                         (LPOVERLAPPED) NULL)) {        /* OVERLAPPED structure */
        _set_errno_from_status (GetLastError ());
        return SCPE_IOERR;
        }
    }
return SCPE_OK;
#else
return SCPE_NOFNC;
#endif
}

static t_bool sim_os_disk_isavailable_raw (FILE *Disk)
{
#ifdef IOCTL_STORAGE_EJECT_MEDIA
DWORD BytesReturned;
uint32 Removable = FALSE;

sim_os_disk_info_raw (Disk, NULL, &Removable);
if (Removable) {
    if (!DeviceIoControl((HANDLE)Disk,                  /* handle to disk */
                         IOCTL_STORAGE_CHECK_VERIFY,    /* dwIoControlCode */
                         NULL,                          /* lpInBuffer */
                         0,                             /* nInBufferSize */
                         NULL,                          /* lpOutBuffer */
                         0,                             /* nOutBufferSize */
                         (LPDWORD) &BytesReturned,      /* number of bytes returned */
                         (LPOVERLAPPED) NULL)) {        /* OVERLAPPED structure */
        _set_errno_from_status (GetLastError ());
        return FALSE;
        }
    }
#endif
return TRUE;
}

static t_stat sim_os_disk_info_raw (FILE *Disk, uint32 *sector_size, uint32 *removable)
{
DWORD IoctlReturnSize;
STORAGE_DEVICE_NUMBER Device;

ZeroMemory (&Device, sizeof (Device));
if (DeviceIoControl((HANDLE)Disk,                      /* handle to volume */
                     IOCTL_STORAGE_GET_DEVICE_NUMBER,  /* dwIoControlCode */
                     NULL,                             /* lpInBuffer */
                     0,                                /* nInBufferSize */
                     (LPVOID) &Device,                 /* output buffer */
                     (DWORD) sizeof(Device),           /* size of output buffer */
                     (LPDWORD) &IoctlReturnSize,       /* number of bytes returned */
                     (LPOVERLAPPED) NULL))             /* OVERLAPPED structure */
     sim_printf ("Device OK - Type: %s, Number: %d\n", _device_type_name (Device.DeviceType), (int)Device.DeviceNumber);

if (sector_size)
    *sector_size = 512;
if (removable)
    *removable = 0;
#ifdef IOCTL_STORAGE_READ_CAPACITY
if (1) {
    STORAGE_READ_CAPACITY S;

    ZeroMemory (&S, sizeof (S));
    S.Version = sizeof (STORAGE_READ_CAPACITY);
    if (DeviceIoControl((HANDLE)Disk,                      /* handle to volume */
                         IOCTL_STORAGE_READ_CAPACITY,      /* dwIoControlCode */
                         NULL,                             /* lpInBuffer */
                         0,                                /* nInBufferSize */
                         (LPVOID) &S,                      /* output buffer */
                         (DWORD) sizeof(S),                /* size of output buffer */
                         (LPDWORD) &IoctlReturnSize,       /* number of bytes returned */
                         (LPOVERLAPPED) NULL))             /* OVERLAPPED structure */
        if (sector_size)
            *sector_size = S.BlockLength;
    }
#endif
#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
if (1) {
    DISK_GEOMETRY_EX G;

    ZeroMemory (&G, sizeof (G));
    if (DeviceIoControl((HANDLE)Disk,                      /* handle to volume */
                         IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, /* dwIoControlCode */
                         NULL,                             /* lpInBuffer */
                         0,                                /* nInBufferSize */
                         (LPVOID) &G,                      /* output buffer */
                         (DWORD) sizeof(G),                /* size of output buffer */
                         (LPDWORD) &IoctlReturnSize,       /* number of bytes returned */
                         (LPOVERLAPPED) NULL))             /* OVERLAPPED structure */
        if (sector_size)
            *sector_size = G.Geometry.BytesPerSector;
    }
#endif
#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY
if (1) {
    DISK_GEOMETRY G;

    if (DeviceIoControl((HANDLE)Disk,                      /* handle to volume */
                         IOCTL_DISK_GET_DRIVE_GEOMETRY,    /* dwIoControlCode */
                         NULL,                             /* lpInBuffer */
                         0,                                /* nInBufferSize */
                         (LPVOID) &G,                      /* output buffer */
                         (DWORD) sizeof(G),                /* size of output buffer */
                         (LPDWORD) &IoctlReturnSize,       /* number of bytes returned */
                         (LPOVERLAPPED) NULL))             /* OVERLAPPED structure */
        if (sector_size)
            *sector_size = G.BytesPerSector;
    }
#endif
#ifdef IOCTL_STORAGE_GET_HOTPLUG_INFO
if (1) {
    STORAGE_HOTPLUG_INFO H;

    ZeroMemory (&H, sizeof (H));
    if (DeviceIoControl((HANDLE)Disk,                      /* handle to volume */
                         IOCTL_STORAGE_GET_HOTPLUG_INFO,   /* dwIoControlCode */
                         NULL,                             /* lpInBuffer */
                         0,                                /* nInBufferSize */
                         (LPVOID) &H,                      /* output buffer */
                         (DWORD) sizeof(H),                /* size of output buffer */
                         (LPDWORD) &IoctlReturnSize,       /* number of bytes returned */
                         (LPOVERLAPPED) NULL))             /* OVERLAPPED structure */
        if (removable)
            *removable = H.MediaRemovable;
    }
#endif
if (removable && *removable)
    sim_printf ("Removable Device\n");
return SCPE_OK;
}

static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
{
OVERLAPPED pos;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
long long addr;

sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects);

addr = ((long long)lba) * ctx->sector_size;
memset (&pos, 0, sizeof (pos));
pos.Offset = (DWORD)addr;
pos.OffsetHigh = (DWORD)(addr >> 32);
if (ReadFile ((HANDLE)(uptr->fileref), buf, sects * ctx->sector_size, (LPDWORD)sectsread, &pos)) {
    if (sectsread)
        *sectsread /= ctx->sector_size;
    return SCPE_OK;
    }
_set_errno_from_status (GetLastError ());
return SCPE_IOERR;
}

static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
{
OVERLAPPED pos;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
long long addr;

sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects);

addr = ((long long)lba) * ctx->sector_size;
memset (&pos, 0, sizeof (pos));
pos.Offset = (DWORD)addr;
pos.OffsetHigh = (DWORD)(addr >> 32);
if (WriteFile ((HANDLE)(uptr->fileref), buf, sects * ctx->sector_size, (LPDWORD)sectswritten, &pos)) {
    if (sectswritten)
        *sectswritten /= ctx->sector_size;
    return SCPE_OK;
    }
_set_errno_from_status (GetLastError ());
return SCPE_IOERR;
}

#elif defined (__linux) || defined (__linux__) || defined (__sun) || defined (__sun__) || defined (__hpux) || defined (_AIX)

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

static t_stat sim_os_disk_implemented_raw (void)
{
return sim_toffset_64 ? SCPE_OK : SCPE_NOFNC;
}

static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode)
{
int mode = 0;

if (strchr (openmode, 'r') && (strchr (openmode, '+') || strchr (openmode, 'w')))
    mode = O_RDWR;
else
    if (strchr (openmode, 'r'))
        mode = O_RDONLY;
#ifdef O_LARGEFILE
mode |= O_LARGEFILE;
#endif
#ifdef O_DSYNC
mode |= O_DSYNC;
#endif
return (FILE *)((long)open (rawdevicename, mode, 0));
}

static int sim_os_disk_close_raw (FILE *f)
{
return close ((int)((long)f));
}

static void sim_os_disk_flush_raw (FILE *f)
{
fsync ((int)((long)f));
}

static t_offset sim_os_disk_size_raw (FILE *f)
{
t_offset pos, size;

pos = (t_offset)lseek ((int)((long)f), (off_t)0, SEEK_CUR);
size = (t_offset)lseek ((int)((long)f), (off_t)0, SEEK_END);
lseek ((int)((long)f), (off_t)pos, SEEK_SET);
return size;
}

static t_stat sim_os_disk_unload_raw (FILE *f)
{
return SCPE_IOERR;
}

static t_bool sim_os_disk_isavailable_raw (FILE *Disk)
{
return TRUE;
}

static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
{
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
off_t addr;
ssize_t bytesread;

sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects);

addr = ((off_t)lba) * ctx->sector_size;
bytesread = pread((int)((long)uptr->fileref), buf, sects * ctx->sector_size, addr);
if (bytesread < 0) {
    if (sectsread)
        *sectsread = 0;
    return SCPE_IOERR;
    }
if (sectsread)
    *sectsread = bytesread / ctx->sector_size;
return SCPE_OK;
}

static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
{
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
off_t addr;
ssize_t byteswritten;

sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects);

addr = ((off_t)lba) * ctx->sector_size;
byteswritten = pwrite((int)((long)uptr->fileref), buf, sects * ctx->sector_size, addr);
if (byteswritten < 0) {
    if (sectswritten)
        *sectswritten = 0;
    return SCPE_IOERR;
    }
if (sectswritten)
    *sectswritten = byteswritten / ctx->sector_size;
return SCPE_OK;
}

static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable)
{
if (sector_size)
    *sector_size = 512;
if (removable)
    *removable = 0;
return SCPE_OK;
}

#else
/*============================================================================*/
/*                        Non-implemented versions                            */
/*============================================================================*/

static t_stat sim_os_disk_implemented_raw (void)
{
return SCPE_NOFNC;
}

static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode)
{
return NULL;
}

static int sim_os_disk_close_raw (FILE *f)
{
return EOF;
}

static void sim_os_disk_flush_raw (FILE *f)
{
}

static t_offset sim_os_disk_size_raw (FILE *f)
{
return (t_offset)-1;
}

static t_stat sim_os_disk_unload_raw (FILE *f)
{
return SCPE_NOFNC;
}

static t_bool sim_os_disk_isavailable_raw (FILE *Disk)
{
return FALSE;
}

static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
{
return SCPE_NOFNC;
}

static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
{
return SCPE_NOFNC;
}

static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable)
{
return SCPE_NOFNC;
}

#endif

/* OS Independent Disk Virtual Disk (VHD) I/O support */

#if (defined (VMS) && !(defined (__ALPHA) || defined (__ia64)))
#define DONT_DO_VHD_SUPPORT  /* VAX/VMS compilers don't have 64 bit integers */
#endif

#if defined (DONT_DO_VHD_SUPPORT)

/*============================================================================*/
/*                        Non-implemented version                             */
/*   This is only for hody systems which don't have 64 bit integer types      */
/*============================================================================*/

static t_stat sim_vhd_disk_implemented (void)
{
return SCPE_NOFNC;
}

static FILE *sim_vhd_disk_open (const char *vhdfilename, const char *openmode)
{
return NULL;
}

static FILE *sim_vhd_disk_merge (const char *szVHDPath, char **ParentVHD)
{
return NULL;
}

static FILE *sim_vhd_disk_create (const char *szVHDPath, t_offset desiredsize)
{
return NULL;
}

static FILE *sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath)
{
return NULL;
}

static int sim_vhd_disk_close (FILE *f)
{
return -1;
}

static void sim_vhd_disk_flush (FILE *f)
{
}

static t_offset sim_vhd_disk_size (FILE *f)
{
return (t_offset)-1;
}

static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
{
return SCPE_IOERR;
}

static t_stat sim_vhd_disk_clearerr (UNIT *uptr)
{
return SCPE_IOERR;
}

static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
{
return SCPE_IOERR;
}

static t_stat sim_vhd_disk_set_dtype (FILE *f, const char *dtype)
{
return SCPE_NOFNC;
}

static const char *sim_vhd_disk_get_dtype (FILE *f)
{
return NULL;
}

#else

/*++
    This code follows the details specified in the "Virtual Hard Disk Image
    Format Specification", Version 1.0 October 11, 2006.  This format
    specification is available for anyone to implement under the
    "Microsoft Open Specification Promise" described at:
        http://www.microsoft.com/interop/osp/default.mspx.
--*/

typedef t_uint64    uint64;
typedef t_int64     int64;

typedef struct _VHD_Footer {
    /*
    Cookies are used to uniquely identify the original creator of the hard disk
    image. The values are case-sensitive.  Microsoft uses the "conectix" string
    to identify this file as a hard disk image created by Microsoft Virtual
    Server, Virtual PC, and predecessor products. The cookie is stored as an
    eight-character ASCII string with the "c" in the first byte, the "o" in
    the second byte, and so on.
    */
    char Cookie[8];
    /*
    This is a bit field used to indicate specific feature support. The following
    table displays the list of features.
    Any fields not listed are reserved.

    Feature Value:
       No features enabled     0x00000000
       Temporary               0x00000001
       Reserved                0x00000002

       No features enabled.
              The hard disk image has no special features enabled in it.
       Temporary.
              This bit is set if the current disk is a temporary disk. A
              temporary disk designation indicates to an application that
              this disk is a candidate for deletion on shutdown.
       Reserved.
              This bit must always be set to 1.
       All other bits are also reserved and should be set to 0.
    */
    uint32 Features;
    /*
    This field is divided into a major/minor version and matches the version of
    the specification used in creating the file. The most-significant two bytes
    are for the major version. The least-significant two bytes are the minor
    version.  This must match the file format specification. For the current
    specification, this field must be initialized to 0x00010000.
    The major version will be incremented only when the file format is modified
    in such a way that it is no longer compatible with older versions of the
    file format.
    */
    uint32 FileFormatVersion;
    /*
    This field holds the absolute byte offset, from the beginning of the file,
    to the next structure. This field is used for dynamic disks and differencing
    disks, but not fixed disks. For fixed disks, this field should be set to
    0xFFFFFFFF.
    */
    uint64 DataOffset;
    /*
    This field stores the creation time of a hard disk image. This is the number
    of seconds since January 1, 2000 12:00:00 AM in UTC/GMT.
    */
    uint32 TimeStamp;
    /*
    This field is used to document which application created the hard disk. The
    field is a left-justified text field. It uses a single-byte character set.
    If the hard disk is created by Microsoft Virtual PC, "vpc " is written in
    this field. If the hard disk image is created by Microsoft Virtual Server,
    then "vs  " is written in this field.
    Other applications should use their own unique identifiers.
    */
    char CreatorApplication[4];
    /*
    This field holds the major/minor version of the application that created
    the hard disk image.  Virtual Server 2004 sets this value to 0x00010000 and
    Virtual PC 2004 sets this to 0x00050000.
    */
    uint32 CreatorVersion;
    /*
    This field stores the type of host operating system this disk image is
    created on.
       Host OS type    Value
       Windows         0x5769326B (Wi2k)
       Macintosh       0x4D616320 (Mac )
    */
    uint8 CreatorHostOS[4];
    /*
    This field stores the size of the hard disk in bytes, from the perspective
    of the virtual machine, at creation time. This field is for informational
    purposes.
    */
    uint64 OriginalSize;
    /*
    This field stores the current size of the hard disk, in bytes, from the
    perspective of the virtual machine.
    This value is same as the original size when the hard disk is created.
    This value can change depending on whether the hard disk is expanded.
    */
    uint64 CurrentSize;
    /*
    This field stores the cylinder, heads, and sectors per track value for the
    hard disk.
       Disk Geometry field          Size (bytes)
       Cylinder                     2
       Heads                        1
       Sectors per track/cylinder   1

    When a hard disk is configured as an ATA hard disk, the CHS values (that is,
    Cylinder, Heads, Sectors per track) are used by the ATA controller to
    determine the size of the disk. When the user creates a hard disk of a
    certain size, the size of the hard disk image in the virtual machine is
    smaller than that created by the user. This is because CHS value calculated
    from the hard disk size is rounded down. The pseudo-code for the algorithm
    used to determine the CHS values can be found in the appendix of this
    document.
    */
    uint32 DiskGeometry;
    /*
       Disk Type field              Value
       None                         0
       Reserved (deprecated)        1
       Fixed hard disk              2
       Dynamic hard disk            3
       Differencing hard disk       4
       Reserved (deprecated)        5
       Reserved (deprecated)        6
    */
    uint32 DiskType;
    /*
    This field holds a basic checksum of the hard disk footer. It is just a
    one's complement of the sum of all the bytes in the footer without the
    checksum field.
    If the checksum verification fails, the Virtual PC and Virtual Server
    products will instead use the header. If the checksum in the header also
    fails, the file should be assumed to be corrupt. The pseudo-code for the
    algorithm used to determine the checksum can be found in the appendix of
    this document.
    */
    uint32 Checksum;
    /*
    Every hard disk has a unique ID stored in the hard disk. This is used to
    identify the hard disk. This is a 128-bit universally unique identifier
    (UUID). This field is used to associate a parent hard disk image with its
    differencing hard disk image(s).
    */
    uint8 UniqueID[16];
    /*
    This field holds a one-byte flag that describes whether the system is in
    saved state. If the hard disk is in the saved state the value is set to 1.
    Operations such as compaction and expansion cannot be performed on a hard
    disk in a saved state.
    */
    uint8 SavedState;
    /*
    This field contains zeroes. It is 427 bytes in size.
    */
    uint8 Reserved1[11];
    /*
    This field is an extension to the VHD spec and includes a simh drive type
    name as a nul terminated string.
    */
    uint8 DriveType[16];
    /*
    This field contains zeroes. It is 400 bytes in size.
    */
    uint8 Reserved[400];
    } VHD_Footer;

/*
For dynamic and differencing disk images, the "Data Offset" field within
the image footer points to a secondary structure that provides additional
information about the disk image. The dynamic disk header should appear on
a sector (512-byte) boundary.
*/
typedef struct _VHD_DynamicDiskHeader {
    /*
    This field holds the value "cxsparse". This field identifies the header.
    */
    char Cookie[8];
    /*
    This field contains the absolute byte offset to the next structure in the
    hard disk image. It is currently unused by existing formats and should be
    set to 0xFFFFFFFF.
    */
    uint64 DataOffset;
    /*
    This field stores the absolute byte offset of the Block Allocation Table
    (BAT) in the file.
    */
    uint64 TableOffset;
    /*
    This field stores the version of the dynamic disk header. The field is
    divided into Major/Minor version. The least-significant two bytes represent
    the minor version, and the most-significant two bytes represent the major
    version. This must match with the file format specification. For this
    specification, this field must be initialized to 0x00010000.
    The major version will be incremented only when the header format is
    modified in such a way that it is no longer compatible with older versions
    of the product.
    */
    uint32 HeaderVersion;
    /*
    This field holds the maximum entries present in the BAT. This should be
    equal to the number of blocks in the disk (that is, the disk size divided
    by the block size).
    */
    uint32 MaxTableEntries;
    /*
    A block is a unit of expansion for dynamic and differencing hard disks. It
    is stored in bytes. This size does not include the size of the block bitmap.
    It is only the size of the data section of the block. The sectors per block
    must always be a power of two. The default value is 0x00200000 (indicating a
    block size of 2 MB).
    */
    uint32 BlockSize;
    /*
    This field holds a basic checksum of the dynamic header. It is a one's
    complement of the sum of all the bytes in the header without the checksum
    field.
    If the checksum verification fails the file should be assumed to be corrupt.
    */
    uint32 Checksum;
    /*
    This field is used for differencing hard disks. A differencing hard disk
    stores a 128-bit UUID of the parent hard disk. For more information, see
    "Creating Differencing Hard Disk Images" later in this paper.
    */
    uint8 ParentUniqueID[16];
    /*
    This field stores the modification time stamp of the parent hard disk. This
    is the number of seconds since January 1, 2000 12:00:00 AM in UTC/GMT.
    */
    uint32 ParentTimeStamp;
    /*
    This field should be set to zero.
    */
    uint32 Reserved0;
    /*
    This field contains a Unicode string (UTF-16) of the parent hard disk
    filename.
    */
    char ParentUnicodeName[512];
    /*
    These entries store an absolute byte offset in the file where the parent
    locator for a differencing hard disk is stored. This field is used only for
    differencing disks and should be set to zero for dynamic disks.
    */
    struct VHD_ParentLocator {
        /*
        The platform code describes which platform-specific format is used for the
        file locator. For Windows, a file locator is stored as a path (for example.
        "c:\disksimages\ParentDisk.vhd"). On a Macintosh system, the file locator
        is a binary large object (blob) that contains an "alias." The parent locator
        table is used to support moving hard disk images across platforms.
        Some current platform codes include the following:
           Platform Code        Description
           None (0x0)
           Wi2r (0x57693272)    [deprecated]
           Wi2k (0x5769326B)    [deprecated]
           W2ru (0x57327275)    Unicode pathname (UTF-16) on Windows relative to the differencing disk pathname.
           W2ku (0x57326B75)    Absolute Unicode (UTF-16) pathname on Windows.
           Mac (0x4D616320)     (Mac OS alias stored as a blob)
           MacX(0x4D616358)     A file URL with UTF-8 encoding conforming to RFC 2396.
        */
        uint8 PlatformCode[4];
        /*
        This field stores the number of 512-byte sectors needed to store the parent
        hard disk locator.
        */
        uint32 PlatformDataSpace;
        /*
        This field stores the actual length of the parent hard disk locator in bytes.
        */
        uint32 PlatformDataLength;
        /*
        This field must be set to zero.
        */
        uint32 Reserved;
        /*
        This field stores the absolute file offset in bytes where the platform
        specific file locator data is stored.
        */
        uint64 PlatformDataOffset;
        /*
        This field stores the absolute file offset in bytes where the platform
        specific file locator data is stored.
        */
        } ParentLocatorEntries[8];
    /*
    This must be initialized to zeroes.
    */
    char Reserved[256];
    } VHD_DynamicDiskHeader;

#define VHD_BAT_FREE_ENTRY (0xFFFFFFFF)
#define VHD_DATA_BLOCK_ALIGNMENT ((uint64)4096)    /* Optimum when underlying storage has 4k sectors */

#define VHD_DT_Fixed                 2  /* Fixed hard disk */
#define VHD_DT_Dynamic               3  /* Dynamic hard disk */
#define VHD_DT_Differencing          4  /* Differencing hard disk */

static uint32 NtoHl(uint32 value);

static uint64 NtoHll(uint64 value);

typedef struct VHD_IOData *VHDHANDLE;

static t_stat ReadFilePosition(FILE *File, void *buf, size_t bufsize, size_t *bytesread, uint64 position)
{
uint32 err = sim_fseeko (File, (t_offset)position, SEEK_SET);
size_t i;

if (bytesread)
    *bytesread = 0;
if (!err) {
    i = fread (buf, 1, bufsize, File);
    err = ferror (File);
    if ((!err) && bytesread)
        *bytesread = i;
    }
return (err ? SCPE_IOERR : SCPE_OK);
}

static t_stat WriteFilePosition(FILE *File, void *buf, size_t bufsize, size_t *byteswritten, uint64 position)
{
uint32 err = sim_fseeko (File, (t_offset)position, SEEK_SET);
size_t i;

if (byteswritten)
    *byteswritten = 0;
if (!err) {
    i = fwrite (buf, 1, bufsize, File);
    err = ferror (File);
    if ((!err) && byteswritten)
        *byteswritten = i;
    }
return (err ? SCPE_IOERR : SCPE_OK);
}

static uint32
CalculateVhdFooterChecksum(void *data,
                           size_t size)
{
uint32 sum = 0;
uint8 *c = (uint8 *)data;

while (size--)
    sum += *c++;
return ~sum;
}

#if defined(_WIN32) || defined (__ALPHA) || defined (__ia64) || defined (VMS)
#ifndef __BYTE_ORDER__
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#endif
#endif
#ifndef __BYTE_ORDER__
#define __BYTE_ORDER__ UNKNOWN
#endif
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
static uint32
NtoHl(uint32 value)
{
uint8 *l = (uint8 *)&value;
return l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24);
}

static uint64
NtoHll(uint64 value)
{
uint8 *l = (uint8 *)&value;
uint64 highresult = l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24);
uint32 lowresult = l[7] | (l[6]<<8) | (l[5]<<16) | (l[4]<<24);
return (highresult << 32) | lowresult;
}
#elif  __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
static uint32
NtoHl(uint32 value)
{
return value;
}

static uint64
NtoHll(uint64 value)
{
return value;
}
#else
static uint32
NtoHl(uint32 value)
{
uint8 *l = (uint8 *)&value;

if (sim_end)
    return l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24);
return value;
}

static uint64
NtoHll(uint64 value)
{
uint8 *l = (uint8 *)&value;

if (sim_end) {
    uint64 highresult = l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24);
    uint32 lowresult = l[7] | (l[6]<<8) | (l[5]<<16) | (l[4]<<24);
    return (highresult << 32) | lowresult;
    }
return value;
}
#endif

static
int
GetVHDFooter(const char *szVHDPath,
             VHD_Footer *sFooter,
             VHD_DynamicDiskHeader *sDynamic,
             uint32 **aBAT,
             uint32 *ModifiedTimeStamp,
             char *szParentVHDPath,
             size_t ParentVHDPathSize)
{
FILE *File = NULL;
uint64 position;
uint32 sum, saved_sum;
int Return = 0;
VHD_Footer sHeader;
struct stat statb;

if (sFooter)
    memset(sFooter, '\0', sizeof(*sFooter));
if (sDynamic)
    memset(sDynamic, '\0', sizeof(*sDynamic));
if (aBAT)
    *aBAT = NULL;
File = sim_fopen (szVHDPath, "rb");
if (!File) {
    Return = errno;
    goto Return_Cleanup;
    }
if (ModifiedTimeStamp) {
    if (fstat (fileno (File), &statb)) {
        Return = errno;
        goto Return_Cleanup;
        }
    else
        *ModifiedTimeStamp = NtoHl ((uint32)(statb.st_mtime-946684800));
    }
position = sim_fsize_ex (File);
if (((int64)position) == -1) {
    Return = errno;
    goto Return_Cleanup;
    }
position -= sizeof(*sFooter);
if (ReadFilePosition(File,
                     sFooter,
                     sizeof(*sFooter),
                     NULL,
                     position)) {
    Return = errno;
    goto Return_Cleanup;
    }
saved_sum = NtoHl(sFooter->Checksum);
sFooter->Checksum = 0;
sum = CalculateVhdFooterChecksum(sFooter, sizeof(*sFooter));
sFooter->Checksum = NtoHl(saved_sum);
if ((sum != saved_sum) || (memcmp("conectix", sFooter->Cookie, sizeof(sFooter->Cookie)))) {
    Return = EINVAL;                                    /* File Corrupt */
    goto Return_Cleanup;
    }
if (ReadFilePosition(File,
                     &sHeader,
                     sizeof(sHeader),
                     NULL,
                     (uint64)0)) {
    Return = errno;
    goto Return_Cleanup;
    }
if ((NtoHl(sFooter->DiskType) != VHD_DT_Dynamic) &&
    (NtoHl(sFooter->DiskType) != VHD_DT_Differencing) &&
    (NtoHl(sFooter->DiskType) != VHD_DT_Fixed)) {
    Return = EINVAL;                                    /* File Corrupt */
    goto Return_Cleanup;
    }
if (((NtoHl(sFooter->DiskType) == VHD_DT_Dynamic) ||
     (NtoHl(sFooter->DiskType) == VHD_DT_Differencing)) &&
     memcmp(sFooter, &sHeader, sizeof(sHeader))) {
    Return = EINVAL;                                    /* File Corrupt */
    goto Return_Cleanup;
    }
if ((sDynamic) &&
    ((NtoHl(sFooter->DiskType) == VHD_DT_Dynamic) ||
     (NtoHl(sFooter->DiskType) == VHD_DT_Differencing))) {
    if (ReadFilePosition(File,
                         sDynamic,
                         sizeof (*sDynamic),
                         NULL,
                         NtoHll (sFooter->DataOffset))) {
        Return = errno;
        goto Return_Cleanup;
        }
    saved_sum = NtoHl (sDynamic->Checksum);
    sDynamic->Checksum = 0;
    sum = CalculateVhdFooterChecksum (sDynamic, sizeof(*sDynamic));
    sDynamic->Checksum = NtoHl (saved_sum);
    if ((sum != saved_sum) || (memcmp ("cxsparse", sDynamic->Cookie, sizeof (sDynamic->Cookie)))) {
        Return = errno;
        goto Return_Cleanup;
        }
    if (aBAT) {
        *aBAT = (uint32*) malloc(512*((sizeof(**aBAT)*NtoHl(sDynamic->MaxTableEntries)+511)/512));
        if (ReadFilePosition(File,
                             *aBAT,
                             sizeof (**aBAT)*NtoHl(sDynamic->MaxTableEntries),
                             NULL,
                             NtoHll (sDynamic->TableOffset))) {
            Return = EINVAL;                            /* File Corrupt */
            goto Return_Cleanup;
            }
        }
    if (szParentVHDPath && ParentVHDPathSize) {
        VHD_Footer sParentFooter;

        memset (szParentVHDPath, '\0', ParentVHDPathSize);
        if (NtoHl (sFooter->DiskType) == VHD_DT_Differencing) {
            size_t i, j;

            for (j=0; j<8; ++j) {
                uint8 *Pdata;
                uint32 PdataSize;
                char ParentName[512];
                char CheckPath[512];
                uint32 ParentModificationTime;

                if ('\0' == sDynamic->ParentLocatorEntries[j].PlatformCode[0])
                    continue;
                memset (ParentName, '\0', sizeof(ParentName));
                memset (CheckPath, '\0', sizeof(CheckPath));
                PdataSize = NtoHl(sDynamic->ParentLocatorEntries[j].PlatformDataSpace);
                Pdata = (uint8*) calloc (1, PdataSize+2);
                if (!Pdata)
                    continue;
                if (ReadFilePosition(File,
                                     Pdata,
                                     PdataSize,
                                     NULL,
                                     NtoHll (sDynamic->ParentLocatorEntries[j].PlatformDataOffset))) {
                    free (Pdata);
                    continue;
                    }
                for (i=0; i<NtoHl(sDynamic->ParentLocatorEntries[j].PlatformDataLength); i+=2)
                    if ((Pdata[i] == '\0') && (Pdata[i+1] == '\0')) {
                        ParentName[i/2] = '\0';
                        break;
                        }
                    else
                        ParentName[i/2] = Pdata[i] ? Pdata[i] : Pdata[i+1];
                free (Pdata);
                if (0 == memcmp (sDynamic->ParentLocatorEntries[j].PlatformCode, "W2ku", 4))
                    strncpy (CheckPath, ParentName, sizeof (CheckPath)-1);
                else
                    if (0 == memcmp (sDynamic->ParentLocatorEntries[j].PlatformCode, "W2ru", 4)) {
                        const char *c;

                        if ((c = strrchr (szVHDPath, '\\')))
                             memcpy (CheckPath, szVHDPath, c-szVHDPath+1);
                             strncpy (CheckPath+strlen(CheckPath), ParentName, sizeof (CheckPath)-(strlen (CheckPath)+1));
                        }
                VhdPathToHostPath (CheckPath, CheckPath, sizeof (CheckPath));
                if (0 == GetVHDFooter(CheckPath,
                                      &sParentFooter,
                                      NULL,
                                      NULL,
                                      &ParentModificationTime,
                                      NULL,
                                      0)) {
                    if ((0 == memcmp (sDynamic->ParentUniqueID, sParentFooter.UniqueID, sizeof (sParentFooter.UniqueID))) &&
                        ((sDynamic->ParentTimeStamp == ParentModificationTime) ||
                         ((NtoHl(sDynamic->ParentTimeStamp)-NtoHl(ParentModificationTime)) == 3600) ||
                         (sim_switches & SWMASK ('O'))))
                         strncpy (szParentVHDPath, CheckPath, ParentVHDPathSize);
                    else {
                        if (0 != memcmp (sDynamic->ParentUniqueID, sParentFooter.UniqueID, sizeof (sParentFooter.UniqueID)))
                            sim_printf ("Error Invalid Parent VHD '%s' for Differencing VHD: %s\n", CheckPath, szVHDPath);
                        else
                            sim_printf ("Error Parent VHD '%s' has been modified since Differencing VHD: %s was created\n", CheckPath, szVHDPath);
                        Return = EINVAL;                /* File Corrupt/Invalid */
                        }
                    break;
                    }
                else {
                    struct stat statb;

                    if (0 == stat (CheckPath, &statb)) {
                        sim_printf ("Parent VHD '%s' corrupt for Differencing VHD: %s\n", CheckPath, szVHDPath);
                        Return = EBADF;                /* File Corrupt/Invalid */
                        break;
                        }
                    }
                }
            if (!*szParentVHDPath) {
                if (Return != EINVAL)                   /* File Not Corrupt? */
                    sim_printf ("Missing Parent VHD for Differencing VHD: %s\n", szVHDPath);
                Return = EBADF;
                }
            }
        }
    }
Return_Cleanup:
if (File)
    fclose(File);
if (aBAT && (0 != Return)) {
    free (*aBAT);
    *aBAT = NULL;
    }
return errno = Return;
}

struct VHD_IOData {
    VHD_Footer Footer;
    VHD_DynamicDiskHeader Dynamic;
    uint32 *BAT;
    FILE *File;
    char ParentVHDPath[512];
    struct VHD_IOData *Parent;
    };

static t_stat sim_vhd_disk_implemented (void)
{
return SCPE_OK;
}

static t_stat sim_vhd_disk_set_dtype (FILE *f, const char *dtype)
{
VHDHANDLE hVHD  = (VHDHANDLE)f;
int Status = 0;

memset (hVHD->Footer.DriveType, '\0', sizeof hVHD->Footer.DriveType);
memcpy (hVHD->Footer.DriveType, dtype, ((1+strlen (dtype)) < sizeof (hVHD->Footer.DriveType)) ? (1+strlen (dtype)) : sizeof (hVHD->Footer.DriveType));
hVHD->Footer.Checksum = 0;
hVHD->Footer.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Footer, sizeof(hVHD->Footer)));

if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Fixed) {
    if (WriteFilePosition(hVHD->File,
                          &hVHD->Footer,
                          sizeof(hVHD->Footer),
                          NULL,
                          NtoHll (hVHD->Footer.CurrentSize)))
        Status = errno;
    goto Cleanup_Return;
    }
else {
    uint64 position;

    position = sim_fsize_ex (hVHD->File);
    if (((int64)position) == -1) {
        Status = errno;
        goto Cleanup_Return;
        }
    position -= sizeof(hVHD->Footer);
    /* Update both copies on a dynamic disk */
    if (WriteFilePosition(hVHD->File,
                          &hVHD->Footer,
                          sizeof(hVHD->Footer),
                          NULL,
                          (uint64)0)) {
        Status = errno;
        goto Cleanup_Return;
        }
    if (WriteFilePosition(hVHD->File,
                          &hVHD->Footer,
                          sizeof(hVHD->Footer),
                          NULL,
                          position)) {
        Status = errno;
        goto Cleanup_Return;
        }
    }
Cleanup_Return:
if (Status)
    return SCPE_IOERR;
return SCPE_OK;
}

static const char *sim_vhd_disk_get_dtype (FILE *f)
{
VHDHANDLE hVHD  = (VHDHANDLE)f;

return (char *)(&hVHD->Footer.DriveType[0]);
}

static FILE *sim_vhd_disk_open (const char *szVHDPath, const char *DesiredAccess)
    {
    VHDHANDLE hVHD = (VHDHANDLE) calloc (1, sizeof(*hVHD));
    int NeedUpdate = FALSE;
    int Status;

    if (!hVHD)
        return (FILE *)hVHD;
    Status = GetVHDFooter (szVHDPath,
                           &hVHD->Footer,
                           &hVHD->Dynamic,
                           &hVHD->BAT,
                           NULL,
                           hVHD->ParentVHDPath,
                           sizeof (hVHD->ParentVHDPath));
    if (Status)
        goto Cleanup_Return;
    if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Differencing) {
        uint32 ParentModifiedTimeStamp;
        VHD_Footer ParentFooter;
        VHD_DynamicDiskHeader ParentDynamic;

        hVHD->Parent = (VHDHANDLE)sim_vhd_disk_open (hVHD->ParentVHDPath, "rb");
        if (!hVHD->Parent) {
            Status = errno;
            goto Cleanup_Return;
            }
        Status = GetVHDFooter (hVHD->ParentVHDPath,
                               &ParentFooter,
                               &ParentDynamic,
                               NULL,
                               &ParentModifiedTimeStamp,
                               NULL,
                               0);
        if (Status)
            goto Cleanup_Return;
        if ((0 != memcmp (hVHD->Dynamic.ParentUniqueID, ParentFooter.UniqueID, sizeof (ParentFooter.UniqueID))) || 
            (ParentModifiedTimeStamp != hVHD->Dynamic.ParentTimeStamp)) {
            if (sim_switches & SWMASK ('O')) {                      /* OVERRIDE consistency checks? */
                if ((sim_switches & SWMASK ('U')) &&                /* FIX (UPDATE) consistency checks AND */
                    (strchr (DesiredAccess, '+'))) {                /* open for write/update? */
                    memcpy (hVHD->Dynamic.ParentUniqueID, ParentFooter.UniqueID, sizeof (ParentFooter.UniqueID));
                    hVHD->Dynamic.ParentTimeStamp = ParentModifiedTimeStamp;
                    hVHD->Dynamic.Checksum = 0;
                    hVHD->Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Dynamic, sizeof(hVHD->Dynamic)));
                    NeedUpdate = TRUE;
                    }
                }
            else {
                Status = EBADF;
                goto Cleanup_Return;
                }
            }
        }
    if (hVHD->Footer.SavedState) {
        Status = EAGAIN;                                /* Busy */
        goto Cleanup_Return;
        }
    hVHD->File = sim_fopen (szVHDPath, DesiredAccess);
    if (!hVHD->File) {
        Status = errno;
        goto Cleanup_Return;
        }
Cleanup_Return:
    if (Status) {
        sim_vhd_disk_close ((FILE *)hVHD);
        hVHD = NULL;
        }
    else {
        if (NeedUpdate) {                               /* Update Differencing Disk Header? */
            if (WriteFilePosition(hVHD->File,
                                  &hVHD->Dynamic,
                                  sizeof (hVHD->Dynamic),
                                  NULL,
                                  NtoHll (hVHD->Footer.DataOffset))) {
                sim_vhd_disk_close ((FILE *)hVHD);
                hVHD = NULL;
                }
            }
        }
    errno = Status;
    return (FILE *)hVHD;
    }

static t_stat
WriteVirtualDiskSectors(VHDHANDLE hVHD,
                        uint8 *buf,
                        t_seccnt sects,
                        t_seccnt *sectswritten,
                        uint32 SectorSize,
                        t_lba lba);

static FILE *sim_vhd_disk_merge (const char *szVHDPath, char **ParentVHD)
    {
    VHDHANDLE hVHD = (VHDHANDLE) calloc (1, sizeof(*hVHD));
    VHDHANDLE Parent = NULL;
    int Status;
    uint32 SectorSize, SectorsPerBlock, BlockSize, BlockNumber, BitMapBytes, BitMapSectors, BlocksToMerge, NeededBlock;
    uint64 BlockOffset;
    size_t BytesRead;
    t_seccnt SectorsWritten;
    void *BlockData = NULL;

    if (!hVHD)
        return (FILE *)hVHD;
    if (0 != (Status = GetVHDFooter (szVHDPath,
                                     &hVHD->Footer,
                                     &hVHD->Dynamic,
                                     &hVHD->BAT,
                                     NULL,
                                     hVHD->ParentVHDPath,
                                     sizeof (hVHD->ParentVHDPath))))
        goto Cleanup_Return;
    if (NtoHl (hVHD->Footer.DiskType) != VHD_DT_Differencing) {
        Status = EINVAL;
        goto Cleanup_Return;
        }
    if (hVHD->Footer.SavedState) {
        Status = EAGAIN;                                /* Busy */
        goto Cleanup_Return;
        }
    SectorSize = 512;
    BlockSize = NtoHl (hVHD->Dynamic.BlockSize);
    BlockData = malloc (BlockSize*SectorSize);
    if (NULL == BlockData) {
        Status = errno;
        goto Cleanup_Return;
        }
    Parent = (VHDHANDLE)sim_vhd_disk_open (hVHD->ParentVHDPath, "rb+");
    if (!Parent) {
        Status = errno;
        goto Cleanup_Return;
        }
    hVHD->File = sim_fopen (szVHDPath, "rb");
    if (!hVHD->File) {
        Status = errno;
        goto Cleanup_Return;
        }
    SectorsPerBlock = NtoHl (hVHD->Dynamic.BlockSize)/SectorSize;
    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);
    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));
        if ((BlockNumber*SectorsPerBlock + BlockSectors) > ((uint64)NtoHll (hVHD->Footer.CurrentSize))/SectorSize)
            BlockSectors = (uint32)(((uint64)NtoHll (hVHD->Footer.CurrentSize))/SectorSize - (BlockNumber*SectorsPerBlock));
        if (ReadFilePosition(hVHD->File,
                             BlockData,
                             SectorSize*BlockSectors,
                             &BytesRead,
                             BlockOffset))
            break;
        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));
        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));
        fclose (hVHD->File);
        hVHD->File = NULL;
        remove (szVHDPath);
        *ParentVHD = (char*) malloc (strlen (hVHD->ParentVHDPath)+1);
        strcpy (*ParentVHD, hVHD->ParentVHDPath);
        }
Cleanup_Return:
    free (BlockData);
    if (hVHD->File)
        fclose (hVHD->File);
    if (Status) {
        free (hVHD->BAT);
        free (hVHD);
        hVHD = NULL;
        sim_vhd_disk_close ((FILE *)Parent);
        }
    else {
        free (hVHD->BAT);
        free (hVHD);
        hVHD = Parent;
        }
    errno = Status;
    return (FILE *)hVHD;
    }

static int sim_vhd_disk_close (FILE *f)
{
VHDHANDLE hVHD = (VHDHANDLE)f;

if (NULL != hVHD) {
    if (hVHD->Parent)
        sim_vhd_disk_close ((FILE *)hVHD->Parent);
    free (hVHD->BAT);
    if (hVHD->File) {
        fflush (hVHD->File);
        fclose (hVHD->File);
        }
    free (hVHD);
    return 0;
    }
return -1;
}

static void sim_vhd_disk_flush (FILE *f)
{
VHDHANDLE hVHD = (VHDHANDLE)f;

if ((NULL != hVHD) && (hVHD->File))
    fflush (hVHD->File);
}

static t_offset sim_vhd_disk_size (FILE *f)
{
VHDHANDLE hVHD = (VHDHANDLE)f;

return (t_offset)(NtoHll (hVHD->Footer.CurrentSize));
}

#include <stdlib.h>
#include <time.h>
static void
_rand_uuid_gen (void *uuidaddr)
{
int i;
uint8 *b = (uint8 *)uuidaddr;
uint32 timenow = (uint32)time (NULL);

memcpy (uuidaddr, &timenow, sizeof (timenow));
srand ((unsigned)timenow);
for (i=4; i<16; i++) {
    b[i] = (uint8)rand();
    }
}

#if defined (_WIN32)
static void
uuid_gen (void *uuidaddr)
{
static
RPC_STATUS
(RPC_ENTRY *UuidCreate_c) (void *);

if (!UuidCreate_c) {
    HINSTANCE hDll;
    hDll = LoadLibraryA("rpcrt4.dll");
    UuidCreate_c = (RPC_STATUS (RPC_ENTRY *) (void *))GetProcAddress(hDll, "UuidCreate");
    }
if (UuidCreate_c)
    UuidCreate_c(uuidaddr);
else
    _rand_uuid_gen (uuidaddr);
}
#elif defined (HAVE_DLOPEN)
#include <dlfcn.h>

static void
uuid_gen (void *uuidaddr)
{
void (*uuid_generate_c) (void *) = NULL;
void *handle;

#define S__STR_QUOTE(tok) #tok
#define S__STR(tok) S__STR_QUOTE(tok)
    handle = dlopen("libuuid." S__STR(HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL);
    if (handle)
        uuid_generate_c = (void (*)(void *))((size_t)dlsym(handle, "uuid_generate"));
if (uuid_generate_c)
    uuid_generate_c(uuidaddr);
else
    _rand_uuid_gen (uuidaddr);
    if (handle)
        dlclose(handle);
}
#else
static void
uuid_gen (void *uuidaddr)
{
_rand_uuid_gen (uuidaddr);
}
#endif

static VHDHANDLE
CreateVirtualDisk(const char *szVHDPath,
                  uint32 SizeInSectors,
                  uint32 BlockSize,
                  t_bool bFixedVHD)
{
VHD_Footer Footer;
VHD_DynamicDiskHeader Dynamic;
uint32 *BAT = NULL;
time_t now;
uint32 i;
FILE *File = NULL;
uint32 Status = 0;
uint32 BytesPerSector = 512;
uint64 SizeInBytes = ((uint64)SizeInSectors)*BytesPerSector;
uint64 TableOffset;
uint32 MaxTableEntries;
VHDHANDLE hVHD = NULL;

if (SizeInBytes > ((uint64)(1024*1024*1024))*2040) {
    Status = EFBIG;
    goto Cleanup_Return;
    }
File = sim_fopen (szVHDPath, "rb");
if (File) {
    fclose (File);
    File = NULL;
    Status = EEXIST;
    goto Cleanup_Return;
    }
File = sim_fopen (szVHDPath, "wb");
if (!File) {
    Status = errno;
    goto Cleanup_Return;
    }

memset (&Footer, 0, sizeof(Footer));
memcpy (Footer.Cookie, "conectix", 8);
Footer.Features = NtoHl (0x00000002);;
Footer.FileFormatVersion = NtoHl (0x00010000);;
Footer.DataOffset = NtoHll (bFixedVHD ? ((long long)-1) : (long long)(sizeof(Footer)));
time (&now);
Footer.TimeStamp = NtoHl ((uint32)(now-946684800));
memcpy (Footer.CreatorApplication, "simh", 4);
Footer.CreatorVersion = NtoHl (0x00040000);
memcpy (Footer.CreatorHostOS, "Wi2k", 4);
Footer.OriginalSize = NtoHll (SizeInBytes);
Footer.CurrentSize = NtoHll (SizeInBytes);
uuid_gen (Footer.UniqueID);
Footer.DiskType = NtoHl (bFixedVHD ? VHD_DT_Fixed : VHD_DT_Dynamic);
Footer.DiskGeometry = NtoHl (0xFFFF10FF);
if (1) { /* CHS Calculation */
    uint32 totalSectors = (uint32)(SizeInBytes/BytesPerSector);/* Total data sectors present in the disk image */
    uint32 cylinders;                                          /* Number of cylinders present on the disk */
    uint32 heads;                                              /* Number of heads present on the disk */
    uint32 sectorsPerTrack;                                    /* Sectors per track on the disk */
    uint32 cylinderTimesHeads;                                 /* Cylinders x heads */

    if (totalSectors > 65535 * 16 * 255)
        totalSectors = 65535 * 16 * 255;

    if (totalSectors >= 65535 * 16 * 63) {
        sectorsPerTrack = 255;
        heads = 16;
        cylinderTimesHeads = totalSectors / sectorsPerTrack;
        }
    else {
        sectorsPerTrack = 17;
        cylinderTimesHeads = totalSectors / sectorsPerTrack;

        heads = (cylinderTimesHeads + 1023) / 1024;

        if (heads < 4)
            heads = 4;
        if (cylinderTimesHeads >= (heads * 1024) || heads > 16)
            {
            sectorsPerTrack = 31;
            heads = 16;
            cylinderTimesHeads = totalSectors / sectorsPerTrack;
            }
        if (cylinderTimesHeads >= (heads * 1024))
            {
            sectorsPerTrack = 63;
            heads = 16;
            cylinderTimesHeads = totalSectors / sectorsPerTrack;
            }
        }
    cylinders = cylinderTimesHeads / heads;
    Footer.DiskGeometry = NtoHl ((cylinders<<16)|(heads<<8)|sectorsPerTrack);
    }
Footer.Checksum = NtoHl (CalculateVhdFooterChecksum(&Footer, sizeof(Footer)));

if (bFixedVHD) {
    if (WriteFilePosition(File,
                          &Footer,
                          sizeof(Footer),
                          NULL,
                          SizeInBytes))
        Status = errno;
    goto Cleanup_Return;
    }

/* Dynamic Disk */
memset (&Dynamic, 0, sizeof(Dynamic));
memcpy (Dynamic.Cookie, "cxsparse", 8);
Dynamic.DataOffset = NtoHll ((uint64)0xFFFFFFFFFFFFFFFFLL);
TableOffset = NtoHll(Footer.DataOffset)+sizeof(Dynamic);
Dynamic.TableOffset = NtoHll (TableOffset);
Dynamic.HeaderVersion = NtoHl (0x00010000);
if (0 == BlockSize)
    BlockSize = 2*1024*1024;
Dynamic.BlockSize = NtoHl (BlockSize);
MaxTableEntries = (uint32)((SizeInBytes+BlockSize-1)/BlockSize);
Dynamic.MaxTableEntries = NtoHl (MaxTableEntries);
Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum(&Dynamic, sizeof(Dynamic)));
BAT = (uint32*) malloc (BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector));
memset (BAT, 0, BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector));
for (i=0; i<MaxTableEntries; ++i)
    BAT[i] = VHD_BAT_FREE_ENTRY;

if (WriteFilePosition(File,
                      &Footer,
                      sizeof(Footer),
                      NULL,
                      0)) {
    Status = errno;
    goto Cleanup_Return;
    }
if (WriteFilePosition(File,
                      &Dynamic,
                      sizeof(Dynamic),
                      NULL,
                      NtoHll(Footer.DataOffset))) {
    Status = errno;
    goto Cleanup_Return;
    }
if (WriteFilePosition(File,
                      BAT,
                      BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector),
                      NULL,
                      NtoHll(Dynamic.TableOffset))) {
    Status = errno;
    goto Cleanup_Return;
    }
if (WriteFilePosition(File,
                      &Footer,
                      sizeof(Footer),
                      NULL,
                      NtoHll(Dynamic.TableOffset)+BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector))) {
    Status = errno;
    goto Cleanup_Return;
    }

Cleanup_Return:
free (BAT);
if (File)
    fclose (File);
if (Status) {
    if (Status != EEXIST)
        remove (szVHDPath);
    }
else {
    hVHD = (VHDHANDLE)sim_vhd_disk_open (szVHDPath, "rb+");
    if (!hVHD)
        Status = errno;
    }
errno = Status;
return hVHD;
}

#if defined(__CYGWIN__) || defined(VMS) || defined(__APPLE__) || defined(__linux) || defined(__linux__) || defined(__unix__)
#include <unistd.h>
#endif
static void
ExpandToFullPath (const char *szFileSpec,
                  char *szFullFileSpecBuffer,
                  size_t BufferSize)
{
char *c;
#ifdef _WIN32
for (c = strchr (szFullFileSpecBuffer, '/'); c; c = strchr (szFullFileSpecBuffer, '/'))
    *c = '\\';
GetFullPathNameA (szFileSpec, (DWORD)BufferSize, szFullFileSpecBuffer, NULL);
for (c = strchr (szFullFileSpecBuffer, '\\'); c; c = strchr (szFullFileSpecBuffer, '\\'))
    *c = '/';
#else
char buffer[PATH_MAX];
char *wd = getcwd(buffer, PATH_MAX);

if ((szFileSpec[0] != '/') || (strchr (szFileSpec, ':')))
    snprintf (szFullFileSpecBuffer, BufferSize, "%s/%s", wd, szFileSpec);
else
    strncpy (szFullFileSpecBuffer, szFileSpec, BufferSize);
if ((c = strstr (szFullFileSpecBuffer, "]/")))
    memcpy (c+1, c+2, strlen(c+2)+1);
memset (szFullFileSpecBuffer + strlen (szFullFileSpecBuffer), 0, BufferSize - strlen (szFullFileSpecBuffer));
#endif
}

static char *
HostPathToVhdPath (const char *szHostPath,
                   char *szVhdPath,
                   size_t VhdPathSize)
{
char *c, *d;

strncpy (szVhdPath, szHostPath, VhdPathSize-1);
if ((szVhdPath[1] == ':') && islower(szVhdPath[0]))
    szVhdPath[0] = toupper(szVhdPath[0]);
szVhdPath[VhdPathSize-1] = '\0';
if ((c = strrchr (szVhdPath, ']'))) {
    *c = '\0';
    if (!(d = strchr (szVhdPath, '[')))
        return d;
    *d = '/';
    while ((d = strchr (d, '.')))
        *d = '/';
    *c = '/';
    }
while ((c = strchr (szVhdPath, '/')))
    *c = '\\';
for (c = strstr (szVhdPath, "\\.\\"); c; c = strstr (szVhdPath, "\\.\\"))
    memcpy (c, c+2, strlen(c+2)+1);
for (c = strstr (szVhdPath, "\\\\"); c; c = strstr (szVhdPath, "\\\\"))
    memcpy (c, c+1, strlen(c+1)+1);
while ((c = strstr (szVhdPath, "\\..\\"))) {
    *c = '\0';
    d = strrchr (szVhdPath, '\\');
    if (d)
        memcpy (d, c+3, strlen(c+3)+1);
    else
        return d;
    }
memset (szVhdPath + strlen (szVhdPath), 0, VhdPathSize - strlen (szVhdPath));
return szVhdPath;
}

static char *
VhdPathToHostPath (const char *szVhdPath,
                   char *szHostPath,
                   size_t HostPathSize)
{
char *c;
char *d = szHostPath;

strncpy (szHostPath, szVhdPath, HostPathSize-1);
szHostPath[HostPathSize-1] = '\0';
#if defined(VMS)
c = strchr (szVhdPath, ':');
if (*(c+1) != '\\')
    return NULL;
*(c+1) = '[';
d = strrchr (c+2, '\\');
if (d) {
    *d = ']';
    while ((d = strrchr (c+2, '\\')))
        *d = '.';
    }
else
    return NULL;
#else
while ((c = strchr (d, '\\')))
    *c = '/';
#endif
memset (szHostPath + strlen (szHostPath), 0, HostPathSize - strlen (szHostPath));
return szHostPath;
}

static VHDHANDLE
CreateDifferencingVirtualDisk(const char *szVHDPath,
                              const char *szParentVHDPath)
{
uint32 BytesPerSector = 512;
VHDHANDLE hVHD = NULL;
VHD_Footer ParentFooter;
VHD_DynamicDiskHeader ParentDynamic;
uint32 ParentTimeStamp;
uint32 Status = 0;
char *RelativeParentVHDPath = NULL;
char *FullParentVHDPath = NULL;
char *RelativeParentVHDPathUnicode = NULL;
char *FullParentVHDPathUnicode = NULL;
char *FullVHDPath = NULL;
char *TempPath = NULL;
size_t i, RelativeMatch, UpDirectories, LocatorsWritten = 0;
int64 LocatorPosition;

if ((Status = GetVHDFooter (szParentVHDPath,
                            &ParentFooter,
                            &ParentDynamic,
                            NULL,
                            &ParentTimeStamp,
                            NULL,
                            0)))
    goto Cleanup_Return;
hVHD = CreateVirtualDisk (szVHDPath,
                          (uint32)(NtoHll(ParentFooter.CurrentSize)/BytesPerSector),
                          NtoHl(ParentDynamic.BlockSize),
                          FALSE);
if (!hVHD) {
    Status = errno;
    goto Cleanup_Return;
    }
LocatorPosition = ((sizeof (hVHD->Footer) + BytesPerSector - 1)/BytesPerSector + (sizeof (hVHD->Dynamic) + BytesPerSector - 1)/BytesPerSector)*BytesPerSector;
hVHD->Dynamic.Checksum = 0;
RelativeParentVHDPath = (char*) calloc (1, BytesPerSector+2);
FullParentVHDPath = (char*) calloc (1, BytesPerSector+2);
RelativeParentVHDPathUnicode = (char*) calloc (1, BytesPerSector+2);
FullParentVHDPathUnicode = (char*) calloc (1, BytesPerSector+2);
FullVHDPath = (char*) calloc (1, BytesPerSector+2);
TempPath = (char*) calloc (1, BytesPerSector+2);
ExpandToFullPath (szParentVHDPath, TempPath, BytesPerSector);
HostPathToVhdPath (TempPath, FullParentVHDPath, BytesPerSector);
for (i=0; i < strlen (FullParentVHDPath); i++)
    hVHD->Dynamic.ParentUnicodeName[i*2+1] = FullParentVHDPath[i];  /* Big Endian Unicode */
for (i=0; i < strlen (FullParentVHDPath); i++)
    FullParentVHDPathUnicode[i*2] = FullParentVHDPath[i];           /* Little Endian Unicode */
ExpandToFullPath (szVHDPath, TempPath, BytesPerSector);
HostPathToVhdPath (TempPath, FullVHDPath, BytesPerSector);
for (i=0, RelativeMatch=UpDirectories=0; i<strlen(FullVHDPath); i++)
    if (FullVHDPath[i] == '\\') {
        if (memcmp (FullVHDPath, FullParentVHDPath, i+1))
            ++UpDirectories;
        else
            RelativeMatch = i;
        }
if (RelativeMatch) {
    char UpDir[4] = "..\\";

    UpDir[2] = FullParentVHDPath[RelativeMatch];
    if (UpDirectories)
        for (i=0; i<UpDirectories; i++)
            strcpy (RelativeParentVHDPath+strlen (RelativeParentVHDPath), UpDir);
    else
        strcpy (RelativeParentVHDPath+strlen (RelativeParentVHDPath), UpDir+1);
    strcpy (RelativeParentVHDPath+strlen (RelativeParentVHDPath), &FullParentVHDPath[RelativeMatch+1]);
    }
for (i=0; i < strlen(RelativeParentVHDPath); i++)
    RelativeParentVHDPathUnicode[i*2] = RelativeParentVHDPath[i];
hVHD->Dynamic.ParentTimeStamp = ParentTimeStamp;
memcpy (hVHD->Dynamic.ParentUniqueID, ParentFooter.UniqueID, sizeof (hVHD->Dynamic.ParentUniqueID));
/* There are two potential parent locators on current vhds */
memcpy (hVHD->Dynamic.ParentLocatorEntries[0].PlatformCode, "W2ku", 4);
hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataSpace = NtoHl (BytesPerSector);
hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataLength = NtoHl ((uint32)(2*strlen(FullParentVHDPath)));
hVHD->Dynamic.ParentLocatorEntries[0].Reserved = 0;
hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector);
++LocatorsWritten;
if (RelativeMatch) {
    memcpy (hVHD->Dynamic.ParentLocatorEntries[1].PlatformCode, "W2ru", 4);
    hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataSpace = NtoHl (BytesPerSector);
    hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataLength = NtoHl ((uint32)(2*strlen(RelativeParentVHDPath)));
    hVHD->Dynamic.ParentLocatorEntries[1].Reserved = 0;
    hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector);
    ++LocatorsWritten;
    }
hVHD->Dynamic.TableOffset = NtoHll (((LocatorPosition+LocatorsWritten*BytesPerSector + VHD_DATA_BLOCK_ALIGNMENT - 1)/VHD_DATA_BLOCK_ALIGNMENT)*VHD_DATA_BLOCK_ALIGNMENT);
hVHD->Dynamic.Checksum = 0;
hVHD->Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Dynamic, sizeof(hVHD->Dynamic)));
hVHD->Footer.Checksum = 0;
hVHD->Footer.DiskType = NtoHl (VHD_DT_Differencing);
memcpy (hVHD->Footer.DriveType, ParentFooter.DriveType, sizeof (hVHD->Footer.DriveType));
hVHD->Footer.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Footer, sizeof(hVHD->Footer)));

if (WriteFilePosition (hVHD->File,
                       &hVHD->Footer,
                       sizeof (hVHD->Footer),
                       NULL,
                       0)) {
    Status = errno;
    goto Cleanup_Return;
    }
if (WriteFilePosition (hVHD->File,
                       &hVHD->Dynamic,
                       sizeof (hVHD->Dynamic),
                       NULL,
                       NtoHll (hVHD->Footer.DataOffset))) {
    Status = errno;
    goto Cleanup_Return;
    }
if (WriteFilePosition (hVHD->File,
                       hVHD->BAT,
                       BytesPerSector*((NtoHl (hVHD->Dynamic.MaxTableEntries)*sizeof(*hVHD->BAT)+BytesPerSector-1)/BytesPerSector),
                       NULL,
                       NtoHll (hVHD->Dynamic.TableOffset))) {
    Status = errno;
    goto Cleanup_Return;
    }
if (WriteFilePosition (hVHD->File,
                       &hVHD->Footer,
                       sizeof (hVHD->Footer),
                       NULL,
                       NtoHll (hVHD->Dynamic.TableOffset)+BytesPerSector*((NtoHl (hVHD->Dynamic.MaxTableEntries)*sizeof(*hVHD->BAT)+BytesPerSector-1)/BytesPerSector))) {
    Status = errno;
    goto Cleanup_Return;
    }
if (hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataLength)
    if (WriteFilePosition (hVHD->File,
                           FullParentVHDPathUnicode,
                           BytesPerSector,
                           NULL,
                           NtoHll (hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataOffset))) {
        Status = errno;
        goto Cleanup_Return;
        }
if (hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataLength)
    if (WriteFilePosition (hVHD->File,
                           RelativeParentVHDPathUnicode,
                           BytesPerSector,
                           NULL,
                           NtoHll (hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataOffset))) {
        Status = errno;
        goto Cleanup_Return;
        }

Cleanup_Return:
free (RelativeParentVHDPath);
free (FullParentVHDPath);
free (RelativeParentVHDPathUnicode);
free (FullParentVHDPathUnicode);
free (FullVHDPath);
free (TempPath);
sim_vhd_disk_close ((FILE *)hVHD);
hVHD = NULL;
if (Status) {
    if ((EEXIST != Status) && (ENOENT != Status))
        remove (szVHDPath);
    }
else {
    hVHD = (VHDHANDLE)sim_vhd_disk_open (szVHDPath, "rb+");
    if (!hVHD)
        Status = errno;
    }
errno = Status;
return hVHD;
}

static FILE *sim_vhd_disk_create (const char *szVHDPath, t_offset desiredsize)
{
return (FILE *)CreateVirtualDisk (szVHDPath, (uint32)(desiredsize/512), 0, (sim_switches & SWMASK ('X')));
}

static FILE *sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath)
{
return (FILE *)CreateDifferencingVirtualDisk (szVHDPath, szParentVHDPath);
}

static t_stat
ReadVirtualDiskSectors(VHDHANDLE hVHD,
                       uint8 *buf,
                       t_seccnt sects,
                       t_seccnt *sectsread,
                       uint32 SectorSize,
                       t_lba lba)
{
uint64 BlockOffset = ((uint64)lba)*SectorSize;
uint32 BlocksRead = 0;
uint32 SectorsInRead;
size_t BytesRead = 0;

if (!hVHD || (hVHD->File == NULL)) {
    errno = EBADF;
    return SCPE_IOERR;
    }
if ((BlockOffset + sects*SectorSize) > (uint64)NtoHll (hVHD->Footer.CurrentSize)) {
    errno = ERANGE;
    return SCPE_IOERR;
    }
if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Fixed) {
    if (ReadFilePosition(hVHD->File,
                         buf,
                         sects*SectorSize,
                         &BytesRead,
                         BlockOffset)) {
        if (sectsread)
            *sectsread = (t_seccnt)(BytesRead/SectorSize);
        return SCPE_IOERR;
        }
    if (sectsread)
        *sectsread /= SectorSize;
    return SCPE_OK;
    }
/* We are now dealing with a Dynamically expanding or differencing disk */
while (sects) {
    uint32 SectorsPerBlock = NtoHl (hVHD->Dynamic.BlockSize)/SectorSize;
    uint64 BlockNumber = lba/SectorsPerBlock;
    uint32 BitMapBytes = (7+(NtoHl (hVHD->Dynamic.BlockSize)/SectorSize))/8;
    uint32 BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize;

    SectorsInRead = SectorsPerBlock - lba%SectorsPerBlock;
    if (SectorsInRead > sects)
        SectorsInRead = sects;
    if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) {
        if (!hVHD->Parent)
            memset (buf, 0, SectorSize*SectorsInRead);
        else {
            if (ReadVirtualDiskSectors(hVHD->Parent,
                                       buf,
                                       SectorsInRead,
                                       NULL,
                                       SectorSize,
                                       lba)) {
                if (sectsread)
                    *sectsread = BlocksRead;
                return FALSE;
                }
            }
        }
    else {
        BlockOffset = SectorSize*((uint64)(NtoHl (hVHD->BAT[BlockNumber]) + lba%SectorsPerBlock + BitMapSectors));
        if (ReadFilePosition(hVHD->File,
                             buf,
                             SectorsInRead*SectorSize,
                             NULL,
                             BlockOffset)) {
            if (sectsread)
                *sectsread = BlocksRead;
            return SCPE_IOERR;
            }
        }
    sects -= SectorsInRead;
    buf = (uint8 *)(((char *)buf) + SectorSize*SectorsInRead);
    lba += SectorsInRead;
    BlocksRead += SectorsInRead;
    }
if (sectsread)
    *sectsread = BlocksRead;
return SCPE_OK;
}

static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
{
VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

return ReadVirtualDiskSectors(hVHD, buf, sects, sectsread, ctx->sector_size, lba);
}

static t_stat sim_vhd_disk_clearerr (UNIT *uptr)
{
VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref;

clearerr (hVHD->File);
return SCPE_OK;
}

static t_bool
BufferIsZeros(void *Buffer, size_t BufferSize)
{
size_t i;
char *c = (char *)Buffer;

for (i=0; i<BufferSize; ++i)
    if (c[i])
        return FALSE;
return TRUE;
}

static t_stat
WriteVirtualDiskSectors(VHDHANDLE hVHD,
                        uint8 *buf,
                        t_seccnt sects,
                        t_seccnt *sectswritten,
                        uint32 SectorSize,
                        t_lba lba)
{
uint64 BlockOffset = ((uint64)lba)*SectorSize;
uint32 BlocksWritten = 0;
uint32 SectorsInWrite;
size_t BytesWritten = 0;

if (!hVHD || !hVHD->File) {
    errno = EBADF;
    return SCPE_IOERR;
    }
if ((BlockOffset + sects*SectorSize) > (uint64)NtoHll(hVHD->Footer.CurrentSize)) {
    errno = ERANGE;
    return SCPE_IOERR;
    }
if (NtoHl(hVHD->Footer.DiskType) == VHD_DT_Fixed) {
    if (WriteFilePosition(hVHD->File,
                          buf,
                          sects*SectorSize,
                          &BytesWritten,
                          BlockOffset)) {
        if (sectswritten)
            *sectswritten = (t_seccnt)(BytesWritten/SectorSize);
        return SCPE_IOERR;
        }
    if (sectswritten)
        *sectswritten /= SectorSize;
    return SCPE_OK;
    }
/* We are now dealing with a Dynamically expanding or differencing disk */
while (sects) {
    uint32 SectorsPerBlock = NtoHl(hVHD->Dynamic.BlockSize)/SectorSize;
    uint64 BlockNumber = lba/SectorsPerBlock;
    uint32 BitMapBytes = (7+(NtoHl(hVHD->Dynamic.BlockSize)/SectorSize))/8;
    uint32 BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize;

    if (BlockNumber >= NtoHl(hVHD->Dynamic.MaxTableEntries)) {
        if (sectswritten)
            *sectswritten = BlocksWritten;
        return SCPE_EOF;
        }
    SectorsInWrite = 1;
    if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) {
        uint8 *BitMap = NULL;
        uint32 BitMapBufferSize = VHD_DATA_BLOCK_ALIGNMENT;
        uint8 *BitMapBuffer = NULL;
        void *BlockData = NULL;
        uint8 *BATUpdateBufferAddress;
        uint32 BATUpdateBufferSize;
        uint64 BATUpdateStorageAddress;

        if (!hVHD->Parent && BufferIsZeros(buf, SectorSize))
            goto IO_Done;
        /* Need to allocate a new Data Block. */
        BlockOffset = sim_fsize_ex (hVHD->File);
        if (((int64)BlockOffset) == -1)
            return SCPE_IOERR;
        if (BitMapSectors*SectorSize > BitMapBufferSize)
            BitMapBufferSize = BitMapSectors*SectorSize;
        BitMapBuffer = (uint8 *)calloc(1, BitMapBufferSize + SectorSize*SectorsPerBlock);
        if (BitMapBufferSize > BitMapSectors*SectorSize)
            BitMap = BitMapBuffer + BitMapBufferSize-BitMapBytes;
        else
            BitMap = BitMapBuffer;
        memset(BitMap, 0xFF, BitMapBytes);
        BlockOffset -= sizeof(hVHD->Footer);
        if (0 == (BlockOffset & ~(VHD_DATA_BLOCK_ALIGNMENT-1)))
            {  // Already aligned, so use padded BitMapBuffer
            if (WriteFilePosition(hVHD->File,
                                  BitMapBuffer,
                                  BitMapBufferSize + SectorSize*SectorsPerBlock,
                                  NULL,
                                  BlockOffset)) {
                free (BitMapBuffer);
                return SCPE_IOERR;
                }
            BlockOffset += BitMapBufferSize;
            }
        else
            {
            // align the data portion of the block to the desired alignment
            // compute the address of the data portion of the block
            BlockOffset += BitMapSectors*SectorSize;
            // round up this address to the desired alignment
            BlockOffset += VHD_DATA_BLOCK_ALIGNMENT-1;
            BlockOffset &= ~(VHD_DATA_BLOCK_ALIGNMENT-1);
            BlockOffset -= BitMapSectors*SectorSize;
            if (WriteFilePosition(hVHD->File,
                                  BitMap,
                                  SectorSize * (BitMapSectors + SectorsPerBlock),
                                  NULL,
                                  BlockOffset)) {
                free (BitMapBuffer);
                return SCPE_IOERR;
                }
            BlockOffset += BitMapSectors*SectorSize;
            }
        free(BitMapBuffer);
        BitMapBuffer = BitMap = NULL;
        /* the BAT block address is the beginning of the block bitmap */
        BlockOffset -= BitMapSectors*SectorSize;
        hVHD->BAT[BlockNumber] = NtoHl((uint32)(BlockOffset/SectorSize));
        BlockOffset += SectorSize * (SectorsPerBlock + BitMapSectors);
        if (WriteFilePosition(hVHD->File,
                              &hVHD->Footer,
                              sizeof(hVHD->Footer),
                              NULL,
                              BlockOffset))
            goto Fatal_IO_Error;
        /* Since a large VHD can have a pretty large BAT, and we've only changed one longword bat entry
           in the current BAT, we write just the aligned sector which contains the updated BAT entry */
        BATUpdateBufferAddress = (uint8 *)hVHD->BAT - (size_t)NtoHll(hVHD->Dynamic.TableOffset) + 
            (size_t)((((size_t)&hVHD->BAT[BlockNumber]) - (size_t)hVHD->BAT + (size_t)NtoHll(hVHD->Dynamic.TableOffset)) & ~(VHD_DATA_BLOCK_ALIGNMENT-1));
        /* If the starting of the BAT isn't on a VHD_DATA_BLOCK_ALIGNMENT boundary and we've just updated 
           a BAT entry early in the array, the buffer computed address might be before the start of the
           BAT table.  If so, only write the BAT data needed */
        if (BATUpdateBufferAddress < (uint8 *)hVHD->BAT) {
            BATUpdateBufferAddress = (uint8 *)hVHD->BAT;
            BATUpdateBufferSize = (uint32)((((size_t)&hVHD->BAT[BlockNumber]) - (size_t)hVHD->BAT) + 512) & ~511;
            BATUpdateStorageAddress = NtoHll(hVHD->Dynamic.TableOffset);
            }
        else {
            BATUpdateBufferSize = VHD_DATA_BLOCK_ALIGNMENT;
            BATUpdateStorageAddress = NtoHll(hVHD->Dynamic.TableOffset) + BATUpdateBufferAddress - ((uint8 *)hVHD->BAT);
            }
        /* If the total BAT is smaller than one VHD_DATA_BLOCK_ALIGNMENT, then be sure to only write out the BAT data */
        if ((size_t)(BATUpdateBufferAddress - (uint8 *)hVHD->BAT + BATUpdateBufferSize) > 512*((sizeof(*hVHD->BAT)*NtoHl(hVHD->Dynamic.MaxTableEntries) + 511)/512))
            BATUpdateBufferSize = (uint32)(512*((sizeof(*hVHD->BAT)*NtoHl(hVHD->Dynamic.MaxTableEntries) + 511)/512) - (BATUpdateBufferAddress - ((uint8 *)hVHD->BAT)));
        if (WriteFilePosition(hVHD->File,
                              BATUpdateBufferAddress,
                              BATUpdateBufferSize,
                              NULL,
                              BATUpdateStorageAddress))
            goto Fatal_IO_Error;
        if (hVHD->Parent)
            { /* Need to populate data block contents from parent VHD */
            uint32 BlockSectors = SectorsPerBlock;

            BlockData = malloc(SectorsPerBlock*SectorSize);

            if (((lba/SectorsPerBlock)*SectorsPerBlock + BlockSectors) > ((uint64)NtoHll (hVHD->Footer.CurrentSize))/SectorSize)
                BlockSectors = (uint32)(((uint64)NtoHll (hVHD->Footer.CurrentSize))/SectorSize - (lba/SectorsPerBlock)*SectorsPerBlock);
            if (ReadVirtualDiskSectors(hVHD->Parent,
                                       (uint8*) BlockData,
                                       BlockSectors,
                                       NULL,
                                       SectorSize,
                                       (lba/SectorsPerBlock)*SectorsPerBlock))
                goto Fatal_IO_Error;
            if (WriteVirtualDiskSectors(hVHD,
                                        (uint8*) BlockData,
                                        BlockSectors,
                                        NULL,
                                        SectorSize,
                                        (lba/SectorsPerBlock)*SectorsPerBlock))
                goto Fatal_IO_Error;
            free(BlockData);
            }
        continue;
Fatal_IO_Error:
        free (BitMap);
        free (BlockData);
        fclose (hVHD->File);
        hVHD->File = NULL;
        return SCPE_IOERR;
        }
    else {
        BlockOffset = 512*((uint64)(NtoHl(hVHD->BAT[BlockNumber]) + lba%SectorsPerBlock + BitMapSectors));
        SectorsInWrite = SectorsPerBlock - lba%SectorsPerBlock;
        if (SectorsInWrite > sects)
            SectorsInWrite = sects;
        if (WriteFilePosition(hVHD->File,
                              buf,
                              SectorsInWrite*SectorSize,
                              NULL,
                              BlockOffset)) {
            if (sectswritten)
                *sectswritten = BlocksWritten;
            return SCPE_IOERR;
            }
        }
IO_Done:
    sects -= SectorsInWrite;
    buf = (uint8 *)(((char *)buf) + SectorsInWrite*SectorSize);
    lba += SectorsInWrite;
    BlocksWritten += SectorsInWrite;
    }
if (sectswritten)
    *sectswritten = BlocksWritten;
return SCPE_OK;
}

static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
{
VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;

return WriteVirtualDiskSectors(hVHD, buf, sects, sectswritten, ctx->sector_size, lba);
}
#endif
Added src/sim_disk.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_disk.h: simulator disk support library definitions

   Copyright (c) 2011, 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:

   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
   ROBERT M SUPNIK 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 Robert M Supnik and 
   Mark Pizzolato shall not be used in advertising or otherwise to promote
   the sale, use or other dealings in this Software without prior written 
   authorization from Robert M Supnik and Mark Pizzolato.

   25-Jan-11    MP      Initial Implemementation
*/

#ifndef SIM_DISK_H_
#define SIM_DISK_H_    0

#ifdef  __cplusplus
extern "C" {
#endif

/* SIMH/Disk format */

typedef uint32          t_seccnt;                       /* disk sector count */
typedef uint32          t_lba;                          /* disk logical block address */

/* Unit flags */

#define DKUF_V_WLK      (UNIT_V_UF + 0)                 /* write locked */
#define DKUF_V_FMT      (UNIT_V_UF + 1)                 /* disk file format */
#define DKUF_W_FMT      2                               /* 2b of formats */
#define DKUF_N_FMT      (1u << DKUF_W_FMT)              /* number of formats */
#define DKUF_M_FMT      ((1u << DKUF_W_FMT) - 1)
#define DKUF_F_STD       0                              /* SIMH format */
#define DKUF_F_RAW       1                              /* Raw Physical Disk Access */
#define DKUF_F_VHD       2                              /* VHD format */
#define DKUF_V_UF       (DKUF_V_FMT + DKUF_W_FMT)
#define DKUF_WLK        (1u << DKUF_V_WLK)
#define DKUF_FMT        (DKUF_M_FMT << DKUF_V_FMT)
#define DKUF_WRP        (DKUF_WLK | UNIT_RO)

#define DK_F_STD        (DKUF_F_STD << DKUF_V_FMT)
#define DK_F_RAW        (DKUF_F_RAW << DKUF_V_FMT)
#define DK_F_VHD        (DKUF_F_VHD << DKUF_V_FMT)

#define DK_GET_FMT(u)   (((u)->flags >> DKUF_V_FMT) & DKUF_M_FMT)

/* Return status codes */

#define DKSE_OK         0                               /* no error */

typedef void (*DISK_PCALLBACK)(UNIT *unit, t_stat status);

/* Prototypes */

t_stat sim_disk_attach (UNIT *uptr, const char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize, 
                        uint32 debugbit, const char *drivetype, uint32 pdp11_tracksize, int completion_delay);
t_stat sim_disk_detach (UNIT *uptr);
t_stat sim_disk_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects);
t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback);
t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects);
t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback);
t_stat sim_disk_unload (UNIT *uptr);
t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat sim_disk_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat sim_disk_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat sim_disk_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat sim_disk_set_asynch (UNIT *uptr, int latency);
t_stat sim_disk_clr_asynch (UNIT *uptr);
t_stat sim_disk_reset (UNIT *uptr);
t_stat sim_disk_perror (UNIT *uptr, const char *msg);
t_stat sim_disk_clearerr (UNIT *uptr);
t_bool sim_disk_isavailable (UNIT *uptr);
t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback);
t_bool sim_disk_wrp (UNIT *uptr);
t_offset sim_disk_size (UNIT *uptr);
t_bool sim_disk_vhd_support (void);
t_bool sim_disk_raw_support (void);
void sim_disk_data_trace (UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason);

#ifdef  __cplusplus
}
#endif

#endif
Added src/sim_ether.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_ether.c: OS-dependent network routines
  ------------------------------------------------------------------------------
   Copyright (c) 2002-2007, David T. Hittner

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

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

  This ethernet simulation is based on the PCAP and WinPcap packages.

  PCAP/WinPcap was chosen as the basis for network code since it is the most
  "universal" of the various network packages available. Using this style has
  allowed rapid network development for the major SIMH platforms. Developing
  a network package specifically for SIMH was rejected due to the time required;
  the advantage would be a more easily compiled and integrated code set.

  There are various problems associated with use of ethernet networking, which
  would be true regardless of the network package used, since there are no
  universally accepted networking methods. The most serious of these is getting
  the proper networking package loaded onto the system, since most environments
  do not come with the network interface packages loaded.

  The second most serious network issue relates to security. The network
  simulation needs to simulate operating system level functionality (packet
  driving). However, the host network programming interfaces tend to operate at
  the user level of functionality, so getting to the full functionality of
  the network interface usually requires that the person executing the
  network code be a privileged user of the host system. See the PCAP/WinPcap
  documentation for the appropriate host platform if unprivileged use of
  networking is needed - there may be known workarounds.

  Define one of the two macros below to enable networking:
    USE_NETWORK - Create statically linked network code
    USE_SHARED  - Create dynamically linked network code

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

  Supported/Tested Platforms:

  Windows(NT,2K,XP,2K3,Vista,Win7)     WinPcap         V3.0+
  Linux                     libpcap at least 0.9
  OpenBSD,FreeBSD,NetBSD    libpcap at least 0.9
  MAC OS/X                  libpcap at least 0.9
  Solaris Sparc             libpcap at least 0.9
  Solaris Intel             libpcap at least 0.9
  AIX                       ??
  HP/UX                     ??
  Compaq Tru64 Unix         ??
  VMS                       Alpha/Itanium VMS only, needs VMS libpcap
  
  WinPcap is available from: 
                        http://winpcap.polito.it/
  libpcap for VMS is available from: 
                        http://simh.trailing-edge.com/sources/vms-pcap.zip
  libpcap for other Unix platforms is available at: 
        NOTE: As of the release of this version of sim_ether.c ALL current 
              *nix platforms ship with a sufficiently new version of 
              libpcap, and ALL provide a libpcap-dev package for developing
              libpcap based applications.  The OS vendor supplied version
              of libpcap AND the libpcap-dev components are preferred for
              proper operation of both simh AND other applications on the 
              host system which use libpcap.
        Current Version:  http://www.tcpdump.org/daily/libpcap-current.tar.gz
        Released Version: http://www.tcpdump.org/release/

        When necessary (see NOTE above about vendor supplied libpcap), 
        we've gotten the tarball, unpacked, built and installed it with:
            gzip -dc libpcap-current.tar.gz | tar xvf -
            cd libpcap-directory-name
            ./configure
            make
            make install
        Note:  The "make install" step generally will have to be done as root.
        This will install libpcap in /usr/local/lib and /usr/local/include
        The current simh makefile will do the right thing to locate and 
        reference the OS provided libpcap or the one just installed.


  Note: Building for the platforms indicated above, with the indicated libpcap, 
  should automatically leverage the appropriate mechanisms contained here.  
  Things are structured so that it is likely to work for any other as yet 
  untested platform.  If it works for you, please let the author know so we 
  can update the table above.  If it doesn't work, then the following #define 
  variables can influence the operation on an untested platform.

  USE_BPF           - Determines if this code leverages a libpcap/WinPcap 
                      provided bpf packet filtering facility.  All tested 
                      environments have bpf facilities that work the way we 
                      need them to.  However a new one might not.  undefine 
                      this variable to let this code do its own filtering.
  USE_SETNONBLOCK   - Specifies whether the libpcap environment's non-blocking 
                      semantics are to be leveraged.  This helps to manage the 
                      varying behaviours of the kernel packet facilities 
                      leveraged by libpcap.
  USE_READER_THREAD - Specifies that packet reading should be done in the 
                      context of a separate thread.  The Posix threading 
                      APIs are used.  This option is less efficient than the
                      default non-threaded approach, but it exists since some 
                      platforms don't want to work with nonblocking libpcap 
                      semantics.   OpenBSD and NetBSD either don't have pthread 
                      APIs available, or they are too buggy to be useful. 
                      Using the threaded approach may require special compile 
                      and/or link time switches (i.e. -lpthread or -pthread, 
                      etc.) Consult the documentation for your platform as 
                      needed.  Although this may be 'less efficient' than the
                      non-threaded approach, the efficiency is an overall system
                      efficiency not necessarily a simulator efficiency.  This 
                      means that work is removed from the thread executing 
                      simulated instructions so the simulated system will most
                      likely run faster (given that modern host CPUs are 
                      multi-core and have someplace to do this work in parallel).
  MUST_DO_SELECT    - Specifies that, when USE_READER_THREAD is active,  
                      select() should be used to determine when available 
                      packets are ready for reading.  Otherwise, we depend 
                      on the libpcap/kernel packet timeout specified on 
                      pcap_open_live.  If USE_READER_THREAD is not set, then 
                      MUST_DO_SELECT is irrelevant
  HAVE_TAP_NETWORK  - Specifies that support for tap networking should be 
                      included.  This can be leveraged, along with OS bridging
                      capabilities to share a single LAN interface.  This 
                      allows device names of the form tap:tap0 to be specified
                      at open time.  This functionality is only useful/needed 
                      on *nix platforms since native sharing of Windows NIC 
                      devices works with no external magic.
  HAVE_VDE_NETWORK  - Specifies that support for vde networking should be 
                      included.  This can be leveraged, along with OS bridging
                      capabilities to share a single LAN interface.  It also
                      can allow a simulator to have useful networking 
                      functionality when running without root access.  This 
                      allows device names of the form vde:/tmp/switch to be 
                      specified at open time.  This functionality is only 
                      available on *nix platforms since the vde api isn't 
                      available on Windows.
  HAVE_SLIRP_NETWORK- Specifies that support for SLiRP networking should be 
                      included.  This can be leveraged to provide User Mode 
                      IP NAT connectivity for simulators.

  NEED_PCAP_SENDPACKET
                    - Specifies that you are using an older version of libpcap
                      which doesn't provide a pcap_sendpacket API.

  NOTE: Changing these defines is done in either sim_ether.h OR on the global 
        compiler command line which builds all of the modules included in a
        simulator.

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

  Modification history:

  30-Mar-12  MP   Added host NIC address determination on supported VMS platforms
  01-Mar-12  MP   Made host NIC address determination on *nix platforms more 
                  robust.
  01-Mar-12  MP   Added host NIC address determination work when building 
                  under Cygwin
  01-Mar-12  AGN  Add conditionals for Cygwin dynamic loading of wpcap.dll
  01-Mar-12  AGN  Specify the full /usr/lib for dlopen under Apple Mac OS X.
  17-Nov-11  MP   Added dynamic loading of libpcap on *nix platforms
  30-Oct-11  MP   Added support for vde (Virtual Distributed Ethernet) networking
  29-Oct-11  MP   Added support for integrated Tap networking interfaces on OSX
  12-Aug-11  MP   Cleaned up payload length determination
                  Fixed race condition detecting reflections when threaded 
                  reading and writing is enabled
  18-Apr-11  MP   Fixed race condition with self loopback packets in 
                  multithreaded environments
  09-Jan-11  MP   Fixed missing crc data when USE_READER_THREAD is defined and 
                  crc's are needed (only the pdp11_xu)
  16-Dec-10  MP   added priority boost for read and write threads when 
                  USE_READER_THREAD does I/O in separate threads.  This helps
                  throughput since it allows these I/O bound threads to preempt 
                  the main thread (which is executing simulated instructions).                  
  09-Dec-10  MP   allowed more flexible parsing of MAC address strings
  09-Dec-10  MP   Added support to determine if network address conflicts exist
  07-Dec-10  MP   Reworked DECnet self detection to the more general approach
                  of loopback self when a Physical Address is being set.
  04-Dec-10  MP   Changed eth_write to do nonblocking writes when 
                  USE_READER_THREAD is defined.
  20-Aug-10  TVO  Fix for Mac OSX 10.6
  17-Jun-10  MP   Fixed bug in the AUTODIN II hash filtering.
  14-Jun-10  MP   Added support for integrated Tap networking interfaces on BSD 
                  platforms.
  13-Jun-10  MP   Added support for integrated Tap networking interfaces on Linux 
                  platforms.
  31-May-10  MP   Added support for more TOE (TCP Offload Engine) features for IPv4
                  network traffic from the host and/or from hosts on the LAN.  These
                  new TOE features are: LSO (Large Send Offload) and Jumbo packet
                  fragmentation support.  These features allow a simulated network
                  device to support traffic when a host leverages a NIC's Large 
                  Send Offload capabilities to fregment and/or segment outgoing 
                  network traffic.  Additionally a simulated network device can 
                  reasonably exist on a LAN which is configured to use Jumbo frames.
  21-May-10  MP   Added functionality to fixup IP header checksums to accomodate 
                  packets from a host with a NIC which has TOE (TCP Offload Engine)
                  enabled which is expected to implement the checksum computations
                  in hardware.  Since we catch packets before they arrive at the
                  NIC the expected checksum insertions haven't been performed yet.
                  This processing is only done for packets sent from the hoat to 
                  the guest we're supporting.  In general this will be a relatively 
                  small number of packets so it is done for all IP frame packets
                  coming from the hoat to the guest.  In order to make the 
                  determination of packets specifically arriving from the host we
                  need to know the hardware MAC address of the host NIC.  Currently
                  determining a NIC's MAC address is relatively easy on Windows.
                  The non-windows code works on linux and may work on other *nix 
                  platforms either as is or with slight modifications.  The code, 
                  as implemented, only messes with this activity if the host 
                  interface MAC address can be determined.
  20-May-10  MP   Added general support to deal with receiving packets smaller 
                  than ETH_MIN_PACKET in length.  These come from packets
                  looped back by some bridging mechanism and need to be padded
                  to the minimum frame size.  A real NIC won't pass us any 
                  packets like that.  This fix belongs here since this layer
                  is responsible for interfacing to they physical layer 
                  devices, AND it belongs here to get CRC processing right.
  05-Mar-08  MP   Added optional multicast filtering support for doing
                  LANCE style AUTODIN II based hashed filtering.
  07-Feb-08  MP   Added eth_show_dev to display ethernet state
                  Changed the return value from eth_read to return whether
                  or not a packet was read.  No existing callers used or 
                  checked constant return value that previously was being
                  supplied.
  29-Jan-08  MP   Added eth_set_async to provide a mechanism (when 
                  USE_READER_THREAD is enabled) to allow packet reception 
                  to dynamically update the simulator event queue and 
                  potentially avoid polling for I/O.  This provides a minimal 
                  overhead (no polling) maximal responsiveness for network 
                  activities.
  29-Jan-08  MP   Properly sequenced activities in eth_close to avoid a race
                  condition when USE_READER_THREAD is enabled.
  25-Jan-08  MP   Changed the following when USE_READER_THREAD is enabled:
                  - Fixed bug when the simulated device doesn't need crc 
                    in packet data which is read.
                  - Added call to pcap_setmintocopy to minimize packet 
                    delivery latencies.
                  - Added ethq_destroy and used it to avoid a memory leak in
                    eth_close.
                  - Properly cleaned up pthread mutexes in eth_close.
                  Migrated to using sim_os_ms_sleep for a delay instead of
                  a call to select().
                  Fixed the bpf filter used when no traffic is to be matched.
                  Reworked eth_add_packet_crc32 implementation to avoid an
                  extra buffer copy while reading packets.
                  Fixedup #ifdef's relating to USE_SHARED so that setting 
                  USE_SHARED or USE_NETWORK will build a working network 
                  environment.
  23-Jan-08  MP   Reworked eth_packet_trace and eth_packet_trace_ex to allow
                  only output ethernet header+crc and provide a mechanism for
                  the simulated device to display full packet data debugging.
  17-May-07  DTH  Fixed non-ethernet device removal loop (from Naoki Hamada)
  15-May-07  DTH  Added dynamic loading of wpcap.dll;
                  Corrected exceed max index bug in ethX lookup
  04-May-07  DTH  Corrected failure to look up ethernet device names in
                  the registry on Windows XP x64
  10-Jul-06  RMS  Fixed linux conditionalization (from Chaskiel Grundman)
  02-Jun-06  JDB  Fixed compiler warning for incompatible sscanf parameter
  15-Dec-05  DTH  Patched eth_host_devices [remove non-ethernet devices]
                  (from Mark Pizzolato and Galen Tackett, 08-Jun-05)
                  Patched eth_open [tun fix](from Antal Ritter, 06-Oct-05)
  30-Nov-05  DTH  Added option to regenerate CRC on received packets; some
                  ethernet devices need to pass it on to the simulation, and by
                  the time libpcap/winpcap gets the packet, the host OS network
                  layer has already stripped CRC out of the packet
  01-Dec-04  DTH  Added Windows user-defined adapter names (from Timothe Litt)
  25-Mar-04  MP   Revised comments and minor #defines to deal with updated
                  libpcap which now provides pcap_sendpacket on all platforms.
  04-Feb-04  MP   Returned success/fail status from eth_write to support
                  determining if the current libpcap connection can successfully 
                  write packets.
                  Added threaded approach to reading packets since
                  this works better on some platforms (solaris intel) than the 
                  inconsistently implemented non-blocking read approach.
  04-Feb-04  DTH  Converted ETH_DEBUG to sim_debug
  13-Jan-04  MP   tested and fixed on OpenBSD, NetBS and FreeBSD.
  09-Jan-04  MP   removed the BIOCSHDRCMPLT ioctl() for OS/X
  05-Jan-04  DTH  Added eth_mac_scan
  30-Dec-03  DTH  Cleaned up queue routines, added no network support message
  26-Dec-03  DTH  Added ethernet show and queue functions from pdp11_xq
  15-Dec-03  MP   polished generic libpcap support.
  05-Dec-03  DTH  Genericized eth_devices() and #ifdefs
  03-Dec-03  MP   Added Solaris support
  02-Dec-03  DTH  Corrected decnet fix to use reflection counting
  01-Dec-03  DTH  Added BPF source filtering and reflection counting
  28-Nov-03  DTH  Rewrote eth_devices using universal pcap_findalldevs()
  25-Nov-03  DTH  Verified DECNET_FIX, reversed ifdef to mainstream code
  19-Nov-03  MP   Fixed BPF functionality on Linux/BSD.
  17-Nov-03  DTH  Added xBSD simplification
  14-Nov-03  DTH  Added #ifdef DECNET_FIX for problematic duplicate detection code
  13-Nov-03  DTH  Merged in __FreeBSD__ support
  21-Oct-03  MP   Added enriched packet dumping for debugging
  20-Oct-03  MP   Added support for multiple ethernet devices on VMS
  20-Sep-03  Ankan Add VMS support (Alpha only)
  29-Sep-03  MP   Changed separator character in eth_fmt_mac to be ":" to
                  format ethernet addresses the way the BPF compile engine
                  wants to see them.
                  Added BPF support to filter packets
                  Added missing printf in eth_close
  07-Jun-03  MP   Added WIN32 support for DECNET duplicate address detection.
  06-Jun-03  MP   Fixed formatting of Ethernet Protocol Type in eth_packet_trace
  30-May-03  DTH  Changed WIN32 to _WIN32 for consistency
  07-Mar-03  MP   Fixed Linux implementation of PacketGetAdapterNames to also
                  work on Red Hat 6.2-sparc and Debian 3.0r1-sparc.
  03-Mar-03  MP   Changed logging to be consistent on stdout and sim_log
  01-Feb-03  MP   Changed type of local variables in eth_packet_trace to
                  conform to the interface needs of eth_mac_fmt wich produces
                  char data instead of unsigned char data.  Suggested by the
                  DECC compiler.
  15-Jan-03  DTH  Corrected PacketGetAdapterNames parameter2 datatype
  26-Dec-02  DTH  Merged Mark Pizzolato's enhancements with main source
                  Added networking documentation
                  Changed _DEBUG to ETH_DEBUG
  20-Dec-02  MP   Added display of packet CRC to the eth_packet_trace.
                  This helps distinguish packets with identical lengths
                  and protocols.
  05-Dec-02  MP   With the goal of draining the input buffer more rapidly
                  changed eth_read to call pcap_dispatch repeatedly until
                  either a timeout returns nothing or a packet allowed by
                  the filter is seen.  This more closely reflects how the
                  pcap layer will work when the filtering is actually done
                  by a bpf filter.
  31-Oct-02  DTH  Added USE_NETWORK conditional
                  Reworked not attached test
                  Added OpenBSD support (from Federico Schwindt)
                  Added ethX detection simplification (from Megan Gentry)
                  Removed sections of temporary code
                  Added parameter validation
  23-Oct-02  DTH  Beta 5 released
  22-Oct-02  DTH  Added all_multicast and promiscuous support
                  Fixed not attached behavior
  21-Oct-02  DTH  Added NetBSD support (from Jason Thorpe)
                  Patched buffer size to make sure entire packet is read in
                  Made 'ethX' check characters passed as well as length
                  Corrected copyright again
  16-Oct-02  DTH  Beta 4 released
                  Corrected copyright
  09-Oct-02  DTH  Beta 3 released
                  Added pdp11 write acceleration (from Patrick Caulfield)
  08-Oct-02  DTH  Beta 2 released
                  Integrated with 2.10-0p4
                  Added variable vector and copyrights
  04-Oct-02  DTH  Added linux support (from Patrick Caulfield)
  03-Oct-02  DTH  Beta version of xq/sim_ether released for SIMH 2.09-11
  24-Sep-02  DTH  Finished eth_devices, eth_getname
  18-Sep-02  DTH  Callbacks implemented
  13-Sep-02  DTH  Basic packet read/write written
  20-Aug-02  DTH  Created Sim_Ether for O/S independant ethernet implementation

  ------------------------------------------------------------------------------
*/

#include <ctype.h>
#include "sim_ether.h"
#include "sim_sock.h"
#include "sim_timer.h"
#if defined(_WIN32)
#include <direct.h>
#else
#include <unistd.h>
#endif

/* Internal routines - forward declarations */
static int _eth_get_system_id (char *buf, size_t buf_size);

/*============================================================================*/
/*                  OS-independant ethernet routines                          */
/*============================================================================*/

t_stat eth_mac_scan (ETH_MAC* mac, const char* strmac)
{
return eth_mac_scan_ex (mac, strmac, NULL);
}

t_stat eth_mac_scan_ex (ETH_MAC* mac, const char* strmac, UNIT *uptr)
{
  unsigned int a[6], g[6];
  FILE *f;
  char filebuf[64] = "";
  uint32 i;
  static const ETH_MAC zeros = {0,0,0,0,0,0};
  static const ETH_MAC ones  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
  ETH_MAC newmac;
  struct {
      uint32 bits;
      char system_id[37];
      char cwd[PATH_MAX];
      char file[PATH_MAX];
      ETH_MAC base_mac;
      char uname[64];
      char sim[128];
      } state;
  CONST char *cptr, *tptr;
  uint32 data;

  /* Allow generated MAC address */
  /* XX:XX:XX:XX:XX:XX{/bits{>file}} */
  /* bits (if specified) must be from 16 thru 48 */

  memset (&state, 0, sizeof(state));
  _eth_get_system_id (state.system_id, sizeof(state.system_id));
  strncpy (state.sim, sim_name, sizeof(state.sim));
  getcwd (state.cwd, sizeof(state.cwd));
  if (uptr)
    strncpy (state.uname, sim_uname (uptr), sizeof(state.uname));
  cptr = strchr (strmac, '>');
  if (cptr) {
    strncpy (state.file, cptr + 1, sizeof(state.file));
    if ((f = fopen (state.file, "r"))) {
      filebuf[sizeof(filebuf)-1] = '\0';
      fgets (filebuf, sizeof(filebuf)-1, f);
      strmac = filebuf;
      fclose (f);
      strcpy (state.file, "");  /* avoid saving */
      }
    }
  cptr = strchr (strmac, '/');
  if (cptr) {
    state.bits = (uint32)strtotv (cptr + 1, &tptr, 10);
    if ((state.bits < 16) || (state.bits > 48))
      return sim_messagef (SCPE_ARG, "Invalid MAC address bits specifier '%d'. Valid values are from 16 thru 48\n", state.bits);
    }
  else
    state.bits = 48;
  data = eth_crc32 (0, (void *)&state, sizeof(state));
  for (i=g[0]=g[1]=0; i<4; i++)
    g[i+2] = (data >> (i << 3)) & 0xFF;
  if ((6 != sscanf(strmac, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])) &&
      (6 != sscanf(strmac, "%x.%x.%x.%x.%x.%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])) &&
      (6 != sscanf(strmac, "%x-%x-%x-%x-%x-%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])))
    return sim_messagef (SCPE_ARG, "Invalid MAC address format: '%s'\n", strmac);
  for (i=0; i<6; i++)
    if (a[i] > 0xFF)
      return sim_messagef (SCPE_ARG, "Invalid MAC address byte value: %02X\n", a[i]);
    else {
      uint32 mask, shift;
    
      state.base_mac[i] = a[i];
      if (((i + 1) << 3) < state.bits)
          shift = 0;
      else
          shift = ((i + 1) << 3) - state.bits;
      mask = 0xFF << shift;
      newmac[i] = (unsigned char)((a[i] & mask) | (g[i] & ~mask));
      }

  /* final check - mac cannot be broadcast or multicast address */
  if (!memcmp(newmac, zeros, sizeof(ETH_MAC)) ||  /* broadcast */
      !memcmp(newmac, ones,  sizeof(ETH_MAC)) ||  /* broadcast */
      (newmac[0] & 0x01)                          /* multicast */
     )
    return sim_messagef (SCPE_ARG, "Can't use Broadcast or MultiCast address as interface MAC address\n");

  /* new mac is OK */
  /* optionally save */
  if (state.file[0]) {              /* Save File specified? */
    f = fopen (state.file, "w");
    if (f == NULL)
      return sim_messagef (SCPE_ARG, "Can't open MAC address configuration file '%s'.\n", state.file);
    eth_mac_fmt (&newmac, filebuf);
    fprintf (f, "%s/48\n", filebuf);
    fprintf (f, "system-id: %s\n", state.system_id);
    fprintf (f, "directory: %s\n", state.cwd);
    fprintf (f, "simulator: %s\n", state.sim);
    fprintf (f, "device:    %s\n", state.uname);
    fprintf (f, "file:      %s\n", state.file);
    eth_mac_fmt (&state.base_mac, filebuf);
    fprintf (f, "base-mac:  %s\n", filebuf);
    fprintf (f, "specified: %d bits\n", state.bits);
    fprintf (f, "generated: %d bits\n", 48-state.bits);
    fclose (f);
    }
  /* copy into passed mac */
  memcpy (*mac, newmac, sizeof(ETH_MAC));
  return SCPE_OK;
}

void eth_mac_fmt(ETH_MAC* const mac, char* buff)
{
  const uint8* m = (const uint8*) mac;
  sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X", m[0], m[1], m[2], m[3], m[4], m[5]);
  return;
}

static const uint32 crcTable[256] = {
  0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
  0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
  0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
  0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
  0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
  0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
  0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
  0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
  0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
  0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
  0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
  0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
  0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
  0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
  0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
  0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
  0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
  0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
  0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
  0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
  0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
  0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
  0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
  0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
  0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
  0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
  0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
  0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
  0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
  0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
  0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
  0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
  0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
  0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
  0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
  0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
  0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
  0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
  0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
  0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
  0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
  0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
  0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};

uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len)
{
  const uint32 mask = 0xFFFFFFFF;
  const unsigned char* buf = (const unsigned char*)vbuf;

  crc ^= mask;
  while (len > 8) {
    crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];
    crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];
    crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];
    crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];
    crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];
    crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];
    crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];
    crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];
    len -= 8;
  }
  while (0 != len--)
    crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];
  return(crc ^ mask);
}

int eth_get_packet_crc32_data(const uint8 *msg, int len, uint8 *crcdata)
{
  int crc_len;

  if (len <= ETH_MAX_PACKET) {
    uint32 crc = eth_crc32(0, msg, len);                  /* calculate CRC */
    uint32 ncrc = htonl(crc);                             /* CRC in network order */
    int size = sizeof(ncrc);                              /* size of crc field */
    memcpy(crcdata, &ncrc, size);                         /* append crc to packet */
    crc_len = len + size;                                 /* set packet crc length */
  } else {
    crc_len = 0;                                          /* appending crc would destroy packet */
  }
  return crc_len;
}

int eth_add_packet_crc32(uint8 *msg, int len)
{
  int crc_len;

  if (len <= ETH_MAX_PACKET) {
    crc_len = eth_get_packet_crc32_data(msg, len, &msg[len]);/* append crc to packet */
  } else {
    crc_len = 0;                                          /* appending crc would destroy packet */
  }
  return crc_len;
}

void eth_setcrc(ETH_DEV* dev, int need_crc)
{
  dev->need_crc = need_crc;
}

void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, const char* txt, int detail, uint32 reason)
{
  if (dev->dptr->dctrl & reason) {
    char src[20];
    char dst[20];
    const unsigned short* proto = (const unsigned short*) &msg[12];
    uint32 crc = eth_crc32(0, msg, len);
    eth_mac_fmt((ETH_MAC*)msg, dst);
    eth_mac_fmt((ETH_MAC*)(msg+6), src);
    sim_debug(reason, dev->dptr, "%s  dst: %s  src: %s  proto: 0x%04X  len: %d  crc: %X\n",
          txt, dst, src, ntohs(*proto), len, crc);
    if (detail) {
      int i, same, group, sidx, oidx;
      char outbuf[80], strbuf[18];
      static const char hex[] = "0123456789ABCDEF";

      for (i=same=0; i<len; i += 16) {
        if ((i > 0) && (0 == memcmp(&msg[i], &msg[i-16], 16))) {
          ++same;
          continue;
        }
        if (same > 0) {
          sim_debug(reason, dev->dptr, "%04X thru %04X same as above\n", i-(16*same), i-1);
          same = 0;
        }
        group = (((len - i) > 16) ? 16 : (len - i));
        for (sidx=oidx=0; sidx<group; ++sidx) {
          outbuf[oidx++] = ' ';
          outbuf[oidx++] = hex[(msg[i+sidx]>>4)&0xf];
          outbuf[oidx++] = hex[msg[i+sidx]&0xf];
          if (isprint(msg[i+sidx]))
            strbuf[sidx] = msg[i+sidx];
          else
            strbuf[sidx] = '.';
        }
        outbuf[oidx] = '\0';
        strbuf[sidx] = '\0';
        sim_debug(reason, dev->dptr, "%04X%-48s %s\n", i, outbuf, strbuf);
      }
      if (same > 0) {
        sim_debug(reason, dev->dptr, "%04X thru %04X same as above\n", i-(16*same), len-1);
      }
    }
  }
}

void eth_packet_trace(ETH_DEV* dev, const uint8 *msg, int len, const char* txt)
{
  eth_packet_trace_ex(dev, msg, len, txt, 0, dev->dbit);
}

void eth_packet_trace_detail(ETH_DEV* dev, const uint8 *msg, int len, const char* txt)
{
  eth_packet_trace_ex(dev, msg, len, txt, 1     , dev->dbit);
}

const char* eth_getname(int number, char* name, char *desc)
{
  ETH_LIST  list[ETH_MAX_DEVICE];
  int count = eth_devices(ETH_MAX_DEVICE, list);

  if ((number < 0) || (count <= number))
      return NULL;
  if (list[number].eth_api != ETH_API_PCAP) {
    sim_printf ("Eth: Pcap capable device not found.  You may need to run as root\n");
    return NULL;
    }

  strcpy(name, list[number].name);
  strcpy(desc, list[number].desc);
  return name;
}

const char* eth_getname_bydesc(const char* desc, char* name, char *ndesc)
{
  ETH_LIST  list[ETH_MAX_DEVICE];
  int count = eth_devices(ETH_MAX_DEVICE, list);
  int i;
  size_t j=strlen(desc);

  for (i=0; i<count; i++) {
    int found = 1;
    size_t k = strlen(list[i].desc);

    if (j != k) continue;
    for (k=0; k<j; k++)
      if (tolower(list[i].desc[k]) != tolower(desc[k]))
        found = 0;
    if (found == 0) continue;

    /* found a case-insensitive description match */
    strcpy(name, list[i].name);
    strcpy(ndesc, list[i].desc);
    return name;
  }
  /* not found */
  return NULL;
}

char* eth_getname_byname(const char* name, char* temp, char *desc)
{
  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)) {
      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)) {
      found = 1;
      strcpy(temp, list[i].desc);
    }
  }
  return (found ? temp : NULL);
}

void eth_zero(ETH_DEV* dev)
{
  /* set all members to NULL OR 0 */
  memset(dev, 0, sizeof(ETH_DEV));
  dev->reflections = -1;                          /* not established yet */
}

static char*   (*p_pcap_lib_version) (void);

static ETH_DEV **eth_open_devices = NULL;
static int eth_open_device_count = 0;
static t_bool eth_show_active = FALSE;

#if defined (USE_NETWORK) || defined (USE_SHARED)
static void _eth_add_to_open_list (ETH_DEV* dev)
{
eth_open_devices = (ETH_DEV**)realloc(eth_open_devices, (eth_open_device_count+1)*sizeof(*eth_open_devices));
eth_open_devices[eth_open_device_count++] = dev;
}

static void _eth_remove_from_open_list (ETH_DEV* dev)
{
int i, j;

for (i=0; i<eth_open_device_count; ++i)
    if (eth_open_devices[i] == dev) {
        for (j=i+1; j<eth_open_device_count; ++j)
            eth_open_devices[j-1] = eth_open_devices[j];
        --eth_open_device_count;
        break;
        }
}
#endif

t_stat eth_show (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
{
  ETH_LIST  list[ETH_MAX_DEVICE];
  int number;

  eth_show_active = TRUE;
  number = eth_devices(ETH_MAX_DEVICE, list);
  fprintf(st, "ETH devices:\n");
  if (number == -1)
    fprintf(st, "  network support not available in simulator\n");
  else
    if (number == 0)
      fprintf(st, "  no network devices are available\n");
    else {
      size_t min, len;
      int i;
      for (i=0, min=0; i<number; i++)
        if ((len = strlen(list[i].name)) > min) min = len;
      for (i=0; i<number; i++)
        fprintf(st," eth%d\t%-*s (%s)\n", i, (int)min, list[i].name, list[i].desc);
    }
  if (p_pcap_lib_version) {
    fprintf(st, "%s\n", p_pcap_lib_version());
    }
  if (eth_open_device_count) {
    int i;
    char desc[ETH_DEV_DESC_MAX], *d;

    fprintf(st,"Open ETH Devices:\n");
    for (i=0; i<eth_open_device_count; i++) {
      d = eth_getdesc_byname(eth_open_devices[i]->name, desc);
      if (d)
        fprintf(st, " %-7s%s (%s)\n", eth_open_devices[i]->dptr->name, eth_open_devices[i]->dptr->units[0].filename, d);
      else
        fprintf(st, " %-7s%s\n", eth_open_devices[i]->dptr->name, eth_open_devices[i]->dptr->units[0].filename);
      eth_show_dev (st, eth_open_devices[i]);
      }
    }
  eth_show_active = FALSE;
  return SCPE_OK;
}

t_stat eth_show_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char *desc)
{
return eth_show (st, uptr, val, NULL);
}

t_stat ethq_init(ETH_QUE* que, int max)
{
  /* create dynamic queue if it does not exist */
  if (!que->item) {
    que->item = (struct eth_item *) calloc(max, sizeof(struct eth_item));
    if (!que->item) {
      /* failed to allocate memory */
      sim_printf("EthQ: failed to allocate dynamic queue[%d]\r\n", max);
      return SCPE_MEM;
    };
    que->max = max;
  };
  ethq_clear(que);
  return SCPE_OK;
}

t_stat ethq_destroy(ETH_QUE* que)
{
  /* release dynamic queue if it exists */
  ethq_clear(que);
  que->max = 0;
  if (que->item) {
    free(que->item);
    que->item = NULL;
  };
  return SCPE_OK;
}

void ethq_clear(ETH_QUE* que)
{
  int i;

  /* free up any extended packets */
  for (i=0; i<que->max; ++i)
    if (que->item[i].packet.oversize) {
      free (que->item[i].packet.oversize);
      que->item[i].packet.oversize = NULL;
      }
  /* clear packet array */
  memset(que->item, 0, sizeof(struct eth_item) * que->max);
  /* clear rest of structure */
  que->count = que->head = que->tail = 0;
}

void ethq_remove(ETH_QUE* que)
{
  struct eth_item* item = &que->item[que->head];

  if (que->count) {
    if (item->packet.oversize)
      free (item->packet.oversize);
    memset(item, 0, sizeof(struct eth_item));
    if (++que->head == que->max)
      que->head = 0;
    que->count--;
  }
}

void ethq_insert_data(ETH_QUE* que, int32 type, const uint8 *data, int used, size_t len, size_t crc_len, const uint8 *crc_data, int32 status)
{
  struct eth_item* item;

  /* if queue empty, set pointers to beginning */
  if (!que->count) {
    que->head = 0;
    que->tail = -1;
  }

  /* find new tail of the circular queue */
  if (++que->tail == que->max)
    que->tail = 0;
  if (++que->count > que->max) {
    que->count = que->max;
    /* lose oldest packet */
    if (++que->head == que->max)
      que->head = 0;
    que->loss++;
    }
  if (que->count > que->high)
    que->high = que->count;

  /* 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 (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));
    if (crc_data && (crc_len > len))
      memcpy(&item->packet.oversize[len], crc_data, ETH_CRC_SIZE);
    }
  item->packet.status = status;
}

void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status)
{
ethq_insert_data(que, type, pack->oversize ? pack->oversize : pack->msg, pack->used, pack->len, pack->crc_len, NULL, status);
}

/*============================================================================*/
/*                        Non-implemented versions                            */
/*============================================================================*/

#if !defined (USE_NETWORK) && !defined (USE_SHARED)
const char *eth_capabilities(void)
    {return "no Ethernet";}
t_stat eth_open(ETH_DEV* dev, const char* name, DEVICE* dptr, uint32 dbit)
  {return SCPE_NOFNC;}
t_stat eth_close (ETH_DEV* dev)
  {return SCPE_NOFNC;}
t_stat eth_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
  {
  fprintf (st, "%s attach help\n\n", dptr->name);
  fprintf (st, "This simulator was not built with ethernet device support\n");
  return SCPE_OK;
  }
t_stat eth_check_address_conflict (ETH_DEV* dev, 
                                   ETH_MAC* const mac)
  {return SCPE_NOFNC;}
t_stat eth_set_throttle (ETH_DEV* dev, uint32 time, uint32 burst, uint32 delay)
  {return SCPE_NOFNC;}
t_stat eth_set_async (ETH_DEV *dev, int latency)
  {return SCPE_NOFNC;}
t_stat eth_clr_async (ETH_DEV *dev)
  {return SCPE_NOFNC;}
t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
  {return SCPE_NOFNC;}
int eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
  {return SCPE_NOFNC;}
t_stat eth_filter (ETH_DEV* dev, int addr_count, ETH_MAC* const addresses,
                   ETH_BOOL all_multicast, ETH_BOOL promiscuous)
  {return SCPE_NOFNC;}
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)
  {return SCPE_NOFNC;}
int eth_devices (int max, ETH_LIST* dev)
  {return -1;}
void eth_show_dev (FILE* st, ETH_DEV* dev)
  {}
static int _eth_get_system_id (char *buf, size_t buf_size)
  {memset (buf, 0, buf_size); return 0;}
#else    /* endif unimplemented */

const char *eth_capabilities(void)
 {
 return "Ethernet Packet transports"
#if defined (HAVE_PCAP_NETWORK)
     ":PCAP"
#endif
#if defined (HAVE_TAP_NETWORK)
     ":TAP"
#endif
#if defined (HAVE_VDE_NETWORK)
     ":VDE"
#endif
#if defined (HAVE_SLIRP_NETWORK)
     ":NAT"
#endif
     ":UDP";
 }

#if (defined (xBSD) || defined (__APPLE__)) && (defined (HAVE_TAP_NETWORK) || defined (HAVE_PCAP_NETWORK))
#include <sys/ioctl.h>
#include <net/bpf.h>
#endif

#if defined (HAVE_PCAP_NETWORK)
/*============================================================================*/
/*      WIN32, Linux, and xBSD routines use WinPcap and libpcap packages      */
/*        OpenVMS Alpha uses a WinPcap port and an associated execlet         */
/*============================================================================*/

#include <pcap.h>
#include <string.h>
#else
struct pcap_pkthdr {
    uint32 caplen;  /* length of portion present */
    uint32 len;     /* length this packet (off wire) */
};
#define PCAP_ERRBUF_SIZE 256
typedef void * pcap_t;  /* Pseudo Type to avoid compiler errors */
#define DLT_EN10MB 1    /* Dummy Value to avoid compiler errors */
#endif /* HAVE_PCAP_NETWORK */

#ifdef HAVE_TAP_NETWORK
#if defined(__linux) || defined(__linux__)
#include <sys/ioctl.h> 
#include <net/if.h> 
#include <linux/if_tun.h> 
#elif defined(HAVE_BSDTUNTAP)
#include <sys/types.h>
#include <net/if_types.h>
#include <net/if.h>
#else /* We don't know how to do this on the current platform */
#undef HAVE_TAP_NETWORK
#endif
#endif /* HAVE_TAP_NETWORK */

#ifdef HAVE_VDE_NETWORK
#ifdef  __cplusplus
extern "C" {
#endif
#include <libvdeplug.h>
#ifdef  __cplusplus
}
#endif
#endif /* HAVE_VDE_NETWORK */

#ifdef HAVE_SLIRP_NETWORK
#include "sim_slirp.h"
#endif /* HAVE_SLIRP_NETWORK */

/* Allows windows to look up user-defined adapter names */
#if defined(_WIN32)
#include <winreg.h>
#endif

#ifdef HAVE_DLOPEN
#include <dlfcn.h>
#endif

#if defined(USE_SHARED) && (defined(_WIN32) || defined(HAVE_DLOPEN))
/* Dynamic DLL loading technique and modified source comes from
   Etherial/WireShark capture_pcap.c */

/* Dynamic DLL load variables */
#ifdef _WIN32
static HINSTANCE hLib = NULL;               /* handle to DLL */
#else
static void *hLib = 0;                      /* handle to Library */
#endif
static int lib_loaded = 0;                  /* 0=not loaded, 1=loaded, 2=library load failed, 3=Func load failed */
static const char* lib_name =
#if defined(_WIN32) || defined(__CYGWIN__)
                          "wpcap.dll";
#elif defined(__APPLE__)
                          "/usr/lib/libpcap.A.dylib";
#else
#define __STR_QUOTE(tok) #tok
#define __STR(tok) __STR_QUOTE(tok)
                          "libpcap." __STR(HAVE_DLOPEN);
#endif
static const char* no_pcap = 
#if defined(_WIN32) || defined(__CYGWIN__)
                          "wpcap load failure";
#else
                          "libpcap load failure";
#endif

/* define pointers to pcap functions needed */
static void    (*p_pcap_close) (pcap_t *);
static int     (*p_pcap_compile) (pcap_t *, struct bpf_program *, const char *, int, bpf_u_int32);
static int     (*p_pcap_datalink) (pcap_t *);
static int     (*p_pcap_dispatch) (pcap_t *, int, pcap_handler, u_char *);
static int     (*p_pcap_findalldevs) (pcap_if_t **, char *);
static void    (*p_pcap_freealldevs) (pcap_if_t *);
static void    (*p_pcap_freecode) (struct bpf_program *);
static char*   (*p_pcap_geterr) (pcap_t *);
static int     (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, char *);
static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *);
#ifdef _WIN32
static int     (*p_pcap_setmintocopy) (pcap_t* handle, int);
static HANDLE  (*p_pcap_getevent) (pcap_t *);
#else
#ifdef MUST_DO_SELECT
static int     (*p_pcap_get_selectable_fd) (pcap_t *);
#endif
static int     (*p_pcap_fileno) (pcap_t *);
#endif
static int     (*p_pcap_sendpacket) (pcap_t* handle, const u_char* msg, int len);
static int     (*p_pcap_setfilter) (pcap_t *, struct bpf_program *);
static int     (*p_pcap_setnonblock)(pcap_t* a, int nonblock, char *errbuf);

/* load function pointer from DLL */
typedef int (*_func)();

static void load_function(const char* function, _func* func_ptr) {
#ifdef _WIN32
    *func_ptr = (_func)((size_t)GetProcAddress(hLib, function));
#else
    *func_ptr = (_func)((size_t)dlsym(hLib, function));
#endif
    if (*func_ptr == 0) {
    sim_printf ("Eth: Failed to find function '%s' in %s\r\n", function, lib_name);
    lib_loaded = 3;
  }
}

static void try_load_function(const char* function, _func* func_ptr) {
#ifdef _WIN32
    *func_ptr = (_func)((size_t)GetProcAddress(hLib, function));
#else
    *func_ptr = (_func)((size_t)dlsym(hLib, function));
#endif
}

/* load wpcap.dll as required */
int load_pcap(void) {
  switch(lib_loaded) {
    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");
        if (p_SetDllDirectory && p_GetSystemDirectory) {
          char npcap_path[512] = "";

          if (p_GetSystemDirectory (npcap_path, sizeof(npcap_path) - 7))
            strcat (npcap_path, "\\Npcap");
          if (p_SetDllDirectory(npcap_path))
            hLib = LoadLibraryA(lib_name);
          p_SetDllDirectory (NULL);
          }
        if (hLib == NULL)
          hLib = LoadLibraryA(lib_name);
        }
#else
      hLib = dlopen(lib_name, RTLD_NOW);
#endif
      if (hLib == 0) {
        /* failed to load DLL */
        sim_printf ("Eth: Failed to load %s\r\n", lib_name);
#ifdef _WIN32
        sim_printf ("Eth: You must install Npcap or WinPcap 4.x to use networking\r\n");
#else
        sim_printf ("Eth: You must install libpcap to use networking\r\n");
#endif
        lib_loaded = 2;
        break;
      } else {
        /* library loaded OK */
        lib_loaded = 1;
      }

      /* load required functions; sets dll_load=3 on error */
      load_function("pcap_close",        (_func *) &p_pcap_close);
      load_function("pcap_compile",      (_func *) &p_pcap_compile);
      load_function("pcap_datalink",     (_func *) &p_pcap_datalink);
      load_function("pcap_dispatch",     (_func *) &p_pcap_dispatch);
      load_function("pcap_findalldevs",  (_func *) &p_pcap_findalldevs);
      load_function("pcap_freealldevs",  (_func *) &p_pcap_freealldevs);
      load_function("pcap_freecode",     (_func *) &p_pcap_freecode);
      load_function("pcap_geterr",       (_func *) &p_pcap_geterr);
      load_function("pcap_lookupnet",    (_func *) &p_pcap_lookupnet);
      load_function("pcap_open_live",    (_func *) &p_pcap_open_live);
#ifdef _WIN32
      load_function("pcap_setmintocopy", (_func *) &p_pcap_setmintocopy);
      load_function("pcap_getevent",     (_func *) &p_pcap_getevent);
#else
#ifdef MUST_DO_SELECT
      load_function("pcap_get_selectable_fd",     (_func *) &p_pcap_get_selectable_fd);
#endif
      load_function("pcap_fileno",       (_func *) &p_pcap_fileno);
#endif
      load_function("pcap_sendpacket",   (_func *) &p_pcap_sendpacket);
      load_function("pcap_setfilter",    (_func *) &p_pcap_setfilter);
      load_function("pcap_setnonblock",  (_func *) &p_pcap_setnonblock);
      load_function("pcap_lib_version",  (_func *) &p_pcap_lib_version);

      if ((lib_loaded == 1) && (!eth_show_active)) {
        /* log successful load */
        sim_printf("%s\n", p_pcap_lib_version());
      }
      break;
    default:                /* loaded or failed */
      break;
  }
  return (lib_loaded == 1) ? 1 : 0;
}

/* define functions with dynamic revectoring */
void pcap_close(pcap_t* a) {
  if (load_pcap() != 0) {
    p_pcap_close(a);
  }
}

/* Some platforms's pcap.h have an ancient declaration of pcap_compile which doesn't have a const in the bpf string argument */
#if !defined (BPF_CONST_STRING)
int pcap_compile(pcap_t* a, struct bpf_program* b, char* c, int d, bpf_u_int32 e) {
#else
int pcap_compile(pcap_t* a, struct bpf_program* b, const char* c, int d, bpf_u_int32 e) {
#endif
  if (load_pcap() != 0) {
    return p_pcap_compile(a, b, c, d, e);
  } else {
    return 0;
  }
}

int pcap_datalink(pcap_t* a) {
  if (load_pcap() != 0) {
    return p_pcap_datalink(a);
  } else {
    return 0;
  }
}

int pcap_dispatch(pcap_t* a, int b, pcap_handler c, u_char* d) {
  if (load_pcap() != 0) {
    return p_pcap_dispatch(a, b, c, d);
  } else {
    return 0;
  }
}

int pcap_findalldevs(pcap_if_t** a, char* b) {
  if (load_pcap() != 0) {
    return p_pcap_findalldevs(a, b);
  } else {
    *a = 0;
    strcpy(b, no_pcap);
    return -1;
  }
}

void pcap_freealldevs(pcap_if_t* a) {
  if (load_pcap() != 0) {
    p_pcap_freealldevs(a);
  }
}

void pcap_freecode(struct bpf_program* a) {
  if (load_pcap() != 0) {
    p_pcap_freecode(a);
  }
}

char* pcap_geterr(pcap_t* a) {
  if (load_pcap() != 0) {
    return p_pcap_geterr(a);
  } else {
    return (char*) 0;
  }
}

int pcap_lookupnet(const char* a, bpf_u_int32* b, bpf_u_int32* c, char* d) {
  if (load_pcap() != 0) {
    return p_pcap_lookupnet(a, b, c, d);
  } else {
    return 0;
  }
}

pcap_t* pcap_open_live(const char* a, int b, int c, int d, char* e) {
  if (load_pcap() != 0) {
    return p_pcap_open_live(a, b, c, d, e);
  } else {
    return (pcap_t*) 0;
  }
}

#ifdef _WIN32
int pcap_setmintocopy(pcap_t* a, int b) {
  if (load_pcap() != 0) {
    return p_pcap_setmintocopy(a, b);
  } else {
    return 0;
  }
}

HANDLE pcap_getevent(pcap_t* a) {
  if (load_pcap() != 0) {
    return p_pcap_getevent(a);
  } else {
    return (HANDLE) 0;
  }
}

#else
#ifdef MUST_DO_SELECT
int pcap_get_selectable_fd(pcap_t* a) {
  if (load_pcap() != 0) {
    return p_pcap_get_selectable_fd(a);
  } else {
    return 0;
  }
}
#endif

int pcap_fileno(pcap_t * a) {
  if (load_pcap() != 0) {
    return p_pcap_fileno(a);
  } else {
    return 0;
  }
}
#endif

int pcap_sendpacket(pcap_t* a, const u_char* b, int c) {
  if (load_pcap() != 0) {
    return p_pcap_sendpacket(a, b, c);
  } else {
    return 0;
  }
}

int pcap_setfilter(pcap_t* a, struct bpf_program* b) {
  if (load_pcap() != 0) {
    return p_pcap_setfilter(a, b);
  } else {
    return 0;
  }
}

int pcap_setnonblock(pcap_t* a, int nonblock, char *errbuf) {
  if (load_pcap() != 0) {
    return p_pcap_setnonblock(a, nonblock, errbuf);
  } else {
    return 0;
  }
}
#endif /* defined(USE_SHARED) && (defined(_WIN32) || defined(HAVE_DLOPEN)) */

/* Some platforms have always had pcap_sendpacket */
#if defined(_WIN32) || defined(__VMS)
#define HAS_PCAP_SENDPACKET 1
#else
/* The latest libpcap and WinPcap all have pcap_sendpacket */
#if !defined (NEED_PCAP_SENDPACKET)
#define HAS_PCAP_SENDPACKET 1
#endif
#endif

#if !defined (HAS_PCAP_SENDPACKET)
/* libpcap has no function to write a packet, so we need to implement
   pcap_sendpacket() for compatibility with the WinPcap base code.
   Return value: 0=Success, -1=Failure */
int pcap_sendpacket(pcap_t* handle, const u_char* msg, int len)
{
#if defined (__linux) || defined (__linux__)
  return (send(pcap_fileno(handle), msg, len, 0) == len)? 0 : -1;
#else
  return (write(pcap_fileno(handle), msg, len) == len)? 0 : -1;
#endif /* linux */
}
#endif /* !HAS_PCAP_SENDPACKET */

#if defined(_WIN32) || defined(__CYGWIN__)
/* extracted from WinPcap's Packet32.h */
struct _PACKET_OID_DATA {
    uint32 Oid;                 ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h
                                ///< for a complete list of valid codes.
    uint32 Length;              ///< Length of the data field
    uint8 Data[1];              ///< variable-lenght field that contains the information passed to or received 
                                ///< from the adapter.
}; 
typedef struct _PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA;
typedef void **LPADAPTER;
#define OID_802_3_CURRENT_ADDRESS               0x01010102 /* Extracted from ntddmdis.h */

static int pcap_mac_if_win32(const char *AdapterName, unsigned char MACAddress[6])
{
  LPADAPTER         lpAdapter;
  PPACKET_OID_DATA  OidData;
  int               Status;
  int               ReturnValue;
#ifdef _WIN32
  HMODULE           hDll;         /* handle to DLL */
#else
  static void       *hDll = NULL; /* handle to Library */
  typedef int BOOLEAN;
#endif
  LPADAPTER (*p_PacketOpenAdapter)(const char *AdapterName);
  void (*p_PacketCloseAdapter)(LPADAPTER lpAdapter);
  int (*p_PacketRequest)(LPADAPTER  AdapterObject,BOOLEAN Set,PPACKET_OID_DATA  OidData);

#ifdef _WIN32
  hDll = LoadLibraryA("packet.dll");
  p_PacketOpenAdapter = (LPADAPTER (*)(const char *AdapterName))GetProcAddress(hDll, "PacketOpenAdapter");
  p_PacketCloseAdapter = (void (*)(LPADAPTER lpAdapter))GetProcAddress(hDll, "PacketCloseAdapter");
  p_PacketRequest = (int (*)(LPADAPTER  AdapterObject,BOOLEAN Set,PPACKET_OID_DATA  OidData))GetProcAddress(hDll, "PacketRequest");
#else
  hDll = dlopen("packet.dll", RTLD_NOW);
  p_PacketOpenAdapter = (LPADAPTER (*)(const char *AdapterName))dlsym(hDll, "PacketOpenAdapter");
  p_PacketCloseAdapter = (void (*)(LPADAPTER lpAdapter))dlsym(hDll, "PacketCloseAdapter");
  p_PacketRequest = (int (*)(LPADAPTER  AdapterObject,BOOLEAN Set,PPACKET_OID_DATA  OidData))dlsym(hDll, "PacketRequest");
#endif
  
  /* Open the selected adapter */

  lpAdapter =   p_PacketOpenAdapter(AdapterName);

  if (!lpAdapter || (*lpAdapter == (void *)-1)) {
#ifdef _WIN32
      FreeLibrary(hDll);
#else
      dlclose(hDll);
#endif
    return -1;
  }

  /* Allocate a buffer to get the MAC adress */

  OidData = (PACKET_OID_DATA *)malloc(6 + sizeof(PACKET_OID_DATA));
  if (OidData == NULL) {
    p_PacketCloseAdapter(lpAdapter);
#ifdef _WIN32
    FreeLibrary(hDll);
#else
    dlclose(hDll);
#endif
    return -1;
  }

  /* Retrieve the adapter MAC querying the NIC driver */

  OidData->Oid = OID_802_3_CURRENT_ADDRESS;

  OidData->Length = 6;
  memset(OidData->Data, 0, 6);

  Status = p_PacketRequest(lpAdapter, FALSE, OidData);
  if(Status) {
    memcpy(MACAddress, OidData->Data, 6);
    ReturnValue = 0;
  } else
    ReturnValue = -1;

  free(OidData);
  p_PacketCloseAdapter(lpAdapter);
#ifdef _WIN32
  FreeLibrary(hDll);
#else
  dlclose(hDll);
#endif
  return ReturnValue;
}

#endif  /* defined(_WIN32) || defined(__CYGWIN__) */

#if defined (__VMS) && !defined(__VAX)
#include <descrip.h>
#include <iodef.h>
#include <ssdef.h>
#include <starlet.h>
#include <stdio.h>
#include <stsdef.h>
#include <nmadef.h>

static int pcap_mac_if_vms(const char *AdapterName, unsigned char MACAddress[6])
{
  char VMS_Device[16];
  $DESCRIPTOR(Device, VMS_Device);
  unsigned short iosb[4];
  unsigned short *w;
  unsigned char *pha = NULL;
  unsigned char *hwa = NULL;
  int tmpval;
  int status;
  unsigned short characteristics[512];
  long chardesc[] = {sizeof(characteristics), (long)&characteristics};
  unsigned short chan;
#pragma member_alignment save
#pragma nomember_alignment
  static struct {
    short fmt;
    long val_fmt;
    short pty;
    long val_pty;
    short pad;
    long val_pad;
    } setup  = {
        NMA$C_PCLI_FMT, NMA$C_LINFM_ETH,
        NMA$C_PCLI_PTY, 0x0090,
        NMA$C_PCLI_PAD, NMA$C_STATE_OFF,
    };
#pragma member_alignment restore
    long setupdesc[] = {sizeof(setup), (long)&setup};

  /* Convert Interface Name to VMS Device Name */
  /* This is a name shuffle */
  /*   WE0 becomes EWA0:    */
  /*   SE1 becomes ESB0:    */
  /*   XE0 becomes EXA0:    */
  tmpval = (int)(AdapterName[2]-'0');
  if ((tmpval < 0) || (tmpval > 25))
    return -1;
  VMS_Device[0] = toupper(AdapterName[1]);
  VMS_Device[1] = toupper(AdapterName[0]);
  VMS_Device[2] = 'A' + tmpval;
  VMS_Device[3] = '0';
  VMS_Device[4] = '\0';
  VMS_Device[5] = '\0';
  Device.dsc$w_length = strlen(VMS_Device);
  if (!$VMS_STATUS_SUCCESS( sys$assign (&Device, &chan, 0, 0, 0) ))
    return -1;
  status = sys$qiow (0, chan, IO$_SETMODE|IO$M_CTRL|IO$M_STARTUP, &iosb, 0, 0, 
                     0, &setupdesc, 0, 0, 0, 0);
  if ((!$VMS_STATUS_SUCCESS(status)) || (!$VMS_STATUS_SUCCESS(iosb[0]))) {
    sys$dassgn(chan);
    return -1;
    }
  status = sys$qiow (0, chan, IO$_SENSEMODE|IO$M_CTRL, &iosb, 0, 0, 
                     0, &chardesc, 0, 0, 0, 0);
  sys$dassgn(chan);
  if ((!$VMS_STATUS_SUCCESS(status)) || (!$VMS_STATUS_SUCCESS(iosb[0])))
    return -1;
  for (w=characteristics; w < &characteristics[iosb[1]]; ) {
    if ((((*w)&0xFFF) == NMA$C_PCLI_HWA) && (6 == *(w+1)))
      hwa = (unsigned char *)(w + 2);
    if ((((*w)&0xFFF) == NMA$C_PCLI_PHA) && (6 == *(w+1)))
      pha = (unsigned char *)(w + 2);
    if (((*w)&0x1000) == 0)
      w += 3;                       /* Skip over Longword Parameter */
    else
      w += (2 + ((1 + *(w+1))/2));  /* Skip over String Parameter */
    }
  if (pha != NULL)                  /* Prefer Physical Address */
    memcpy(MACAddress, pha, 6);
  else
    if (hwa != NULL)                /* Fallback to Hardware Address */
      memcpy(MACAddress, hwa, 6);
    else
      return -1;
  return 0;
}
#endif /* defined (__VMS) && !defined(__VAX) */

static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname)
{
  memset(&dev->host_nic_phy_hw_addr, 0, sizeof(dev->host_nic_phy_hw_addr));
  dev->have_host_nic_phy_addr = 0;
  if (dev->eth_api != ETH_API_PCAP)
    return;
#if defined(_WIN32) || defined(__CYGWIN__)
  if (!pcap_mac_if_win32(devname, dev->host_nic_phy_hw_addr))
    dev->have_host_nic_phy_addr = 1;
#elif defined (__VMS) && !defined(__VAX)
  if (!pcap_mac_if_vms(devname, dev->host_nic_phy_hw_addr))
    dev->have_host_nic_phy_addr = 1;
#elif !defined(__CYGWIN__) && !defined(__VMS)
  if (1) {
    char command[1024];
    FILE *f;
    int i;
    const char *patterns[] = {
        "grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]",
        "egrep [0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]",
        NULL};

    memset(command, 0, sizeof(command));
    for (i=0; patterns[i] && (0 == dev->have_host_nic_phy_addr); ++i) {
      snprintf(command, sizeof(command)-1, "ifconfig %s | %s  >NIC.hwaddr", devname, patterns[i]);
      (void)system(command);
      if (NULL != (f = fopen("NIC.hwaddr", "r"))) {
        while (0 == dev->have_host_nic_phy_addr) {
          if (fgets(command, sizeof(command)-1, f)) {
            char *p1, *p2;

            p1 = strchr(command, ':');
            while (p1) {
              p2 = strchr(p1+1, ':');
              if (p2 <= p1+3) {
                unsigned int mac_bytes[6];
                if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) {
                  dev->host_nic_phy_hw_addr[0] = mac_bytes[0];
                  dev->host_nic_phy_hw_addr[1] = mac_bytes[1];
                  dev->host_nic_phy_hw_addr[2] = mac_bytes[2];
                  dev->host_nic_phy_hw_addr[3] = mac_bytes[3];
                  dev->host_nic_phy_hw_addr[4] = mac_bytes[4];
                  dev->host_nic_phy_hw_addr[5] = mac_bytes[5];
                  dev->have_host_nic_phy_addr = 1;
                  }
                break;
                }
              p1 = p2;
              }
            }
          else
            break;
          }
        fclose(f);
        remove("NIC.hwaddr");
        }
      }
    }
#endif
}

#if defined(__APPLE__)
#include <uuid/uuid.h>
#include <unistd.h>
static int _eth_get_system_id (char *buf, size_t buf_size)
{
static struct timespec wait = {5, 0};   /* 5 seconds */
static uuid_t uuid;

memset (buf, 0, buf_size);
if (buf_size < 37)
  return -1;
if (gethostuuid (uuid, &wait))
  memset (uuid, 0, sizeof(uuid));
uuid_unparse_lower(uuid, buf);
return 0;
}

#elif defined(_WIN32)
static int _eth_get_system_id (char *buf, size_t buf_size)
{
  LONG status;
  DWORD reglen, regtype;
  HKEY reghnd;

  memset (buf, 0, buf_size);
#ifndef KEY_WOW64_64KEY
#define KEY_WOW64_64KEY         (0x0100)
#endif
  if ((status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", 0, KEY_QUERY_VALUE|KEY_WOW64_64KEY, &reghnd)) != ERROR_SUCCESS)
    return -1;
  reglen = buf_size;
  if ((status = RegQueryValueExA (reghnd, "MachineGuid", NULL, &regtype, buf, &reglen)) != ERROR_SUCCESS) {
    RegCloseKey (reghnd);
    return -1;
    }
  RegCloseKey (reghnd );
  /* make sure value is the right type, bail if not acceptable */
  if ((regtype != REG_SZ) || (reglen > buf_size))
    return -1;
  /* registry value seems OK */
  return 0;
}

#else
static int _eth_get_system_id (char *buf, size_t buf_size)
{
FILE *f;

memset (buf, 0, buf_size);
if ((f = fopen ("/etc/machine-id", "r"))) {
  fread (buf, 1, buf_size, f);
  fclose (f);
  }
else {
  if ((f = popen ("hostname", "r"))) {
    fread (buf, 1, buf_size, f);
    pclose (f);
    }
  }
while ((strlen (buf) > 0) && sim_isspace(buf[strlen (buf) - 1]))
  buf[strlen (buf) - 1] = '\0';
return 0;
}
#endif

/* Forward declarations */
static void
_eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data);

static t_stat
_eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine);

static void
_eth_error(ETH_DEV* dev, const char* where);

#if defined(HAVE_SLIRP_NETWORK)
static void _slirp_callback (void *opaque, const unsigned char *buf, int len)
{
struct pcap_pkthdr header;

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;
SOCKET select_fd = 0;
#if defined (_WIN32)
HANDLE hWait = (dev->eth_api == ETH_API_PCAP) ? pcap_getevent ((pcap_t*)dev->handle) : NULL;
#endif

switch (dev->eth_api) {
  case ETH_API_PCAP:
#if defined (HAVE_PCAP_NETWORK)
#if defined (MUST_DO_SELECT)
    do_select = 1;
    select_fd = pcap_get_selectable_fd((pcap_t *)dev->handle);
#endif
#endif
    break;
  case ETH_API_TAP:
  case ETH_API_VDE:
  case ETH_API_UDP:
  case ETH_API_NAT:
    do_select = 1;
    select_fd = dev->fd_handle;
    break;
  }

sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n");

/* Boost Priority for this I/O thread vs the CPU instruction execution 
   thread which, in general, won't be readily yielding the processor 
   when this thread needs to run */
sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);

while (dev->handle) {
#if defined (_WIN32)
  if (dev->eth_api == ETH_API_PCAP) {
    if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250))
      sel_ret = 1;
    }
  if ((dev->eth_api == ETH_API_UDP) || (dev->eth_api == ETH_API_NAT))
#endif /* _WIN32 */
  if (1) {
    if (do_select) {
#ifdef HAVE_SLIRP_NETWORK
      if (dev->eth_api == ETH_API_NAT) {
        sel_ret = sim_slirp_select ((SLIRP*)dev->handle, 250);
        }
      else
#endif
        {
        fd_set setl;
        struct timeval timeout;
        
        FD_ZERO(&setl);
        FD_SET(select_fd, &setl);
        timeout.tv_sec = 0;
        timeout.tv_usec = 250*1000;
        sel_ret = select(1+select_fd, &setl, NULL, NULL, &timeout);
        }
      }
    else
      sel_ret = 1;
    if (sel_ret < 0 && errno != EINTR) 
      break;
    }
  if (sel_ret > 0) {
    if (!dev->handle)
      break;
    /* dispatch read request queue available packets */
    switch (dev->eth_api) {
#ifdef HAVE_PCAP_NETWORK
      case ETH_API_PCAP:
        status = pcap_dispatch ((pcap_t*)dev->handle, -1, &_eth_callback, (u_char*)dev);
        break;
#endif
#ifdef HAVE_TAP_NETWORK
      case ETH_API_TAP:
        if (1) {
          struct pcap_pkthdr header;
          int len;
          u_char buf[ETH_MAX_JUMBO_FRAME];

          memset(&header, 0, sizeof(header));
          len = read(dev->fd_handle, buf, sizeof(buf));
          if (len > 0) {
            status = 1;
            header.caplen = header.len = len;
            _eth_callback((u_char *)dev, &header, buf);
            }
          else {
            if (len < 0)
              status = -1;
            else
              status = 0;
            }
          }
        break;
#endif /* HAVE_TAP_NETWORK */
#ifdef HAVE_VDE_NETWORK
      case ETH_API_VDE:
        if (1) {
          struct pcap_pkthdr header;
          int len;
          u_char buf[ETH_MAX_JUMBO_FRAME];

          memset(&header, 0, sizeof(header));
          len = vde_recv((VDECONN *)dev->handle, buf, sizeof(buf), 0);
          if (len > 0) {
            status = 1;
            header.caplen = header.len = len;
            _eth_callback((u_char *)dev, &header, buf);
            }
          else {
            if (len < 0)
              status = -1;
            else
              status = 0;
            }
          }
        break;
#endif /* HAVE_VDE_NETWORK */
#ifdef HAVE_SLIRP_NETWORK
      case ETH_API_NAT:
        sim_slirp_dispatch ((SLIRP*)dev->handle);
        status = 1;
        break;
#endif /* HAVE_SLIRP_NETWORK */
      case ETH_API_UDP:
        if (1) {
          struct pcap_pkthdr header;
          int len;
          u_char buf[ETH_MAX_JUMBO_FRAME];

          memset(&header, 0, sizeof(header));
          len = (int)sim_read_sock (select_fd, (char *)buf, (int32)sizeof(buf));
          if (len > 0) {
            status = 1;
            header.caplen = header.len = len;
            _eth_callback((u_char *)dev, &header, buf);
            }
          else {
            if (len < 0)
              status = -1;
            else
              status = 0;
            }
          }
        break;
      }
    if ((status > 0) && (dev->asynch_io)) {
      int wakeup_needed;

      pthread_mutex_lock (&dev->lock);
      wakeup_needed = (dev->read_queue.count != 0);
      pthread_mutex_unlock (&dev->lock);
      if (wakeup_needed) {
        sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n");
        sim_activate_abs (dev->dptr->units, dev->asynch_io_latency);
        }
      }
    if (status < 0) {
      ++dev->receive_packet_errors;
      _eth_error (dev, "_eth_reader");
      if (dev->handle) { /* Still attached? */
#if defined (_WIN32)
        hWait = (dev->eth_api == ETH_API_PCAP) ? pcap_getevent ((pcap_t*)dev->handle) : NULL;
#endif
        if (do_select) {
          select_fd = dev->fd_handle;
#if !defined (_WIN32) && defined(HAVE_PCAP_NETWORK)
          if (dev->eth_api == ETH_API_PCAP)
            select_fd = pcap_get_selectable_fd((pcap_t *)dev->handle);
#endif
          }
        }
      }
    }
  }

sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n");
return NULL;
}

static void *
_eth_writer(void *arg)
{
ETH_DEV* volatile dev = (ETH_DEV*)arg;
ETH_WRITE_REQUEST *request;

/* Boost Priority for this I/O thread vs the CPU instruction execution 
   thread which in general won't be readily yielding the processor when 
   this thread needs to run */
sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);

sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n");

pthread_mutex_lock (&dev->writer_lock);
while (dev->handle) {
  pthread_cond_wait (&dev->writer_cond, &dev->writer_lock);
  while (NULL != (request = dev->write_requests)) {
    /* Pull buffer off request list */
    dev->write_requests = request->next;
    pthread_mutex_unlock (&dev->writer_lock);

    if (dev->throttle_delay != ETH_THROT_DISABLED_DELAY) {
      uint32 packet_delta_time = sim_os_msec() - dev->throttle_packet_time;
      dev->throttle_events <<= 1;
      dev->throttle_events += (packet_delta_time < dev->throttle_time) ? 1 : 0;
      if ((dev->throttle_events & dev->throttle_mask) == dev->throttle_mask) {
        sim_os_ms_sleep (dev->throttle_delay);
        ++dev->throttle_count;
        }
      dev->throttle_packet_time = sim_os_msec();
      }
    dev->write_status = _eth_write(dev, &request->packet, NULL);

    pthread_mutex_lock (&dev->writer_lock);
    /* Put buffer on free buffer list */
    request->next = dev->write_buffers;
    dev->write_buffers = request;
    }
  }
pthread_mutex_unlock (&dev->writer_lock);

sim_debug(dev->dbit, dev->dptr, "Writer Thread Exiting\n");
return NULL;
}
#endif

t_stat eth_set_async (ETH_DEV *dev, int latency)
{
#if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO)
char *msg = "Eth: can't operate asynchronously, must poll\r\n";
sim_printf ("%s", msg);
return SCPE_NOFNC;
#else
int wakeup_needed;

dev->asynch_io = 1;
dev->asynch_io_latency = latency;
pthread_mutex_lock (&dev->lock);
wakeup_needed = (dev->read_queue.count != 0);
pthread_mutex_unlock (&dev->lock);
if (wakeup_needed) {
  sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n");
  sim_activate_abs (dev->dptr->units, dev->asynch_io_latency);
  }
#endif
return SCPE_OK;
}

t_stat eth_clr_async (ETH_DEV *dev)
{
#if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO)
return SCPE_NOFNC;
#else
/* make sure device exists */
if (!dev) return SCPE_UNATT;

dev->asynch_io = 0;
return SCPE_OK;
#endif
}

t_stat eth_set_throttle (ETH_DEV* dev, uint32 time, uint32 burst, uint32 delay)
{
if (!dev)
  return SCPE_IERR;
dev->throttle_time = time;
dev->throttle_burst = burst;
dev->throttle_delay = delay;
dev->throttle_mask = (1 << dev->throttle_burst) - 1;
return SCPE_OK;
}

static t_stat _eth_open_port(char *savname, int *eth_api, void **handle, SOCKET *fd_handle, char errbuf[PCAP_ERRBUF_SIZE], char *bpf_filter, void *opaque, DEVICE *dptr, uint32 dbit)
{
int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ;

if (bufsz < ETH_MAX_JUMBO_FRAME)
  bufsz = ETH_MAX_JUMBO_FRAME;    /* Enable handling of jumbo frames */

*eth_api = 0;
*handle = NULL;
*fd_handle = 0;

/* attempt to connect device */
memset(errbuf, 0, PCAP_ERRBUF_SIZE);
if (0 == strncmp("tap:", savname, 4)) {
  int  tun = -1;    /* TUN/TAP Socket */
  int  on = 1;
  const char *devname = savname + 4;

  while (isspace(*devname))
      ++devname;
#if defined(HAVE_TAP_NETWORK)
  if (!strcmp(savname, "tap:tapN")) {
    sim_printf ("Eth: Must specify actual tap device name (i.e. tap:tap0)\r\n");
    return SCPE_OPENERR | SCPE_NOMESSAGE;
    }
#endif
#if (defined(__linux) || defined(__linux__)) && defined(HAVE_TAP_NETWORK)
  if ((tun = open("/dev/net/tun", O_RDWR)) >= 0) {
    struct ifreq ifr; /* Interface Requests */

    memset(&ifr, 0, sizeof(ifr));
    /* Set up interface flags */
    strcpy(ifr.ifr_name, devname);
    ifr.ifr_flags = IFF_TAP|IFF_NO_PI;

    /* Send interface requests to TUN/TAP driver. */
    if (ioctl(tun, TUNSETIFF, &ifr) >= 0) {
      if (ioctl(tun, FIONBIO, &on)) {
        strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1);
        close(tun);
        }
      else {
        *fd_handle = tun;
        strcpy(savname, ifr.ifr_name);
        }
      }
    else
      strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1);
    }
  else
    strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1);
#elif defined(HAVE_BSDTUNTAP) && defined(HAVE_TAP_NETWORK)
  if (1) {
    char dev_name[64] = "";

    snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", devname);
    dev_name[sizeof(dev_name)-1] = '\0';

    if ((tun = open(dev_name, O_RDWR)) >= 0) {
      if (ioctl(tun, FIONBIO, &on)) {
        strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1);
        close(tun);
        }
      else {
        *fd_handle = tun;
        strcpy(savname, devname);
        }
#if defined (__APPLE__)
      if (1) {
        struct ifreq ifr;
        int s;

        memset (&ifr, 0, sizeof(ifr));
        ifr.ifr_addr.sa_family = AF_INET;
        strncpy(ifr.ifr_name, savname, sizeof(ifr.ifr_name));
        if ((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
          if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) >= 0) {
            ifr.ifr_flags |= IFF_UP;
            if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr)) {
              strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1);
              close(tun);
              }
            }
          close(s);
          }
        }
#endif
      }
    else
      strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1);
  }
#else
  strncpy(errbuf, "No support for tap: devices", PCAP_ERRBUF_SIZE-1);
#endif /* !defined(__linux) && !defined(HAVE_BSDTUNTAP) */
  if (0 == errbuf[0]) {
    *eth_api = ETH_API_TAP;
    *handle = (void *)1;  /* Flag used to indicated open */
    }
  }
else { /* !tap: */
  if (0 == strncmp("vde:", savname, 4)) {
#if defined(HAVE_VDE_NETWORK)
    struct vde_open_args voa;
    const char *devname = savname + 4;

    memset(&voa, 0, sizeof(voa));
    if (!strcmp(savname, "vde:vdedevice")) {
      sim_printf ("Eth: Must specify actual vde device name (i.e. vde:/tmp/switch)\r\n");
      return SCPE_OPENERR | SCPE_NOMESSAGE;
      }
    while (isspace(*devname))
        ++devname;
    if (!(*handle = (void*) vde_open((char *)devname, (char *)"simh", &voa)))
      strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1);
    else {
      *eth_api = ETH_API_VDE;
      *fd_handle = vde_datafd((VDECONN*)(*handle));
      }
#else
    strncpy(errbuf, "No support for vde: network devices", PCAP_ERRBUF_SIZE-1);
#endif /* defined(HAVE_VDE_NETWORK) */
    }
  else { /* !vde: */
    if (0 == strncmp("nat:", savname, 4)) {
#if defined(HAVE_SLIRP_NETWORK)
      const char *devname = savname + 4;

      while (isspace(*devname))
          ++devname;
      if (!(*handle = (void*) sim_slirp_open(devname, opaque, &_slirp_callback, dptr, dbit)))
        strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1);
      else {
        *eth_api = ETH_API_NAT;
        *fd_handle = 0;
        }
#else
      strncpy(errbuf, "No support for nat: network devices", PCAP_ERRBUF_SIZE-1);
#endif /* defined(HAVE_SLIRP_NETWORK) */
      }
    else { /* not nat: */
      if (0 == strncmp("udp:", savname, 4)) {
        char localport[CBUFSIZE], host[CBUFSIZE], port[CBUFSIZE];
        char hostport[2*CBUFSIZE];
        const char *devname = savname + 4;

        if (!strcmp(savname, "udp:sourceport:remotehost:remoteport")) {
          sim_printf ("Eth: Must specify actual udp host and ports(i.e. udp:1224:somehost.com:2234)\r\n");
          return SCPE_OPENERR | SCPE_NOMESSAGE;
          }

        while (isspace(*devname))
            ++devname;
        if (SCPE_OK != sim_parse_addr_ex (devname, host, sizeof(host), "localhost", port, sizeof(port), localport, sizeof(localport), NULL))
          return SCPE_OPENERR;

        if (localport[0] == '\0')
          strcpy (localport, port);
        sprintf (hostport, "%s:%s", host, port);
        if ((SCPE_OK == sim_parse_addr (hostport, NULL, 0, NULL, NULL, 0, NULL, "localhost")) &&
            (0 == strcmp (localport, port))) {
          sim_printf ("Eth: Must specify different udp localhost ports\r\n");
          return SCPE_OPENERR | SCPE_NOMESSAGE;
          }
        *fd_handle = sim_connect_sock_ex (localport, hostport, NULL, NULL, SIM_SOCK_OPT_DATAGRAM);
        if (INVALID_SOCKET == *fd_handle)
            return SCPE_OPENERR;
        *eth_api = ETH_API_UDP;
        *handle = (void *)1;  /* Flag used to indicated open */
        }
      else { /* not udp:, so attempt to open the parameter as if it were an explicit device name */
#if defined(HAVE_PCAP_NETWORK)
        *handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf);
        if (!*handle) { /* can't open device */
          sim_printf ("Eth: pcap_open_live error - %s\r\n", errbuf);
          return SCPE_OPENERR | SCPE_NOMESSAGE;
          }
        *eth_api = ETH_API_PCAP;
#if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__)
        /* Tell the kernel that the header is fully-formed when it gets it.
           This is required in order to fake the src address. */
        if (1) {
          int one = 1;
          ioctl(pcap_fileno(*handle), BIOCSHDRCMPLT, &one);
          }
#endif /* xBSD */
#if defined(_WIN32)
        pcap_setmintocopy ((pcap_t*)(*handle), 0);
#endif
#if !defined (USE_READER_THREAD)
#ifdef USE_SETNONBLOCK
        /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */
        if (pcap_setnonblock (*handle, 1, errbuf) == -1) {
          sim_printf ("Eth: Failed to set non-blocking: %s\r\n", errbuf);
          }
#endif
#if defined (__APPLE__)
        if (1) {
          /* Deliver packets immediately, needed for OS X 10.6.2 and later
           * (Snow-Leopard).
           * See this thread on libpcap and Mac Os X 10.6 Snow Leopard on
           * the tcpdump mailinglist: http://seclists.org/tcpdump/2010/q1/110
           */
          int v = 1;
          ioctl(pcap_fileno(*handle), BIOCIMMEDIATE, &v);
          }
#endif /* defined (__APPLE__) */
#endif /* !defined (USE_READER_THREAD) */
#else
        strncpy (errbuf, "Unknown or unsupported network device", PCAP_ERRBUF_SIZE-1);
#endif /* defined(HAVE_PCAP_NETWORK) */
        } /* not udp:, so attempt to open the parameter as if it were an explicit device name */
      } /* !nat: */
    } /* !vde: */
  } /* !tap: */
if (errbuf[0])
  return SCPE_OPENERR;

#ifdef USE_BPF
if (bpf_filter && (*eth_api == ETH_API_PCAP)) {
  struct bpf_program bpf;
  int status;
  bpf_u_int32  bpf_subnet, bpf_netmask;

  if (pcap_lookupnet(savname, &bpf_subnet, &bpf_netmask, errbuf)<0)
    bpf_netmask = 0;
  /* compile filter string */
  if ((status = pcap_compile((pcap_t*)(*handle), &bpf, bpf_filter, 1, bpf_netmask)) < 0) {
    sprintf(errbuf, "%s", pcap_geterr((pcap_t*)(*handle)));
    sim_printf("Eth: pcap_compile error: %s\r\n", errbuf);
    /* show erroneous BPF string */
    sim_printf ("Eth: BPF string is: |%s|\r\n", bpf_filter);
    }
  else {
    /* apply compiled filter string */
    if ((status = pcap_setfilter((pcap_t*)(*handle), &bpf)) < 0) {
      sprintf(errbuf, "%s", pcap_geterr((pcap_t*)(*handle)));
      sim_printf("Eth: pcap_setfilter error: %s\r\n", errbuf);
      }
    else {
#ifdef USE_SETNONBLOCK
      /* set file non-blocking */
      status = pcap_setnonblock ((pcap_t*)(*handle), 1, errbuf);
#endif /* USE_SETNONBLOCK */
      }
    pcap_freecode(&bpf);
    }
  }
#endif /* USE_BPF */
return SCPE_OK;
}

t_stat eth_open(ETH_DEV* dev, const char* name, DEVICE* dptr, uint32 dbit)
{
t_stat r;
int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ;
char errbuf[PCAP_ERRBUF_SIZE];
char temp[1024], desc[1024] = "";
const char* savname = name;
char namebuf[4*CBUFSIZE];
int   num;

if (bufsz < ETH_MAX_JUMBO_FRAME)
  bufsz = ETH_MAX_JUMBO_FRAME;    /* Enable handling of jumbo frames */

/* initialize device */
eth_zero(dev);

/* translate name of type "ethX" to real device name */
if ((strlen(name) == 4)
    && (tolower(name[0]) == 'e')
    && (tolower(name[1]) == 't')
    && (tolower(name[2]) == 'h')
    && isdigit(name[3])
   ) {
  num = atoi(&name[3]);
  savname = eth_getname(num, temp, desc);
  if (savname == NULL) /* didn't translate */
    return SCPE_OPENERR;
  }
else {
  /* are they trying to use device description? */
  savname = eth_getname_bydesc(name, temp, desc);
  if (savname == NULL) { /* didn't translate */
    /* probably is not ethX and has no description */
    savname = eth_getname_byname(name, temp, desc);
    if (savname == NULL) {/* didn't translate */
      savname = name;
      desc[0] = '\0';   /* no description */
      }
    }
  }

namebuf[sizeof(namebuf)-1] = '\0';
strncpy (namebuf, savname, sizeof(namebuf)-1);
savname = namebuf;
r = _eth_open_port(namebuf, &dev->eth_api, &dev->handle, &dev->fd_handle, errbuf, NULL, (void *)dev, dptr, dbit);

if (errbuf[0]) {
  sim_printf ("Eth: open error - %s\r\n", errbuf);
  return SCPE_OPENERR | SCPE_NOMESSAGE;
  }
if (r != SCPE_OK)
  return r;

if (!strcmp (desc, "No description available"))
    strcpy (desc, "");
sim_printf ("Eth: opened OS device %s%s%s\r\n", savname, desc[0] ? " - " : "", desc);

/* get the NIC's hardware MAC address */
eth_get_nic_hw_addr(dev, savname);

/* save name of device */
dev->name = (char *)malloc(strlen(savname)+1);
strcpy(dev->name, savname);

/* save debugging information */
dev->dptr = dptr;
dev->dbit = dbit;

#if defined (USE_READER_THREAD)
if (1) {
  pthread_attr_t attr;

  ethq_init (&dev->read_queue, 200);         /* initialize FIFO queue */
  pthread_mutex_init (&dev->lock, NULL);
  pthread_mutex_init (&dev->writer_lock, NULL);
  pthread_mutex_init (&dev->self_lock, NULL);
  pthread_cond_init (&dev->writer_cond, NULL);
  pthread_attr_init(&attr);
  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
#if defined(__hpux)
  {
    /* libpcap needs sizeof(long) * 8192 bytes on the stack */
    size_t stack_size;
    const size_t min_stack_size = sizeof(long) * 8192 * 3 / 2;
    if (!pthread_attr_getstacksize(&attr, &stack_size) && stack_size < min_stack_size) {
      pthread_attr_setstacksize(&attr, min_stack_size);
    }
  }
#endif /* defined(__hpux) */
  pthread_create (&dev->reader_thread, &attr, _eth_reader, (void *)dev);
  pthread_create (&dev->writer_thread, &attr, _eth_writer, (void *)dev);
  pthread_attr_destroy(&attr);
  }
#endif /* defined (USE_READER_THREAD */
_eth_add_to_open_list (dev);
return SCPE_OK;
}

static t_stat _eth_close_port(int eth_api, pcap_t *pcap, SOCKET pcap_fd)
{
switch (eth_api) {
#ifdef HAVE_PCAP_NETWORK
  case ETH_API_PCAP:
    pcap_close(pcap);
    break;
#endif
#ifdef HAVE_TAP_NETWORK
  case ETH_API_TAP:
    close(pcap_fd);
    break;
#endif
#ifdef HAVE_VDE_NETWORK
  case ETH_API_VDE:
    vde_close((VDECONN*)pcap);
    break;
#endif
#ifdef HAVE_SLIRP_NETWORK
  case ETH_API_NAT:
    sim_slirp_close((SLIRP*)pcap);
    break;
#endif
  case ETH_API_UDP:
    sim_close_sock(pcap_fd);
    break;
  }
return SCPE_OK;
}

t_stat eth_close(ETH_DEV* dev)
{
pcap_t *pcap;
SOCKET pcap_fd;

/* make sure device exists */
if (!dev) return SCPE_UNATT;

/* close the device */
pcap_fd = dev->fd_handle;                   /* save handle to possibly close later */
pcap = (pcap_t *)dev->handle;
dev->handle = NULL;
dev->fd_handle = 0;
dev->have_host_nic_phy_addr = 0;

#if defined (USE_READER_THREAD)
pthread_join (dev->reader_thread, NULL);
pthread_mutex_destroy (&dev->lock);
pthread_cond_signal (&dev->writer_cond);
pthread_join (dev->writer_thread, NULL);
pthread_mutex_destroy (&dev->self_lock);
pthread_mutex_destroy (&dev->writer_lock);
pthread_cond_destroy (&dev->writer_cond);
if (1) {
  ETH_WRITE_REQUEST *buffer;
   while (NULL != (buffer = dev->write_buffers)) {
    dev->write_buffers = buffer->next;
    free(buffer);
    }
  while (NULL != (buffer = dev->write_requests)) {
    dev->write_requests = buffer->next;
    free(buffer);
    }
  }
ethq_destroy (&dev->read_queue);         /* release FIFO queue */
#endif

_eth_close_port (dev->eth_api, pcap, pcap_fd);
sim_printf ("Eth: closed %s\r\n", dev->name);

/* clean up the mess */
free(dev->name);
free(dev->bpf_filter);
eth_zero(dev);
_eth_remove_from_open_list (dev);
return SCPE_OK;
}

t_stat eth_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "%s attach help\n\n", dptr->name);
fprintf (st, "   sim> SHOW ETHERNET\n");
fprintf (st, "   libpcap version 1.0.0\n");
fprintf (st, "   ETH devices:\n");
fprintf (st, "    eth0   en0                                  (No description available)\n");
#if defined(HAVE_TAP_NETWORK)
fprintf (st, "    eth1   tap:tapN                             (Integrated Tun/Tap support)\n");
#endif
#if defined(HAVE_SLIRP_NETWORK)
fprintf (st, "    eth2   vde:device                           (Integrated VDE support)\n");
#endif
#if defined(HAVE_SLIRP_NETWORK)
fprintf (st, "    eth3   nat:{optional-nat-parameters}        (Integrated NAT (SLiRP) support)\n");
#endif
fprintf (st, "    eth4   udp:sourceport:remotehost:remoteport (Integrated UDP bridge support)\n");
fprintf (st, "   sim> ATTACH %s eth0\n\n", dptr->name);
fprintf (st, "or equivalently:\n\n");
fprintf (st, "   sim> ATTACH %s en0\n\n", dptr->name);
#if defined(HAVE_SLIRP_NETWORK)
sim_slirp_attach_help (st, dptr, uptr, flag, cptr);
#endif
return SCPE_OK;
}

static int _eth_rand_byte()
{
static int rand_initialized = 0;

if (!rand_initialized)
  srand((unsigned int)sim_os_msec());
return (rand() & 0xFF);
}

t_stat eth_check_address_conflict (ETH_DEV* dev, 
                                   ETH_MAC* const mac)
{
ETH_PACK send, recv;
t_stat status;
uint32 i;
int responses = 0;
uint32 offset, function;
char mac_string[32];

eth_mac_fmt(mac, mac_string);
sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict for MAC address: %s\n", mac_string);

/* The process of checking address conflicts is used in two ways:
   1) to determine the behavior of the currently running packet 
      delivery facility regarding whether it may receive copies 
      of every packet sent (and how many). 
   2) to verify if a MAC address which this facility is planning 
      to use as the source address of packets is already in use 
      by some other node on the local network 
   Case #1, doesn't require (and explicitly doesn't want) any 
   interaction or response from other systems on the LAN so 
   therefore no considerations regarding switch packet forwarding 
   are important.  Meanwhile, Case #2 does require responses from 
   other components on the LAN to provide useful functionality. 
   The original designers of this mechanism did this when essentially 
   all LANs were single collision domains (i.e. ALL nodes which might 
   be affected by an address conflict were physically present on a single
   Ethernet cable which might have been extended by a couple of repeaters).
   Since that time, essentially no networks are single collision domains.  
   Thick and thinwire Ethernet cables don't exist and very few networks 
   even have hubs.  Today, essentially all LANs are deployed using one 
   or more layers of network switches.  In a switched LAN environment, the 
   switches on the LAN "learn" which ports on the LAN source traffic from 
   which MAC addresses and then forward traffic destined for particular 
   MAC address to the appropriate ports.  If a particular MAC address is
   already in use somewhere on the LAN, then the switches "know" where 
   it is.  The host based test using the loopback protocol is poorly 
   designed to detect this condition.  This test is performed by the host
   first changing the device's Physical MAC address to the address which
   is to be tested, and then sending a loopback packet FROM AND TO this
   MAC address with a loopback reply to be sent by a system which may be
   currently using the MAC address.  If no reply is received, then the 
   MAC address is presumed to be unused.  The sending of this packet will
   result in its delivery to the right system since the switch port/MAC
   address tables know where to deliver packets destined to this MAC 
   address, however the response it generates won't be delivered to the 
   system performing the test since the switches on the LAN won't know 
   about the local port being the right target for packets with this MAC 
   address.  A better test design to detect these conflicts would be for 
   the testing system to send a loopback packet FROM the current physical
   MAC address (BEFORE changing it) TO the MAC address being tested with 
   the loopback response coming to the current physical MAC address of 
   the device.  If a response is received, then the address is in use and
   the attempt to change the device's MAC address should fail.  Since we 
   can't change the software running in these simulators to implement this
   better conflict detection approach, we can still "do the right thing" 
   in the sim_ether layer.  We're already handling the loopback test 
   packets specially since we always had to avoid receiving the packets 
   which were being sent, but needed to allow for the incoming loopback 
   packets to be properly dealt with.  We can extend this current special
   handling to change outgoing "loopback to self" packets to have source 
   AND loopback destination addresses in the packets to be the host NIC's
   physical address.  The switch network will already know the correct 
   MAC/port relationship for the host NIC's physical address, so loopback 
   response packets will be delivered as needed.

   Code in _eth_write and _eth_callback provide the special handling to 
   perform the described loopback packet adjustments, and code in 
   eth_filter_hash makes sure that the loopback response packets are received.

   */

/* build a loopback forward request packet */
memset (&send, 0, sizeof(ETH_PACK));
send.len = ETH_MIN_PACKET;                              /* minimum packet size */
for (i=0; i<send.len; i++)
  send.msg[i] = _eth_rand_byte();
memcpy(&send.msg[0], mac, sizeof(ETH_MAC));             /* target address */
memcpy(&send.msg[6], mac, sizeof(ETH_MAC));             /* source address */
send.msg[12] = 0x90;                                    /* loopback packet type */
send.msg[13] = 0;
send.msg[14] = 0;                                       /* Offset */
send.msg[15] = 0;
send.msg[16] = 2;                                       /* Forward */
send.msg[17] = 0;
memcpy(&send.msg[18], mac, sizeof(ETH_MAC));            /* Forward Destination */
send.msg[24] = 1;                                       /* Reply */
send.msg[25] = 0;

eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0);

/* send the packet */
status = _eth_write (dev, &send, NULL);
if (status != SCPE_OK) {
  const char *msg;
  msg = (dev->eth_api == ETH_API_PCAP) ?
      "Eth: Error Transmitting packet: %s\r\n"
        "You may need to run as root, or install a libpcap version\r\n"
        "which is at least 0.9 from your OS vendor or www.tcpdump.org\r\n" :
      "Eth: Error Transmitting packet: %s\r\n"
        "You may need to run as root.\r\n";
  sim_printf(msg, strerror(errno));
  return status;
  }

sim_os_ms_sleep (300);   /* time for a conflicting host to respond */

eth_packet_trace_detail (dev, send.msg, send.len, "Sent-Address-Check");

/* empty the read queue and count the responses */
do {
  memset (&recv, 0, sizeof(ETH_PACK));
  status = eth_read (dev, &recv, NULL);
  eth_packet_trace_detail (dev, recv.msg, recv.len, "Recv-Address-Check");
  offset = 16 + (recv.msg[14] | (recv.msg[15] << 8));
  function = 0;
  if ((offset+2) < recv.len)
    function = recv.msg[offset] | (recv.msg[offset+1] << 8);
  if (((0 == memcmp(send.msg+12, recv.msg+12, 2)) &&   /* Protocol Match */
       (function == 1) &&                              /* Function is Reply */
       (0 == memcmp(&send.msg[offset], &recv.msg[offset], send.len-offset))) || /* Content Match */
      (0 == memcmp(send.msg, recv.msg, send.len)))     /* Packet Match (Reflection) */
    responses++;
  } while (recv.len > 0);

sim_debug(dev->dbit, dev->dptr, "Address Conflict = %d\n", responses);
return responses;
}

t_stat eth_reflect(ETH_DEV* dev)
{
/* Test with an address no NIC should have. */
/* We do this to avoid reflections from the wire, */
/* in the event that a simulated NIC has a MAC address conflict. */
static ETH_MAC mac = {0xfe,0xff,0xff,0xff,0xff,0xfe};

sim_debug(dev->dbit, dev->dptr, "Determining Reflections...\n");

dev->reflections = 0;
dev->reflections = eth_check_address_conflict (dev, &mac);

sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections);
return dev->reflections;
}

static void
_eth_error(ETH_DEV* dev, const char* where)
{
char msg[64];
const char *netname = "";
time_t now;

time(&now);
sim_printf ("%s", asctime(localtime(&now)));
switch (dev->eth_api) {
  case ETH_API_PCAP:
      netname = "pcap";
      break;
  case ETH_API_TAP:
      netname = "tap";
      break;
  case ETH_API_VDE:
      netname = "vde";
      break;
  case ETH_API_UDP:
      netname = "udp";
      break;
  case ETH_API_NAT:
      netname = "nat";
      break;
  }
sprintf(msg, "%s(%s): ", where, netname);
switch (dev->eth_api) {
#if defined(HAVE_PCAP_NETWORK)
  case ETH_API_PCAP:
      sim_printf ("%s%s\n", msg, pcap_geterr ((pcap_t*)dev->handle));
      break;
#endif
  default:
      sim_err_sock (INVALID_SOCKET, msg);
      break;
  }
#ifdef USE_READER_THREAD
pthread_mutex_lock (&dev->lock);
++dev->error_waiting_threads;
if (!dev->error_needs_reset)
  dev->error_needs_reset = (((dev->transmit_packet_errors + dev->receive_packet_errors)%ETH_ERROR_REOPEN_THRESHOLD) == 0);
pthread_mutex_unlock (&dev->lock);
#else
dev->error_needs_reset = (((dev->transmit_packet_errors + dev->receive_packet_errors)%ETH_ERROR_REOPEN_THRESHOLD) == 0);
#endif
/* Limit errors to 1 per second (per invoking thread (reader and writer)) */
sim_os_sleep (1);
/* 
 When all of the threads which can reference this ETH_DEV object are
 simultaneously waiting in this routine, we have the potential to close
 and reopen the network connection.
 We do this after ETH_ERROR_REOPEN_THRESHOLD total errors have occurred.  
 In practice could be as frequently as once every ETH_ERROR_REOPEN_THRESHOLD/2 
 seconds, but normally would be about once every 1.5*ETH_ERROR_REOPEN_THRESHOLD 
 seconds (ONLY when the error condition exists).
 */
#ifdef USE_READER_THREAD
pthread_mutex_lock (&dev->lock);
if ((dev->error_waiting_threads == 2) &&
    (dev->error_needs_reset)) {
#else
if (dev->error_needs_reset) {
#endif
  char errbuf[PCAP_ERRBUF_SIZE];
  t_stat r;

  _eth_close_port(dev->eth_api, (pcap_t *)dev->handle, dev->fd_handle);
  sim_os_sleep (ETH_ERROR_REOPEN_PAUSE);

  r = _eth_open_port(dev->name, &dev->eth_api, &dev->handle, &dev->fd_handle, errbuf, dev->bpf_filter, (void *)dev, dev->dptr, dev->dbit);
  dev->error_needs_reset = FALSE;
  if (r == SCPE_OK)
    sim_printf ("%s ReOpened: %s \n", msg, dev->name);
  else
    sim_printf ("%s ReOpen Attempt Failed: %s - %s\n", msg, dev->name, errbuf);
  ++dev->error_reopen_count;
  }
#ifdef USE_READER_THREAD
--dev->error_waiting_threads;
pthread_mutex_unlock (&dev->lock);
#endif
}

static
t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
{
int status = 1;   /* default to failure */

/* make sure device exists */
if ((!dev) || (dev->eth_api == ETH_API_NONE)) return SCPE_UNATT;

/* make sure packet exists */
if (!packet) return SCPE_ARG;

/* make sure packet is acceptable length */
if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) {
  int loopback_self_frame = LOOPBACK_SELF_FRAME(packet->msg, packet->msg);
  int loopback_physical_response = LOOPBACK_PHYSICAL_RESPONSE(dev, packet->msg);

  eth_packet_trace (dev, packet->msg, packet->len, "writing");

  /* record sending of loopback packet (done before actual send to avoid race conditions with receiver) */
  if (loopback_self_frame || loopback_physical_response) {
    /* Direct loopback responses to the host physical address since our physical address
       may not have been learned yet. */
    if (loopback_self_frame && dev->have_host_nic_phy_addr) {
      memcpy(&packet->msg[6],  dev->host_nic_phy_hw_addr, sizeof(ETH_MAC));
      memcpy(&packet->msg[18], dev->host_nic_phy_hw_addr, sizeof(ETH_MAC));
      eth_packet_trace (dev, packet->msg, packet->len, "writing-fixed");
    }
#ifdef USE_READER_THREAD
    pthread_mutex_lock (&dev->self_lock);
#endif
    dev->loopback_self_sent += dev->reflections;
    dev->loopback_self_sent_total++;
#ifdef USE_READER_THREAD
    pthread_mutex_unlock (&dev->self_lock);
#endif
  }

    /* dispatch write request (synchronous; no need to save write info to dev) */
  switch (dev->eth_api) {
#ifdef HAVE_PCAP_NETWORK
    case ETH_API_PCAP:
      status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len);
      break;
#endif
#ifdef HAVE_TAP_NETWORK
    case ETH_API_TAP:
      status = (((int)packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1);
      break;
#endif
#ifdef HAVE_VDE_NETWORK
    case ETH_API_VDE:
      status = vde_send((VDECONN*)dev->handle, (void *)packet->msg, packet->len, 0);
      if ((status == (int)packet->len) || (status == 0))
        status = 0;
      else
        if ((status == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
          status = 0;
        else
          status = 1;
      break;
#endif
#ifdef HAVE_SLIRP_NETWORK
    case ETH_API_NAT:
      status = sim_slirp_send((SLIRP*)dev->handle, (char *)packet->msg, (size_t)packet->len, 0);
      if ((status == (int)packet->len) || (status == 0))
        status = 0;
      else
        status = 1;
      break;
#endif
    case ETH_API_UDP:
      status = (((int32)packet->len == sim_write_sock (dev->fd_handle, (char *)packet->msg, (int32)packet->len)) ? 0 : -1);
      break;
    }
  ++dev->packets_sent;              /* basic bookkeeping */
  /* On error, correct loopback bookkeeping */
  if ((status != 0) && loopback_self_frame) {
#ifdef USE_READER_THREAD
    pthread_mutex_lock (&dev->self_lock);
#endif
    dev->loopback_self_sent -= dev->reflections;
    dev->loopback_self_sent_total--;
#ifdef USE_READER_THREAD
    pthread_mutex_unlock (&dev->self_lock);
#endif
    }
  if (status != 0) {
    ++dev->transmit_packet_errors;
    _eth_error (dev, "_eth_write");
    }

  } /* if packet->len */

/* call optional write callback function */
if (routine)
  (routine)(status);

return ((status == 0) ? SCPE_OK : SCPE_IOERR);
}

t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
{
#ifdef USE_READER_THREAD
ETH_WRITE_REQUEST *request;
int write_queue_size = 1;

/* make sure device exists */
if ((!dev) || (dev->eth_api == ETH_API_NONE)) return SCPE_UNATT;

/* Get a buffer */
pthread_mutex_lock (&dev->writer_lock);
if (NULL != (request = dev->write_buffers))
  dev->write_buffers = request->next;
pthread_mutex_unlock (&dev->writer_lock);
if (NULL == request)
  request = (ETH_WRITE_REQUEST *)malloc(sizeof(*request));

/* Copy buffer contents */
request->packet.len = packet->len;
request->packet.used = packet->used;
request->packet.status = packet->status;
request->packet.crc_len = packet->crc_len;
memcpy(request->packet.msg, packet->msg, packet->len);

/* Insert buffer at the end of the write list (to make sure that */
/* packets make it to the wire in the order they were presented here) */
pthread_mutex_lock (&dev->writer_lock);
request->next = NULL;
if (dev->write_requests) {
  ETH_WRITE_REQUEST *last_request = dev->write_requests;

  ++write_queue_size;
  while (last_request->next) {
    last_request = last_request->next;
    ++write_queue_size;
    }
  last_request->next = request;
  }
else
    dev->write_requests = request;
if (write_queue_size > dev->write_queue_peak)
  dev->write_queue_peak = write_queue_size;
pthread_mutex_unlock (&dev->writer_lock);

/* Awaken writer thread to perform actual write */
pthread_cond_signal (&dev->writer_cond);

/* Return with a status from some prior write */
if (routine)
  (routine)(dev->write_status);
return dev->write_status;
#else
return _eth_write(dev, packet, routine);
#endif
}

static int
_eth_hash_lookup(ETH_MULTIHASH hash, const u_char* data)
{
int key = 0x3f & (eth_crc32(0, data, 6) >> 26);

key ^= 0x3f;
return (hash[key>>3] & (1 << (key&0x7)));
}

#if 0
static int
_eth_hash_validate(ETH_MAC *MultiCastList, int count, ETH_MULTIHASH hash)
{
ETH_MULTIHASH lhash;
int i;

memset(lhash, 0, sizeof(lhash));
for (i=0; i<count; ++i) {
  int key = 0x3f & (eth_crc32(0, MultiCastList[i], 6) >> 26);

  key ^= 0x3F;
  printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X Key: %X, Byte: %X, Val: %X\n", 
      MultiCastList[i][0], MultiCastList[i][1], MultiCastList[i][2], MultiCastList[i][3], MultiCastList[i][4], MultiCastList[i][5], 
      key, key>>3, (1 << (key&0x7)));
  lhash[key>>3] |= (1 << (key&0x7));
  }
if (memcmp(hash, lhash, sizeof(lhash))) {
  printf("Inconsistent Computed Hash:\n");
  printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", 
         hash[0], hash[1], hash[2], hash[3], 
         hash[4], hash[5], hash[6], hash[7]);
  printf("Was:       %02X %02X %02X %02X %02X %02X %02X %02X\n", 
         lhash[0], lhash[1], lhash[2], lhash[3], 
         lhash[4], lhash[5], lhash[6], lhash[7]);
  }
else {
  printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", 
         hash[0], hash[1], hash[2], hash[3], 
         hash[4], hash[5], hash[6], hash[7]);
  printf("Was:       %02X %02X %02X %02X %02X %02X %02X %02X\n", 
         lhash[0], lhash[1], lhash[2], lhash[3], 
         lhash[4], lhash[5], lhash[6], lhash[7]);
  }
return 0;
}

static void
_eth_test_multicast_hash()
{
ETH_MAC tMacs[] = {
                   {0xAB, 0x00, 0x04, 0x01, 0xAC, 0x10},
                   {0xAB, 0x00, 0x00, 0x04, 0x00, 0x00},
                   {0x09, 0x00, 0x2B, 0x00, 0x00, 0x0F},
                   {0x09, 0x00, 0x2B, 0x02, 0x01, 0x04},
                   {0x09, 0x00, 0x2B, 0x02, 0x01, 0x07},
                   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
                   {0x01, 0x00, 0x5E, 0x00, 0x00, 0x01}};
ETH_MULTIHASH thash = {0x01, 0x40, 0x00, 0x00, 0x48, 0x88, 0x40, 0x00};

_eth_hash_validate(tMacs, sizeof(tMacs)/sizeof(tMacs[0]), thash);
}
#endif

/* The IP header */
struct IPHeader {
  uint8 verhlen;          /* Version & Header Length in dwords */
#define IP_HLEN(IP) (((IP)->verhlen&0xF)<<2) /* Header Length in Bytes */
#define IP_VERSION(IP) ((((IP)->verhlen)>>4)&0xF) /* IP Version */
  uint8 tos;              /* Type of service */
  uint16 total_len;       /* Length of the packet in dwords */
  uint16 ident;           /* unique identifier */
  uint16 flags;           /* Fragmentation Flags */
#define IP_DF_FLAG (0x4000)
#define IP_MF_FLAG (0x2000)
#define IP_OFFSET_MASK (0x1FFF)
#define IP_FRAG_DF(IP) (ntohs(((IP)->flags))&IP_DF_FLAG)
#define IP_FRAG_MF(IP) (ntohs(((IP)->flags))&IP_MF_FLAG)
#define IP_FRAG_OFFSET(IP) (ntohs(((IP)->flags))&IP_OFFSET_MASK)
  uint8 ttl;              /* Time to live */
  uint8 proto;            /* Protocol number (TCP, UDP etc) */
  uint16 checksum;        /* IP checksum */
  uint32 source_ip;       /* Source Address */
  uint32 dest_ip;         /* Destination Address */
  };

/* ICMP header */
struct ICMPHeader {
  uint8 type;          /* ICMP packet type */
  uint8 code;          /* Type sub code */
  uint16 checksum;     /* ICMP Checksum */
  uint32 otherstuff[1];/* optional data */
  };

struct UDPHeader {
  uint16 source_port;
  uint16 dest_port;
  uint16 length;      /* The length of the entire UDP datagram, including both header and Data fields. */
  uint16 checksum;
  };

struct TCPHeader {
  uint16 source_port;
  uint16 dest_port;
  uint32 sequence_number;
  uint32 acknowledgement_number;
  uint16 data_offset_and_flags;
#define TCP_DATA_OFFSET(TCP) ((ntohs((TCP)->data_offset_and_flags)>>12)<<2)
#define TCP_CWR_FLAG (0x80)
#define TCP_ECR_FLAG (0x40)
#define TCP_URG_FLAG (0x20)
#define TCP_ACK_FLAG (0x10)
#define TCP_PSH_FLAG (0x08)
#define TCP_RST_FLAG (0x04)
#define TCP_SYN_FLAG (0x02)
#define TCP_FIN_FLAG (0x01)
#define TCP_FLAGS_MASK (0xFFF)
  uint16 window;
  uint16 checksum;
  uint16 urgent;
  uint16 otherstuff[1];  /* The rest of the packet */
  };

#ifndef IPPROTO_TCP
#define IPPROTO_TCP             6               /* tcp */
#endif
#ifndef IPPROTO_UDP
#define IPPROTO_UDP             17              /* user datagram protocol */
#endif
#ifndef IPPROTO_ICMP
#define IPPROTO_ICMP            1               /* control message protocol */
#endif

static uint16 
ip_checksum(uint16 *buffer, int size) 
{
unsigned long cksum = 0;
    
/* Sum all the words together, adding the final byte if size is odd  */
while (size > 1) {
  cksum += *buffer++;
  size -= sizeof(*buffer);
}
if (size) {
  uint16 endword;
  uint8 *endbytes = (uint8 *)&endword;

  endbytes[0] = *((uint8 *)buffer);
  endbytes[1] = 0;
  cksum += endword;
  }

/* Do a little shuffling  */
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
    
/* Return the bitwise complement of the resulting mishmash  */
return (uint16)(~cksum);
}

static uint16 
pseudo_checksum(uint16 len, uint16 proto, uint16 *src_addr, uint16 *dest_addr, uint8 *buff)
{
uint32 sum;

/* Sum the data first */
sum = 0xffff&(~ip_checksum((uint16 *)buff, len));

/* add the pseudo header which contains the IP source and destinationn addresses */
sum += src_addr[0];
sum += src_addr[1];
sum += dest_addr[0];
sum += dest_addr[1];
/* and the protocol number and the length of the UDP packet */
sum = sum + htons(proto) + htons(len);

/* Do a little shuffling  */
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
    
/* Return the bitwise complement of the resulting mishmash  */
return (uint16)(~sum);
}

static void
_eth_fix_ip_jumbo_offload(ETH_DEV* dev, u_char* msg, int len)
{
const unsigned short* proto = (const unsigned short*) &msg[12];
struct IPHeader *IP;
struct TCPHeader *TCP = NULL;
struct UDPHeader *UDP;
struct ICMPHeader *ICMP;
uint16 orig_checksum;
uint16 payload_len;
uint16 mtu_payload;
uint16 ip_flags;
uint16 frag_offset;
struct pcap_pkthdr header;
uint16 orig_tcp_flags;

/* Only interested in IP frames */
if (ntohs(*proto) != 0x0800) {
  ++dev->jumbo_dropped; /* Non IP Frames are dropped */
  return;
  }
IP = (struct IPHeader *)&msg[14];
if (IP_VERSION(IP) != 4) {
  ++dev->jumbo_dropped; /* Non IPv4 jumbo frames are dropped */
  return;
  }
if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) {
  ++dev->jumbo_dropped; /* Bogus header length frames are dropped */
  return;
  }
if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) {
  ++dev->jumbo_dropped; /* Previously fragmented, but currently jumbo sized frames are dropped */
  return;
  }
switch (IP->proto) {
  case IPPROTO_UDP:
    UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP));
    if (ntohs(UDP->length) > (len-IP_HLEN(IP))) {
      ++dev->jumbo_dropped; /* Bogus UDP packet length (packet contained length exceeds packet size) frames are dropped */
      return;
      }
    if (UDP->checksum == 0)
      break; /* UDP Checksums are disabled */
    orig_checksum = UDP->checksum;
    UDP->checksum = 0;
    UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP);
    if (orig_checksum != UDP->checksum)
      eth_packet_trace (dev, msg, len, "reading jumbo UDP header Checksum Fixed");
    break;
  case IPPROTO_ICMP:
    ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP));
    orig_checksum = ICMP->checksum;
    ICMP->checksum = 0;
    ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP));
    if (orig_checksum != ICMP->checksum)
      eth_packet_trace (dev, msg, len, "reading jumbo ICMP header Checksum Fixed");
    break;
  case IPPROTO_TCP:
    TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP));
    if ((TCP_DATA_OFFSET(TCP) > (len-IP_HLEN(IP))) || (TCP_DATA_OFFSET(TCP) < 20)) {
      ++dev->jumbo_dropped; /* Bogus TCP packet header length (packet contained length exceeds packet size) frames are dropped */
      return;
      }
    /* We don't do anything with the TCP checksum since we're going to resegment the TCP data below */
    break;
  default:
    ++dev->jumbo_dropped; /* We onlt handle UDP, ICMP and TCP jumbo frames others are dropped */
    return;
  }
/* Reasonable Checksums are now in the jumbo packet, but we've got to actually */
/* deliver ONLY standard sized ethernet frames.  Our job here is to now act as */
/* a router might have to and fragment these IPv4 frames as they are delivered */
/* into the virtual NIC. We do this by walking down the packet and dispatching */
/* a chunk at a time recomputing an appropriate header for each chunk. For */
/* datagram oriented protocols (UDP and ICMP) this is done by simple packet */
/* fragmentation.  For TCP this is done by breaking large packets into separate */
/* TCP packets. */
memset(&header, 0, sizeof(header));
switch (IP->proto) {
  case IPPROTO_UDP:
  case IPPROTO_ICMP:
    ++dev->jumbo_fragmented;
    /* When we're performing LSO (Large Send Offload), we're given a 
       'template' header which may not include a value being populated 
       in the IP header length (which is only 16 bits).
       We process as payload everything which isn't known header data. */
    payload_len = (uint16)(len - (14 + IP_HLEN(IP)));
    mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP));
    frag_offset = 0;
    while (payload_len > 0) {
      ip_flags = frag_offset;
      if (payload_len > mtu_payload) {
        ip_flags |= IP_MF_FLAG;
        IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP));
        }
      else {
        IP->total_len = htons(payload_len + IP_HLEN(IP));
        }
      IP->flags = htons(ip_flags);
      IP->checksum = 0;
      IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP));
      header.caplen = header.len = 14 + ntohs(IP->total_len);
      eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment");
#if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET
      if (1) {
        /* Debugging is easier if we read packets directly with pcap
           (i.e. we can use Wireshark to verify packet contents)
           we don't want to do this all the time for 2 reasons:
             1) sending through pcap involves kernel transitions and
             2) if the current system reflects sent packets, the 
                recieving side will receive and process 2 copies of 
                any packets sent this way. */
        ETH_PACK pkt;

        memset(&pkt, 0, sizeof(pkt));
        memcpy(pkt.msg, ((u_char *)IP)-14, header.len);
        pkt.len = header.len;
        _eth_write(dev, &pkt, NULL);
        }
#else
      _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14);
#endif
      payload_len -= (ntohs(IP->total_len) - IP_HLEN(IP));
      frag_offset += (ntohs(IP->total_len) - IP_HLEN(IP))>>3;
      if (payload_len > 0) {
        /* Move the MAC and IP headers down to just prior to the next payload segment */
        memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP));
        IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - IP_HLEN(IP));
        }
      }
    break;
  case IPPROTO_TCP:
    ++dev->jumbo_fragmented;
    eth_packet_trace_ex (dev, ((u_char *)IP)-14, len, "Fragmenting Jumbo TCP segment", 1, dev->dbit);
    TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP));
    orig_tcp_flags = ntohs(TCP->data_offset_and_flags);
    /* When we're performing LSO (Large Send Offload), we're given a 
       'template' header which may not include a value being populated 
       in the IP header length (which is only 16 bits).
       We process as payload everything which isn't known header data. */
    payload_len = (uint16)(len - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)));
    mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP));
    while (payload_len > 0) {
      if (payload_len > mtu_payload) {
        TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG));
        IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP));
        }
      else {
        TCP->data_offset_and_flags = htons(orig_tcp_flags);
        IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP));
        }
      IP->checksum = 0;
      IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP));
      TCP->checksum = 0;
      TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP);
      header.caplen = header.len = 14 + ntohs(IP->total_len);
      eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit);
#if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET
      if (1) {
        /* Debugging is easier if we read packets directly with pcap
           (i.e. we can use Wireshark to verify packet contents)
           we don't want to do this all the time for 2 reasons:
             1) sending through pcap involves kernel transitions and
             2) if the current system reflects sent packets, the 
                recieving side will receive and process 2 copies of 
                any packets sent this way. */
        ETH_PACK pkt;

        memset(&pkt, 0, sizeof(pkt));
        memcpy(pkt.msg, ((u_char *)IP)-14, header.len);
        pkt.len = header.len;
        _eth_write(dev, &pkt, NULL);
        }
#else
      _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14);
#endif
      payload_len -= (ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)));
      if (payload_len > 0) {
        /* Move the MAC, IP and TCP headers down to just prior to the next payload segment */
        memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP));
        IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)));
        TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP));
        TCP->sequence_number = htonl(mtu_payload + ntohl(TCP->sequence_number));
        }
      }
    break;
  }
}

static void
_eth_fix_ip_xsum_offload(ETH_DEV* dev, const u_char* msg, int len)
{
const unsigned short* proto = (const unsigned short*) &msg[12];
struct IPHeader *IP;
struct TCPHeader *TCP;
struct UDPHeader *UDP;
struct ICMPHeader *ICMP;
uint16 orig_checksum;

/* Only need to process locally originated packets */
if ((!dev->have_host_nic_phy_addr) || (memcmp(msg+6, dev->host_nic_phy_hw_addr, 6)))
  return;
/* Only interested in IP frames */
if (ntohs(*proto) != 0x0800)
  return;
IP = (struct IPHeader *)&msg[14];
if (IP_VERSION(IP) != 4)
  return; /* Only interested in IPv4 frames */
if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len))
  return; /* Bogus header length */
orig_checksum = IP->checksum;
IP->checksum = 0;
IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP));
if (orig_checksum != IP->checksum)
  eth_packet_trace (dev, msg, len, "reading IP header Checksum Fixed");
if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP))
  return; /* Insufficient data to compute payload checksum */
switch (IP->proto) {
  case IPPROTO_UDP:
    UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP));
    if (ntohs(UDP->length) > (len-IP_HLEN(IP)))
      return; /* packet contained length exceeds packet size */
    if (UDP->checksum == 0)
      return; /* UDP Checksums are disabled */
    orig_checksum = UDP->checksum;
    UDP->checksum = 0;
    UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP);
    if (orig_checksum != UDP->checksum)
      eth_packet_trace (dev, msg, len, "reading UDP header Checksum Fixed");
    break;
  case IPPROTO_TCP:
    TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP));
    orig_checksum = TCP->checksum;
    TCP->checksum = 0;
    TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP);
    if (orig_checksum != TCP->checksum)
      eth_packet_trace (dev, msg, len, "reading TCP header Checksum Fixed");
    break;
  case IPPROTO_ICMP:
    ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP));
    orig_checksum = ICMP->checksum;
    ICMP->checksum = 0;
    ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP));
    if (orig_checksum != ICMP->checksum)
      eth_packet_trace (dev, msg, len, "reading ICMP header Checksum Fixed");
    break;
  }
}

static int
_eth_process_loopback (ETH_DEV* dev, const u_char* data, uint32 len)
{
int protocol = data[12] | (data[13] << 8);
ETH_PACK  response;
uint32 offset, function;

if (protocol != 0x0090)     /* !ethernet loopback */
  return 0;

if (LOOPBACK_REFLECTION_TEST_PACKET(dev, data))
  return 0;                 /* Ignore reflection check packet */

offset   = 16 + (data[14] | (data[15] << 8));
if (offset >= len)
  return 0;
function = data[offset] | (data[offset+1] << 8);

if (function != 2) /*forward*/
  return 0;

/* The only packets we should be responding to are ones which 
   we received due to them being directed to our physical MAC address, 
   OR the Broadcast address OR to a Multicast address we're listening to 
   (we may receive others if we're in promiscuous mode, but shouldn't 
   respond to them) */
if ((0 == (data[0]&1)) &&           /* Multicast or Broadcast */
    (0 != memcmp(dev->filter_address[0], data, sizeof(ETH_MAC))))
  return 0;

/* Attempts to forward to multicast or broadcast addresses are explicitly 
   ignored by consuming the packet and doing nothing else */
if (data[offset+2]&1)
  return 1;

eth_packet_trace (dev, data, len, "rcvd");

sim_debug(dev->dbit, dev->dptr, "_eth_process_loopback()\n");

/* create forward response packet */
memset(&response, 0, sizeof(response));
response.len = len;
memcpy(response.msg, data, len);
memcpy(&response.msg[0], &response.msg[offset+2], sizeof(ETH_MAC));
memcpy(&response.msg[6], dev->filter_address[0], sizeof(ETH_MAC));
offset += 8 - 16; /* Account for the Ethernet Header and Offset value in this number  */
response.msg[14] = offset & 0xFF;
response.msg[15] = (offset >> 8) & 0xFF;

/* send response packet */
eth_write(dev, &response, NULL);

eth_packet_trace(dev, response.msg, response.len, ((function == 1) ? "loopbackreply" : "loopbackforward"));

++dev->loopback_packets_processed;

return 1;
}

static void
_eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data)
{
ETH_DEV*  dev = (ETH_DEV*) info;
int to_me;
int from_me = 0;
int i;
int bpf_used;

if (LOOPBACK_PHYSICAL_RESPONSE(dev, data)) {
  u_char *datacopy = (u_char *)malloc(header->len);

  /* Since we changed the outgoing loopback packet to have the physical MAC address of the
     host's interface instead of the programmatically set physical address of this pseudo
     device, we restore parts of the modified packet back as needed */
  memcpy(datacopy, data, header->len);
  memcpy(datacopy, dev->physical_addr, sizeof(ETH_MAC));
  memcpy(datacopy+18, dev->physical_addr, sizeof(ETH_MAC));
  _eth_callback(info, header, datacopy);
  free(datacopy);
  return;
}
switch (dev->eth_api) {
  case ETH_API_PCAP:
#ifdef USE_BPF
    bpf_used = 1;
    to_me = 1;
    /* AUTODIN II hash mode? */
    if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast))
      to_me = _eth_hash_lookup(dev->hash, data);
    break;
#endif /* USE_BPF */
  case ETH_API_TAP:
  case ETH_API_VDE:
  case ETH_API_UDP:
  case ETH_API_NAT:
    bpf_used = 0;
    to_me = 0;
    eth_packet_trace (dev, data, header->len, "received");

    for (i = 0; i < dev->addr_count; i++) {
      if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1;
      if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1;
    }

    /* all multicast mode? */
    if (dev->all_multicast && (data[0] & 0x01)) to_me = 1;

    /* promiscuous mode? */
    if (dev->promiscuous) to_me = 1;

    /* AUTODIN II hash mode? */
    if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01))
      to_me = _eth_hash_lookup(dev->hash, data);
    break;
  default:
    bpf_used = to_me = 0;                           /* Should NEVER happen */
    abort();
    break;
  }

/* detect reception of loopback packet to our physical address */
if ((LOOPBACK_SELF_FRAME(dev->physical_addr, data)) ||
    (LOOPBACK_PHYSICAL_REFLECTION(dev, data))) {
#ifdef USE_READER_THREAD
  pthread_mutex_lock (&dev->self_lock);
#endif
  dev->loopback_self_rcvd_total++;
  /* lower reflection count - if already zero, pass it on */
  if (dev->loopback_self_sent > 0) {
    eth_packet_trace (dev, data, header->len, "ignored");
    dev->loopback_self_sent--;
    to_me = 0;
    }
  else
    if (!bpf_used)
      from_me = 0;
#ifdef USE_READER_THREAD
  pthread_mutex_unlock (&dev->self_lock);
#endif
  }

if (bpf_used ? to_me : (to_me && !from_me)) {
  if (header->len > ETH_MIN_JUMBO_FRAME) {
    if (header->len <= header->caplen) {/* Whole Frame captured? */
      u_char *datacopy = (u_char *)malloc(header->len);
      memcpy(datacopy, data, header->len);
      _eth_fix_ip_jumbo_offload(dev, datacopy, header->len);
      free(datacopy);
      }
    else
      ++dev->jumbo_truncated;
    return;
    }
  if (_eth_process_loopback(dev, data, header->len))
    return;  
#if defined (USE_READER_THREAD)
  if (1) {
    int crc_len = 0;
    uint8 crc_data[4];
    uint32 len = header->len;
    u_char *moved_data = NULL;

    if (header->len < ETH_MIN_PACKET) {   /* Pad runt packets before CRC append */
      moved_data = (u_char *)malloc(ETH_MIN_PACKET);
      memcpy(moved_data, data, len);
      memset(moved_data + len, 0, ETH_MIN_PACKET-len);
      len = ETH_MIN_PACKET;
      data = moved_data;
      }

    /* If necessary, fix IP header checksums for packets originated locally */
    /* but were presumed to be traversing a NIC which was going to handle that task */
    /* This must be done before any needed CRC calculation */
    _eth_fix_ip_xsum_offload(dev, (const u_char*)data, len);
    
    if (dev->need_crc)
      crc_len = eth_get_packet_crc32_data(data, len, crc_data);

    eth_packet_trace (dev, data, len, "rcvqd");

    pthread_mutex_lock (&dev->lock);
    ethq_insert_data(&dev->read_queue, ETH_ITM_NORMAL, data, 0, len, crc_len, crc_data, 0);
    ++dev->packets_received;
    pthread_mutex_unlock (&dev->lock);
    free(moved_data);
    }
#else /* !USE_READER_THREAD */
  /* set data in passed read packet */
  dev->read_packet->len = header->len;
  memcpy(dev->read_packet->msg, data, header->len);
  /* Handle runt case and pad with zeros.  */
  /* The real NIC won't hand us runts from the wire, BUT we may be getting */
  /* some packets looped back before they actually traverse the wire */
  /* (by an internal bridge device for instance) */
  if (header->len < ETH_MIN_PACKET) {
    memset(&dev->read_packet->msg[header->len], 0, ETH_MIN_PACKET-header->len);
    dev->read_packet->len = ETH_MIN_PACKET;
    }
  /* If necessary, fix IP header checksums for packets originated by the local host */
  /* but were presumed to be traversing a NIC which was going to handle that task */
  /* This must be done before any needed CRC calculation */
  _eth_fix_ip_xsum_offload(dev, dev->read_packet->msg, dev->read_packet->len);
  if (dev->need_crc)
    dev->read_packet->crc_len = eth_add_packet_crc32(dev->read_packet->msg, dev->read_packet->len);
  else
    dev->read_packet->crc_len = 0;

  eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading");

  ++dev->packets_received;

  /* call optional read callback function */
  if (dev->read_callback)
    (dev->read_callback)(0);
#endif
  }
}

int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
{
int status;

/* make sure device exists */

if ((!dev) || (dev->eth_api == ETH_API_NONE)) return 0;

/* make sure packet exists */
if (!packet) return 0;

packet->len = 0;
#if !defined (USE_READER_THREAD)
/* set read packet */
dev->read_packet = packet;

/* set optional callback routine */
dev->read_callback = routine;

/* dispatch read request to either receive a filtered packet or timeout */
do {
  switch (dev->eth_api) {
#ifdef HAVE_PCAP_NETWORK
    case ETH_API_PCAP:
      status = pcap_dispatch((pcap_t*)dev->handle, 1, &_eth_callback, (u_char*)dev);
      break;
#endif
#ifdef HAVE_TAP_NETWORK
    case ETH_API_TAP:
      if (1) {
        struct pcap_pkthdr header;
        int len;
        u_char buf[ETH_MAX_JUMBO_FRAME];

        memset(&header, 0, sizeof(header));
        len = read(dev->fd_handle, buf, sizeof(buf));
        if (len > 0) {
          status = 1;
          header.caplen = header.len = len;
          _eth_callback((u_char *)dev, &header, buf);
          }
        else {
          if (len < 0)
            status = -1;
          else
            status = 0;
          }
        }
      break;
#endif /* HAVE_TAP_NETWORK */
#ifdef HAVE_VDE_NETWORK
    case ETH_API_VDE:
      if (1) {
        struct pcap_pkthdr header;
        int len;
        u_char buf[ETH_MAX_JUMBO_FRAME];

        memset(&header, 0, sizeof(header));
        len = vde_recv((VDECONN*)dev->handle, buf, sizeof(buf), 0);
        if (len > 0) {
          status = 1;
          header.caplen = header.len = len;
          _eth_callback((u_char *)dev, &header, buf);
          }
        else {
          if (len < 0)
            status = -1;
          else
            status = 0;
          }
        }
      break;
#endif /* HAVE_VDE_NETWORK */
    case ETH_API_UDP:
      if (1) {
        struct pcap_pkthdr header;
        int len;
        u_char buf[ETH_MAX_JUMBO_FRAME];

        memset(&header, 0, sizeof(header));
        len = (int)sim_read_sock (dev->fd_handle, (char *)buf, (int32)sizeof(buf));
        if (len > 0) {
          status = 1;
          header.caplen = header.len = len;
          _eth_callback((u_char *)dev, &header, buf);
          }
        else {
          if (len < 0)
            status = -1;
          else
            status = 0;
          }
        }
      break;
    }
  } while ((status > 0) && (0 == packet->len));
if (status < 0) {
  ++dev->receive_packet_errors;
  _eth_error (dev, "eth_reader");
  }

#else /* USE_READER_THREAD */

  status = 0;
  pthread_mutex_lock (&dev->lock);
  if (dev->read_queue.count > 0) {
    ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head];
    packet->len = item->packet.len;
    packet->crc_len = item->packet.crc_len;
    memcpy(packet->msg, item->packet.msg, ((packet->len > packet->crc_len) ? packet->len : packet->crc_len));
    status = 1;
    ethq_remove(&dev->read_queue);
  }
  pthread_mutex_unlock (&dev->lock);  
  if ((status) && (routine))
    routine(0);
#endif

return status;
}

t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses,
                  ETH_BOOL all_multicast, ETH_BOOL promiscuous)
{
return eth_filter_hash(dev, addr_count, addresses, 
                       all_multicast, promiscuous, 
                       NULL);
}

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

/* make sure device exists */
if (!dev) return SCPE_UNATT;

/* filter count OK? */
if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX))
  return SCPE_ARG;
else
  if (!addresses) return SCPE_ARG;

/* test reflections.  This is done early in this routine since eth_reflect */
/* calls eth_filter recursively and thus changes the state of the device. */
if (dev->reflections == -1)
  status = eth_reflect(dev);

/* set new filter addresses */
for (i = 0; i < addr_count; i++)
  memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC));
dev->addr_count = addr_count;

/* store other flags */
dev->all_multicast = all_multicast;
dev->promiscuous   = promiscuous;

/* store multicast hash data */
dev->hash_filter = (hash != NULL);
if (hash) {
  memcpy(dev->hash, hash, sizeof(*hash));
  sim_debug(dev->dbit, dev->dptr, "Multicast Hash: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n",
                                  dev->hash[0], dev->hash[1], dev->hash[2], dev->hash[3], 
                                  dev->hash[4], dev->hash[5], dev->hash[6], dev->hash[7]);
  }

/* print out filter information if debugging */
if (dev->dptr->dctrl & dev->dbit) {
  sim_debug(dev->dbit, dev->dptr, "Filter Set\n");
  for (i = 0; i < addr_count; i++) {
    char mac[20];
    eth_mac_fmt(&dev->filter_address[i], mac);
    sim_debug(dev->dbit, dev->dptr, "  Addr[%d]: %s\n", i, mac);
    }
  if (dev->all_multicast) {
    sim_debug(dev->dbit, dev->dptr, "All Multicast\n");
    }
  if (dev->promiscuous) {
    sim_debug(dev->dbit, dev->dptr, "Promiscuous\n");
    }
  }

/* setup BPF filters and other fields to minimize packet delivery */
strcpy(buf, "");

/* construct destination filters - since the real ethernet interface was set
   into promiscuous mode by eth_open(), we need to filter out the packets that
   our simulated interface doesn't want. */
if (!dev->promiscuous) {
  for (i = 0; i < addr_count; i++) {
    eth_mac_fmt(&dev->filter_address[i], mac);
    if (!strstr(buf, mac))    /* eliminate duplicates */
      sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "((", mac);
    }
  if (dev->all_multicast || dev->hash_filter)
    sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "((");
  if (strlen(buf) > 0)
    sprintf(&buf[strlen(buf)], ")");
  }

/* construct source filters - this prevents packets from being reflected back 
   by systems where WinPcap and libpcap cause packet reflections. Note that
   some systems do not reflect packets at all. This *assumes* that the 
   simulated NIC will not send out packets with multicast source fields. */
if ((addr_count > 0) && (dev->reflections > 0)) {
  if (strlen(buf) > 0)
    sprintf(&buf[strlen(buf)], " and ");
  sprintf (&buf[strlen(buf)], "not (");
  buf2 = &buf[strlen(buf)];
  for (i = 0; i < addr_count; i++) {
    if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */
    eth_mac_fmt(&dev->filter_address[i], mac);
    if (!strstr(buf2, mac))   /* eliminate duplicates */
      sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac);
    }
  sprintf (&buf[strlen(buf)], ")");
  if (1 == strlen(buf2)) {          /* all addresses were multicast? */
    buf[strlen(buf)-6] = '\0';      /* Remove "not ()" */
    if (strlen(buf) > 0)
        buf[strlen(buf)-5] = '\0';  /* remove " and " */
    }
  }
if (strlen(buf) > 0)
  sprintf(&buf[strlen(buf)], ")");
/* When changing the Physical Address on a LAN interface, VMS sends out a 
   loopback packet with the source and destination addresses set to the same 
   value as the Physical Address which is being setup.  This packet is
   designed to find and help diagnose MAC address conflicts (which also 
   include DECnet address conflicts). Normally, this packet would not be 
   seen by the sender, only by the other machine that has the same Physical 
   Address (or possibly DECnet address). If the ethernet subsystem is 
   reflecting packets, the network startup will fail to start if it sees the 
   reflected packet, since it thinks another system is using this Physical 
   Address (or DECnet address). We have to let these packets through, so 
   that if another machine has the same Physical Address (or DECnet address)
   that we can detect it. Both eth_write() and _eth_callback() help by 
   checking the reflection count - eth_write() adds the reflection count to
   dev->loopback_self_sent, and _eth_callback() check the value - if the
   dev->loopback_self_sent count is zero, then the packet has come from 
   another machine with the same address, and needs to be passed on to the 
   simulated machine. */
memset(dev->physical_addr, 0, sizeof(ETH_MAC));
dev->loopback_self_sent = 0;
/* check for physical address in filters */
if ((addr_count) && (dev->reflections > 0)) {
  for (i = 0; i < addr_count; i++) {
    if (dev->filter_address[i][0]&1)
      continue;  /* skip all multicast addresses */
    eth_mac_fmt(&dev->filter_address[i], mac);
    if (strcmp(mac, "00:00:00:00:00:00") != 0) {
      memcpy(dev->physical_addr, &dev->filter_address[i], sizeof(ETH_MAC));
      /* let packets through where dst and src are the same as our physical address */
      sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac);
      if (dev->have_host_nic_phy_addr) {
        eth_mac_fmt(&dev->host_nic_phy_hw_addr, mac);
        sprintf(&buf[strlen(buf)], " or ((ether dst %s) and (ether proto 0x9000))", mac);
      }
      break;
      }
    }
  }
if ((0 == strlen(buf)) && (!dev->promiscuous)) /* Empty filter means match nothing */
  strcpy(buf, "ether host fe:ff:ff:ff:ff:ff"); /* this should be a good match nothing filter */
sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf);

/* 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) {
  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));
    sim_printf("Eth: pcap_compile error: %s\r\n", errbuf);
    /* show erroneous BPF string */
    sim_printf ("Eth: BPF string is: |%s|\r\n", buf);
    }
  else {
    /* apply compiled filter string */
    if ((status = pcap_setfilter((pcap_t*)dev->handle, &bpf)) < 0) {
      sprintf(errbuf, "%s", pcap_geterr((pcap_t*)dev->handle));
      sim_printf("Eth: pcap_setfilter error: %s\r\n", errbuf);
      }
    else {
      /* Save BPF filter string */
      dev->bpf_filter = (char *)realloc(dev->bpf_filter, 1 + strlen(buf));
      strcpy (dev->bpf_filter, buf);
#ifdef USE_SETNONBLOCK
      /* set file non-blocking */
      status = pcap_setnonblock (dev->handle, 1, errbuf);
#endif /* USE_SETNONBLOCK */
      }
    pcap_freecode(&bpf);
    }
#ifdef USE_READER_THREAD
  pthread_mutex_lock (&dev->lock);
  ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */
  pthread_mutex_unlock (&dev->lock);
#endif
  }
#endif /* USE_BPF */

return SCPE_OK;
}

/*
     The libpcap provided API pcap_findalldevs() on most platforms, will 
     leverage the getifaddrs() API if it is available in preference to 
     alternate platform specific methods of determining the interface list.

     A limitation of getifaddrs() is that it returns only interfaces which
     have associated addresses.  This may not include all of the interesting
     interfaces that we are interested in since a host may have dedicated
     interfaces for a simulator, which is otherwise unused by the host.

     One could hand craft the the build of libpcap to specifically use 
     alternate methods to implement pcap_findalldevs().  However, this can 
     get tricky, and would then result in a sort of deviant libpcap.

     This routine exists to allow platform specific code to validate and/or 
     extend the set of available interfaces to include any that are not
     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)
  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)
      list[j] = list[j+1];
    --used;
    --i;
    }
  } /* for */

#if defined(_WIN32)
/* replace device description with user-defined adapter name (if defined) */
for (i=0; i<used; i++) {
  char regkey[2048];
  unsigned char regval[2048];
  LONG status;
  DWORD reglen, regtype;
  HKEY reghnd;

  /* These registry keys don't seem to exist for all devices, so we simply ignore errors. */
  /* Windows XP x64 registry uses wide characters by default,
     so we force use of narrow characters by using the 'A'(ANSI) version of RegOpenKeyEx.
     This could cause some problems later, if this code is internationalized. Ideally,
     the pcap lookup will return wide characters, and we should use them to build a wide
     registry key, rather than hardcoding the string as we do here. */
  if (list[i].name[strlen( "\\Device\\NPF_" )] == '{') {
    sprintf( regkey, "SYSTEM\\CurrentControlSet\\Control\\Network\\"
             "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s\\Connection", list[i].name+
             strlen( "\\Device\\NPF_" ) );
    if ((status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, regkey, 0, KEY_QUERY_VALUE, &reghnd)) != ERROR_SUCCESS)
      continue;
    reglen = sizeof(regval);

    /* look for user-defined adapter name, bail if not found */  
    /* same comment about Windows XP x64 (above) using RegQueryValueEx */
    if ((status = RegQueryValueExA (reghnd, "Name", NULL, &regtype, regval, &reglen)) != ERROR_SUCCESS) {
      RegCloseKey (reghnd);
      continue;
      }
    /* make sure value is the right type, bail if not acceptable */
    if ((regtype != REG_SZ) || (reglen > sizeof(regval))) {
      RegCloseKey (reghnd);
      continue;
      }
    /* registry value seems OK, finish up and replace description */
    RegCloseKey (reghnd );
    sprintf (list[i].desc, "%s", regval);
    }
  } /* for */
#endif

#ifdef HAVE_TAP_NETWORK
if (used < max) {
#if defined(__OpenBSD__)
  sprintf(list[used].name, "%s", "tap:tunN");
#else
  sprintf(list[used].name, "%s", "tap:tapN");
#endif
  sprintf(list[used].desc, "%s", "Integrated Tun/Tap support");
  list[used].eth_api = ETH_API_TAP;
  ++used;
  }
#endif
#ifdef HAVE_VDE_NETWORK
if (used < max) {
  sprintf(list[used].name, "%s", "vde:device");
  sprintf(list[used].desc, "%s", "Integrated VDE support");
  list[used].eth_api = ETH_API_VDE;
  ++used;
  }
#endif
#ifdef HAVE_SLIRP_NETWORK
if (used < max) {
  sprintf(list[used].name, "%s", "nat:{optional-nat-parameters}");
  sprintf(list[used].desc, "%s", "Integrated NAT (SLiRP) support");
  list[used].eth_api = ETH_API_NAT;
  ++used;
  }
#endif

if (used < max) {
  sprintf(list[used].name, "%s", "udp:sourceport:remotehost:remoteport");
  sprintf(list[used].desc, "%s", "Integrated UDP bridge support");
  list[used].eth_api = ETH_API_UDP;
  ++used;
  }

return used;
}

int eth_devices(int max, ETH_LIST* list)
{
int i = 0;
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 */
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
  sim_printf ("Eth: error in pcap_findalldevs: %s\r\n", errbuf);
  }
else {
  /* copy device list into the passed structure */
  for (i=0, dev=alldevs; dev && (i < max); dev=dev->next, ++i) {
    if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue;
    strncpy(list[i].name, dev->name, sizeof(list[i].name)-1);
    if (dev->description)
      strncpy(list[i].desc, dev->description, sizeof(list[i].desc)-1);
    else
      strncpy(list[i].desc, "No description available", sizeof(list[i].desc)-1);
    }

  /* free device list */
  pcap_freealldevs(alldevs);
  }
#endif

/* Add any host specific devices and/or validate those already found */
i = eth_host_devices(i, max, list);

/* If no devices were found and an error message was left in the buffer, display it */
if ((i == 0) && (errbuf[0])) {
    sim_printf ("Eth: pcap_findalldevs warning: %s\r\n", errbuf);
    }

/* return device count */
return i;
}

void eth_show_dev (FILE *st, ETH_DEV* dev)
{
fprintf(st, "Ethernet Device:\n");
if (!dev) {
  fprintf(st, "-- Not Attached\n");
  return;
  }
fprintf(st, "  Name:                    %s\n", dev->name);
fprintf(st, "  Reflections:             %d\n", dev->reflections);
fprintf(st, "  Self Loopbacks Sent:     %d\n", dev->loopback_self_sent_total);
fprintf(st, "  Self Loopbacks Rcvd:     %d\n", dev->loopback_self_rcvd_total);
if (dev->have_host_nic_phy_addr) {
  char hw_mac[20];

  eth_mac_fmt(&dev->host_nic_phy_hw_addr, hw_mac);
  fprintf(st, "  Host NIC Address:        %s\n", hw_mac);
  }
if (dev->jumbo_dropped)
  fprintf(st, "  Jumbo Dropped:           %d\n", dev->jumbo_dropped);
if (dev->jumbo_fragmented)
  fprintf(st, "  Jumbo Fragmented:        %d\n", dev->jumbo_fragmented);
if (dev->jumbo_truncated)
  fprintf(st, "  Jumbo Truncated:         %d\n", dev->jumbo_truncated);
if (dev->packets_sent)
  fprintf(st, "  Packets Sent:            %d\n", dev->packets_sent);
if (dev->transmit_packet_errors)
  fprintf(st, "  Send Packet Errors:      %d\n", dev->transmit_packet_errors);
if (dev->packets_received)
  fprintf(st, "  Packets Received:        %d\n", dev->packets_received);
if (dev->receive_packet_errors)
  fprintf(st, "  Read Packet Errors:      %d\n", dev->receive_packet_errors);
if (dev->error_reopen_count)
  fprintf(st, "  Error ReOpen Count:      %d\n", dev->error_reopen_count);
if (dev->loopback_packets_processed)
  fprintf(st, "  Loopback Packets:        %d\n", dev->loopback_packets_processed);
#if defined(USE_READER_THREAD)
fprintf(st, "  Asynch Interrupts:       %s\n", dev->asynch_io?"Enabled":"Disabled");
if (dev->asynch_io)
  fprintf(st, "  Interrupt Latency:       %d uSec\n", dev->asynch_io_latency);
if (dev->throttle_count)
  fprintf(st, "  Throttle Delays:         %d\n", dev->throttle_count);
fprintf(st, "  Read Queue: Count:       %d\n", dev->read_queue.count);
fprintf(st, "  Read Queue: High:        %d\n", dev->read_queue.high);
fprintf(st, "  Read Queue: Loss:        %d\n", dev->read_queue.loss);
fprintf(st, "  Peak Write Queue Size:   %d\n", dev->write_queue_peak);
#endif
if (dev->bpf_filter)
  fprintf(st, "  BPF Filter: %s\n", dev->bpf_filter);
#if defined(HAVE_SLIRP_NETWORK)
if (dev->eth_api == ETH_API_NAT)
  sim_slirp_show ((SLIRP *)dev->handle, st);
#endif
}
#endif /* USE_NETWORK */
Added src/sim_ether.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_ether.h: OS-dependent network information
  ------------------------------------------------------------------------------

   Copyright (c) 2002-2005, David T. Hittner

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

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

  Modification history:

  01-Mar-12  AGN  Cygwin doesn't have non-blocking pcap I/O pcap (it uses WinPcap)
  17-Nov-11  MP   Added dynamic loading of libpcap on *nix platforms
  30-Oct-11  MP   Added support for vde (Virtual Distributed Ethernet) networking
  18-Apr-11  MP   Fixed race condition with self loopback packets in 
                  multithreaded environments
  09-Dec-10  MP   Added support to determine if network address conflicts exist
  07-Dec-10  MP   Reworked DECnet self detection to the more general approach
                  of loopback self when any Physical Address is being set.
  04-Dec-10  MP   Changed eth_write to do nonblocking writes when 
                  USE_READER_THREAD is defined.
  07-Feb-08  MP   Added eth_show_dev to display ethernet state
  28-Jan-08  MP   Added eth_set_async
  23-Jan-08  MP   Added eth_packet_trace_ex and ethq_destroy
  30-Nov-05  DTH  Added CRC length to packet and more field comments
  04-Feb-04  DTH  Added debugging information
  14-Jan-04  MP   Generalized BSD support issues
  05-Jan-04  DTH  Added eth_mac_scan
  26-Dec-03  DTH  Added ethernet show and queue functions from pdp11_xq
  23-Dec-03  DTH  Added status to packet
  01-Dec-03  DTH  Added reflections, tweaked decnet fix items
  25-Nov-03  DTH  Verified DECNET_FIX, reversed ifdef to mainstream code
  14-Nov-03  DTH  Added #ifdef DECNET_FIX for problematic duplicate detection code
  07-Jun-03  MP   Added WIN32 support for DECNET duplicate address detection.
  05-Jun-03  DTH  Added used to struct eth_packet
  01-Feb-03  MP   Changed some uint8 strings to char* to reflect usage 
  22-Oct-02  DTH  Added all_multicast and promiscuous support
  21-Oct-02  DTH  Corrected copyright again
  16-Oct-02  DTH  Fixed copyright
  08-Oct-02  DTH  Integrated with 2.10-0p4, added variable vector and copyrights
  03-Oct-02  DTH  Beta version of xq/sim_ether released for SIMH 2.09-11
  15-Aug-02  DTH  Started XQ simulation

  ------------------------------------------------------------------------------
*/

#ifndef SIM_ETHER_H
#define SIM_ETHER_H

#include "sim_defs.h"
#include "sim_sock.h"

#ifdef  __cplusplus
extern "C" {
#endif

/* make common BSD code a bit easier to read in this file */
/* OS/X seems to define and compile using one of these BSD types */
#if defined(__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__)
#define xBSD 1
#endif
#if !defined(__FreeBSD__) && !defined(_WIN32) && !defined(VMS) && !defined(__CYGWIN__) && !defined(__APPLE__)
#define USE_SETNONBLOCK 1
#endif

/* cygwin dowsn't have the right features to use the threaded network I/O */
#if defined(__CYGWIN__) || defined(__ZAURUS__) // psco added check for Zaurus platform
#define DONT_USE_READER_THREAD
#endif

#if ((((defined(__sun) || defined(__sun__)) && defined(__i386__)) || defined(__linux)) && !defined(DONT_USE_READER_THREAD))
#define USE_READER_THREAD 1
#endif

#if defined(DONT_USE_READER_THREAD)
#undef USE_READER_THREAD
#endif

/* make common winpcap code a bit easier to read in this file */
#if defined(_WIN32) || defined(VMS) || defined(__CYGWIN__)
#define PCAP_READ_TIMEOUT -1
#else
#define PCAP_READ_TIMEOUT  1
#endif

/* set related values to have correct relationships */
#if defined (USE_READER_THREAD)
#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
#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 */
#if defined(USE_SHARED) && !defined(_WIN32) && !defined(HAVE_DLOPEN)
#undef USE_SHARED
#endif

/* USE_SHARED implies shared pcap, so force HAVE_PCAP_NETWORK */
#if defined(USE_SHARED) && !defined(HAVE_PCAP_NETWORK)
#define HAVE_PCAP_NETWORK 1
#endif

/*
  USE_BPF is defined to let this code leverage the libpcap/OS kernel provided 
  BPF packet filtering.  This generally will enhance performance.  It may not 
  be available in some environments and/or it may not work correctly, so 
  undefining this will still provide working code here.
*/
#if defined(HAVE_PCAP_NETWORK)
#define USE_BPF 1
#if defined (_WIN32) && !defined (BPF_CONST_STRING)
#define BPF_CONST_STRING 1
#endif
#else
#define DONT_USE_PCAP_FINDALLDEVS 1
#endif

#if defined (USE_READER_THREAD)
#include <pthread.h>
#endif

/* structure declarations */

#define ETH_PROMISC            1                        /* promiscuous mode = true */
#define ETH_TIMEOUT           -1                        /* read timeout in milliseconds (immediate) */
#define ETH_FILTER_MAX        20                        /* maximum address filters */
#define ETH_DEV_NAME_MAX     256                        /* maximum device name size */
#define ETH_DEV_DESC_MAX     256                        /* maximum device description size */
#define ETH_MIN_PACKET        60                        /* minimum ethernet packet size */
#define ETH_MAX_PACKET      1514                        /* maximum ethernet packet size */
#define ETH_MAX_JUMBO_FRAME 65536                       /* maximum ethernet jumbo frame size (or Offload Segment Size) */
#define ETH_MAX_DEVICE        20                        /* maximum ethernet devices */
#define ETH_CRC_SIZE           4                        /* ethernet CRC size */
#define ETH_FRAME_SIZE (ETH_MAX_PACKET+ETH_CRC_SIZE)    /* ethernet maximum frame size */
#define ETH_MIN_JUMBO_FRAME ETH_MAX_PACKET              /* Threshold size for Jumbo Frame Processing */

#define LOOPBACK_SELF_FRAME(phy_mac, msg)                                                     \
    (((msg)[12] == 0x90) && ((msg)[13] == 0x00) &&              /* Ethernet Loopback */       \
     ((msg)[16] == 0x02) && ((msg)[17] == 0x00) &&              /* Forward Function */        \
     ((msg)[24] == 0x01) && ((msg)[25] == 0x00) &&              /* Next Function - Reply */   \
     (memcmp(phy_mac, (msg),    6) == 0) &&                     /* Ethernet Destination */    \
     (memcmp(phy_mac, (msg)+6,  6) == 0) &&                     /* Ethernet Source */         \
     (memcmp(phy_mac, (msg)+18, 6) == 0))                       /* Forward Address */

#define LOOPBACK_PHYSICAL_RESPONSE(dev, msg)                                                    \
    ((dev->have_host_nic_phy_addr) &&                                                           \
     ((msg)[12] == 0x90) && ((msg)[13] == 0x00) &&              /* Ethernet Loopback */         \
     ((msg)[14] == 0x08) && ((msg)[15] == 0x00) &&              /* Skipcount - 8 */             \
     ((msg)[16] == 0x02) && ((msg)[17] == 0x00) &&              /* Last Function - Forward */   \
     ((msg)[24] == 0x01) && ((msg)[25] == 0x00) &&              /* Function - Reply */          \
     (memcmp(dev->host_nic_phy_hw_addr, (msg)+18, 6) == 0) &&   /* Forward Address - Host MAC */\
     (memcmp(dev->host_nic_phy_hw_addr, (msg),    6) == 0) &&   /* Ethernet Source - Host MAC */\
     (memcmp(dev->physical_addr,  (msg)+6,  6) == 0))           /* Ethernet Source */

#define LOOPBACK_PHYSICAL_REFLECTION(dev, msg)                                                  \
    ((dev->have_host_nic_phy_addr) &&                                                           \
     ((msg)[12] == 0x90) && ((msg)[13] == 0x00) &&              /* Ethernet Loopback */         \
     ((msg)[16] == 0x02) && ((msg)[17] == 0x00) &&              /* Forward Function */          \
     ((msg)[24] == 0x01) && ((msg)[25] == 0x00) &&              /* Next Function - Reply */     \
     (memcmp(dev->host_nic_phy_hw_addr, (msg)+6,  6) == 0) &&   /* Ethernet Source - Host MAC */\
     (memcmp(dev->host_nic_phy_hw_addr, (msg)+18, 6) == 0))     /* Forward Address - Host MAC */

#define LOOPBACK_REFLECTION_TEST_PACKET(dev, msg)                                                \
    ((dev->have_host_nic_phy_addr) &&                                                            \
     ((msg)[12] == 0x90) && ((msg)[13] == 0x00) &&             /* Ethernet Loopback */           \
     ((msg)[14] == 0x00) && ((msg)[15] == 0x00) &&             /* Skipcount - 0 */               \
     ((msg)[16] == 0x02) && ((msg)[17] == 0x00) &&             /* Forward Function */            \
     ((msg)[24] == 0x01) && ((msg)[25] == 0x00) &&             /* Next Function - Reply */       \
     ((msg)[00] == 0xFE) && ((msg)[01] == 0xFF) &&             /* Ethernet Destination - Reflection Test MAC */\
     ((msg)[02] == 0xFF) && ((msg)[03] == 0xFF) &&                                               \
     ((msg)[04] == 0xFF) && ((msg)[05] == 0xFE) &&                                               \
     (memcmp(dev->host_nic_phy_hw_addr, (msg)+6,  6) == 0))    /* Ethernet Source - Host MAC */

struct eth_packet {
  uint8   msg[ETH_FRAME_SIZE];                          /* ethernet frame (message) */
  uint8   *oversize;                                    /* oversized frame (message) */
  uint32  len;                                          /* packet length without CRC */
  uint32  used;                                         /* bytes processed (used in packet chaining) */
  int     status;                                       /* transmit/receive status */
  uint32  crc_len;                                      /* packet length with CRC */
};

struct eth_item {
  int                 type;                             /* receive (0=setup, 1=loopback, 2=normal) */
#define ETH_ITM_SETUP    0
#define ETH_ITM_LOOPBACK 1
#define ETH_ITM_NORMAL   2
  struct eth_packet   packet;
};

struct eth_queue {
  int                 max;
  int                 count;
  int                 head;
  int                 tail;
  int                 loss;
  int                 high;
  struct eth_item*    item;
};

struct eth_list {
  char    name[ETH_DEV_NAME_MAX];
  char    desc[ETH_DEV_DESC_MAX];
  int     eth_api;
};

typedef int ETH_BOOL;
typedef unsigned char ETH_MAC[6];
typedef unsigned char ETH_MULTIHASH[8];
typedef struct eth_packet  ETH_PACK;
typedef void (*ETH_PCALLBACK)(int status);
typedef struct eth_list ETH_LIST;
typedef struct eth_queue ETH_QUE;
typedef struct eth_item ETH_ITEM;
struct eth_write_request {
  struct eth_write_request *next;
  ETH_PACK packet;
  };
typedef struct eth_write_request ETH_WRITE_REQUEST;

struct eth_device {
  char*         name;                                   /* name of ethernet device */
  void*         handle;                                 /* handle of implementation-specific device */
  SOCKET        fd_handle;                              /* fd to kernel device (where needed) */
  char*         bpf_filter;                             /* bpf filter currently in effect */
  int           eth_api;                                /* Designator for which API is being used to move packets */
#define ETH_API_NONE 0                                  /* No API in use yet */
#define ETH_API_PCAP 1                                  /* Pcap API in use */
#define ETH_API_TAP  2                                  /* tun/tap API in use */
#define ETH_API_VDE  3                                  /* VDE API in use */
#define ETH_API_UDP  4                                  /* UDP API in use */
#define ETH_API_NAT  5                                  /* NAT (SLiRP) API in use */
  ETH_PCALLBACK read_callback;                          /* read callback function */
  ETH_PCALLBACK write_callback;                         /* write callback function */
  ETH_PACK*     read_packet;                            /* read packet */
  ETH_MAC       filter_address[ETH_FILTER_MAX];         /* filtering addresses */
  int           addr_count;                             /* count of filtering addresses */
  ETH_BOOL      promiscuous;                            /* promiscuous mode flag */
  ETH_BOOL      all_multicast;                          /* receive all multicast messages */
  ETH_BOOL      hash_filter;                            /* filter using AUTODIN II multicast hash */
  ETH_MULTIHASH hash;                                   /* AUTODIN II multicast hash */
  int32         loopback_self_sent;                     /* loopback packets sent but not seen */
  int32         loopback_self_sent_total;               /* total loopback packets sent */
  int32         loopback_self_rcvd_total;               /* total loopback packets seen */
  ETH_MAC       physical_addr;                          /* physical address of interface */
  int32         have_host_nic_phy_addr;                 /* flag indicating that the host_nic_phy_hw_addr is valid */
  ETH_MAC       host_nic_phy_hw_addr;                   /* MAC address of the attached NIC */
  uint32        jumbo_fragmented;                       /* Giant IPv4 Frames Fragmented */
  uint32        jumbo_dropped;                          /* Giant Frames Dropped */
  uint32        jumbo_truncated;                        /* Giant Frames too big for capture buffer - Dropped */
  uint32        packets_sent;                           /* Total Packets Sent */
  uint32        packets_received;                       /* Total Packets Received */
  uint32        loopback_packets_processed;             /* Total Loopback Packets Processed */
  uint32        transmit_packet_errors;                 /* Total Send Packet Errors */
  uint32        receive_packet_errors;                  /* Total Read Packet Errors */
  int32         error_waiting_threads;                  /* Count of threads currently waiting after an error */
  ETH_BOOL      error_needs_reset;                      /* Flag indicating to force reset */
#define ETH_ERROR_REOPEN_THRESHOLD 10                   /* Attempt ReOpen after 20 send/receive errors */
#define ETH_ERROR_REOPEN_PAUSE 4                        /* Seconds to pause between closing and reopening LAN */
  uint32        error_reopen_count;                     /* Count of ReOpen Attempts */
  DEVICE*       dptr;                                   /* device ethernet is attached to */
  uint32        dbit;                                   /* debugging bit */
  int           reflections;                            /* packet reflections on interface */
  int           need_crc;                               /* device needs CRC (Cyclic Redundancy Check) */
  /* Throttling control parameters: */
  uint32        throttle_time;                          /* ms burst time window */
#define ETH_THROT_DEFAULT_TIME 5                        /* 5ms Default burst time window */
  uint32        throttle_burst;                         /* packets passed with throttle_time which trigger throttling */
#define ETH_THROT_DEFAULT_BURST 4                       /* 4 Packet burst in time window */
  uint32        throttle_delay;                         /* ms to delay when throttling.  0 disables throttling */
#define ETH_THROT_DISABLED_DELAY 0                      /* 0 Delay disables throttling */
#define ETH_THROT_DEFAULT_DELAY 10                      /* 10ms Delay during burst */
  /* Throttling state variables: */
  uint32        throttle_mask;                          /* match test for threshold detection (1 << throttle_burst) - 1 */
  uint32        throttle_events;                        /* keeps track of packet arrival values */
  uint32        throttle_packet_time;                   /* time last packet was transmitted */
  uint32        throttle_count;                         /* Total Throttle Delays */
#if defined (USE_READER_THREAD)
  int           asynch_io;                              /* Asynchronous Interrupt scheduling enabled */
  int           asynch_io_latency;                      /* instructions to delay pending interrupt */
  ETH_QUE       read_queue;
  pthread_mutex_t     lock;
  pthread_t     reader_thread;                          /* Reader Thread Id */
  pthread_t     writer_thread;                          /* Writer Thread Id */
  pthread_mutex_t     writer_lock;
  pthread_mutex_t     self_lock;
  pthread_cond_t      writer_cond;
  ETH_WRITE_REQUEST *write_requests;
  int write_queue_peak;
  ETH_WRITE_REQUEST *write_buffers;
  t_stat write_status;
#endif
};

typedef struct eth_device  ETH_DEV;

/* prototype declarations*/

t_stat eth_open   (ETH_DEV* dev, const char* name,      /* open ethernet interface */
                   DEVICE* dptr, uint32 dbit);
t_stat eth_close  (ETH_DEV* dev);                       /* close ethernet interface */
t_stat eth_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
t_stat eth_write  (ETH_DEV* dev, ETH_PACK* packet,      /* write sychronous packet; */
                   ETH_PCALLBACK routine);              /*  callback when done */
int eth_read      (ETH_DEV* dev, ETH_PACK* packet,      /* read single packet; */
                   ETH_PCALLBACK routine);              /*  callback when done*/
t_stat eth_filter (ETH_DEV* dev, int addr_count,        /* set filter on incoming packets */
                   ETH_MAC* const addresses,
                   ETH_BOOL all_multicast,
                   ETH_BOOL promiscuous);
t_stat eth_filter_hash (ETH_DEV* dev, int addr_count,   /* set filter on incoming packets with AUTODIN II based hash */
                        ETH_MAC* const addresses,
                        ETH_BOOL all_multicast,
                        ETH_BOOL promiscuous,
                        ETH_MULTIHASH* const hash);
t_stat eth_check_address_conflict (ETH_DEV* dev, 
                                   ETH_MAC* const address);
int eth_devices   (int max, ETH_LIST* dev);             /* get ethernet devices on host */
void eth_setcrc   (ETH_DEV* dev, int need_crc);         /* enable/disable CRC mode */
t_stat eth_set_async (ETH_DEV* dev, int latency);       /* set read behavior to be async */
t_stat eth_clr_async (ETH_DEV* dev);                    /* set read behavior to be not async */
t_stat eth_set_throttle (ETH_DEV* dev, uint32 time, uint32 burst, uint32 delay); /* set transmit throttle parameters */
uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len); /* Compute Ethernet Autodin II CRC for buffer */

void eth_packet_trace (ETH_DEV* dev, const uint8 *msg, int len, const char* txt); /* trace ethernet packet header+crc */
void eth_packet_trace_ex (ETH_DEV* dev, const uint8 *msg, int len, const char* txt, int detail, uint32 reason); /* trace ethernet packet */
t_stat eth_show (FILE* st, UNIT* uptr,                  /* show ethernet devices */
                 int32 val, CONST void* desc);
t_stat eth_show_devices (FILE* st, DEVICE *dptr,        /* show ethernet devices */
                         UNIT* uptr, int32 val, CONST char* desc);
void eth_show_dev (FILE*st, ETH_DEV* dev);              /* show ethernet device state */

void eth_mac_fmt (ETH_MAC* const add, char* buffer);    /* format ethernet mac address */
t_stat eth_mac_scan (ETH_MAC* mac, const char* strmac); /* scan string for mac, put in mac */
t_stat eth_mac_scan_ex (ETH_MAC* mac,                   /* scan string for mac, put in mac */
                        const char* strmac, UNIT *uptr);/* for specified unit */

t_stat ethq_init (ETH_QUE* que, int max);               /* initialize FIFO queue */
void ethq_clear  (ETH_QUE* que);                        /* clear FIFO queue */
void ethq_remove (ETH_QUE* que);                        /* remove item from FIFO queue */
void ethq_insert (ETH_QUE* que, int32 type,             /* insert item into FIFO queue */
                  ETH_PACK* packet, int32 status);
void ethq_insert_data(ETH_QUE* que, int32 type,         /* insert item into FIFO queue */
                  const uint8 *data, int used, size_t len, 
                  size_t crc_len, const uint8 *crc_data, int32 status);
t_stat ethq_destroy(ETH_QUE* que);                      /* release FIFO queue */

const char *eth_capabilities(void);

#ifdef  __cplusplus
}
#endif

#endif                                                  /* _SIM_ETHER_H */
Added src/sim_fio.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_fio.c: simulator file I/O library

   Copyright (c) 1993-2008, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   03-Jun-11    MP      Simplified VMS 64b support and made more portable
   02-Feb-11    MP      Added sim_fsize_ex and sim_fsize_name_ex returning t_addr
                        Added export of sim_buf_copy_swapped and sim_buf_swap_data
   28-Jun-07    RMS     Added VMS IA64 support (from Norm Lastovica)
   10-Jul-06    RMS     Fixed linux conditionalization (from Chaskiel Grundman)
   15-May-06    RMS     Added sim_fsize_name
   21-Apr-06    RMS     Added FreeBSD large file support (from Mark Martinec)
   19-Nov-05    RMS     Added OS/X large file support (from Peter Schorn)
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   17-Jul-04    RMS     Fixed bug in optimized sim_fread (reported by Scott Bailey)
   26-May-04    RMS     Optimized sim_fread (suggested by John Dundas)
   02-Jan-04    RMS     Split out from SCP

   This library includes:

   sim_finit         -       initialize package
   sim_fopen         -       open file
   sim_fread         -       endian independent read (formerly fxread)
   sim_write         -       endian independent write (formerly fxwrite)
   sim_fseek         -       conditionally extended (>32b) seek (
   sim_fseeko        -       extended seek (>32b if available)
   sim_fsize         -       get file size
   sim_fsize_name    -       get file size of named file
   sim_fsize_ex      -       get file size as a t_offset
   sim_fsize_name_ex -       get file size as a t_offset of named file
   sim_buf_copy_swapped -    copy data swapping elements along the way
   sim_buf_swap_data -       swap data elements inplace in buffer
   sim_shmem_open            create or attach to a shared memory region
   sim_shmem_close           close a shared memory region


   sim_fopen and sim_fseek are OS-dependent.  The other routines are not.
   sim_fsize is always a 32b routine (it is used only with small capacity random
   access devices like fixed head disks and DECtapes).
*/

#include "sim_defs.h"

t_bool sim_end;                     /* TRUE = little endian, FALSE = big endian */
t_bool sim_taddr_64;                /* t_addr is > 32b and Large File Support available */
t_bool sim_toffset_64;              /* Large File (>2GB) file I/O Support available */

#if defined(fprintf)                /* Make sure to only use the C rtl stream I/O routines */
#undef fprintf
#undef fputs
#undef fputc
#endif

/* OS-independent, endian independent binary I/O package

   For consistency, all binary data read and written by the simulator
   is stored in little endian data order.  That is, in a multi-byte
   data item, the bytes are written out right to left, low order byte
   to high order byte.  On a big endian host, data is read and written
   from high byte to low byte.  Consequently, data written on a little
   endian system must be byte reversed to be usable on a big endian
   system, and vice versa.

   These routines are analogs of the standard C runtime routines
   fread and fwrite.  If the host is little endian, or the data items
   are size char, then the calls are passed directly to fread or
   fwrite.  Otherwise, these routines perform the necessary byte swaps.
   Sim_fread swaps in place, sim_fwrite uses an intermediate buffer.
*/

int32 sim_finit (void)
{
union {int32 i; char c[sizeof (int32)]; } end_test;

end_test.i = 1;                                         /* test endian-ness */
sim_end = (end_test.c[0] != 0);
sim_toffset_64 = (sizeof(t_offset) > sizeof(int32));    /* Large File (>2GB) support */
sim_taddr_64 = sim_toffset_64 && (sizeof(t_addr) > sizeof(int32));
return sim_end;
}

void sim_buf_swap_data (void *bptr, size_t size, size_t count)
{
uint32 j;
int32 k;
unsigned char by, *sptr, *dptr;

if (sim_end || (count == 0) || (size == sizeof (char)))
    return;
for (j = 0, dptr = sptr = (unsigned char *) bptr;       /* loop on items */
     j < count; j++) { 
    for (k = (int32)(size - 1); k >= (((int32) size + 1) / 2); k--) {
        by = *sptr;                                     /* swap end-for-end */
        *sptr++ = *(dptr + k);
        *(dptr + k) = by;
        }
    sptr = dptr = dptr + size;                          /* next item */
    }
}

size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr)
{
size_t c;

if ((size == 0) || (count == 0))                        /* check arguments */
    return 0;
c = fread (bptr, size, count, fptr);                    /* read buffer */
if (sim_end || (size == sizeof (char)) || (c == 0))     /* le, byte, or err? */
    return c;                                           /* done */
sim_buf_swap_data (bptr, size, count);
return c;
}

void sim_buf_copy_swapped (void *dbuf, const void *sbuf, size_t size, size_t count)
{
size_t j;
int32 k;
const unsigned char *sptr = (const unsigned char *)sbuf;
unsigned char *dptr = (unsigned char *)dbuf;

if (sim_end || (size == sizeof (char))) {
    memcpy (dptr, sptr, size * count);
    return;
    }
for (j = 0; j < count; j++) {                           /* loop on items */
    for (k = (int32)(size - 1); k >= 0; k--)
        *(dptr + k) = *sptr++;
    dptr = dptr + size;
    }
}

size_t sim_fwrite (const void *bptr, size_t size, size_t count, FILE *fptr)
{
size_t c, nelem, nbuf, lcnt, total;
int32 i;
const unsigned char *sptr;
unsigned char *sim_flip;

if ((size == 0) || (count == 0))                        /* check arguments */
    return 0;
if (sim_end || (size == sizeof (char)))                 /* le or byte? */
    return fwrite (bptr, size, count, fptr);            /* done */
sim_flip = (unsigned char *)malloc(FLIP_SIZE);
if (!sim_flip)
    return 0;
nelem = FLIP_SIZE / size;                               /* elements in buffer */
nbuf = count / nelem;                                   /* number buffers */
lcnt = count % nelem;                                   /* count in last buf */
if (lcnt) nbuf = nbuf + 1;
else lcnt = nelem;
total = 0;
sptr = (const unsigned char *) bptr;                    /* init input ptr */
for (i = (int32)nbuf; i > 0; i--) {                     /* loop on buffers */
    c = (i == 1)? lcnt: nelem;
    sim_buf_copy_swapped (sim_flip, sptr, size, c);
    sptr = sptr + size * count;
    c = fwrite (sim_flip, size, c, fptr);
    if (c == 0) {
        free(sim_flip);
        return total;
        }
    total = total + c;
    }
free(sim_flip);
return total;
}

/* Forward Declaration */

t_offset sim_ftell (FILE *st);

/* Get file size */

t_offset sim_fsize_ex (FILE *fp)
{
t_offset pos, sz;

if (fp == NULL)
    return 0;
pos = sim_ftell (fp);
sim_fseek (fp, 0, SEEK_END);
sz = sim_ftell (fp);
sim_fseeko (fp, pos, SEEK_SET);
return sz;
}

t_offset sim_fsize_name_ex (const char *fname)
{
FILE *fp;
t_offset sz;

if ((fp = sim_fopen (fname, "rb")) == NULL)
    return 0;
sz = sim_fsize_ex (fp);
fclose (fp);
return sz;
}

uint32 sim_fsize_name (const char *fname)
{
return (uint32)(sim_fsize_name_ex (fname));
}

uint32 sim_fsize (FILE *fp)
{
return (uint32)(sim_fsize_ex (fp));
}

/* OS-dependent routines */

/* Optimized file open */

FILE *sim_fopen (const char *file, const char *mode)
{
#if defined (VMS)
return fopen (file, mode, "ALQ=32", "DEQ=4096",
        "MBF=6", "MBC=127", "FOP=cbt,tef", "ROP=rah,wbh", "CTX=stm");
#elif (defined (__linux) || defined (__linux__) || defined (__hpux) || defined (_AIX)) && !defined (DONT_DO_LARGEFILE)
return fopen64 (file, mode);
#else
return fopen (file, mode);
#endif
}

#if !defined (DONT_DO_LARGEFILE)
/* 64b VMS */

#if ((defined (__ALPHA) || defined (__ia64)) && defined (VMS) && (__DECC_VER >= 60590001)) || \
    ((defined(__sun) || defined(__sun__)) && defined(_LARGEFILE_SOURCE))
#define S_SIM_IO_FSEEK_EXT_ 1
int sim_fseeko (FILE *st, t_offset offset, int whence)
{
return fseeko (st, (off_t)offset, whence);
}

t_offset sim_ftell (FILE *st)
{
return (t_offset)(ftello (st));
}

#endif

/* Alpha UNIX - natively 64b */

#if defined (__ALPHA) && defined (__unix__)             /* Alpha UNIX */
#define S_SIM_IO_FSEEK_EXT_ 1
int sim_fseeko (FILE *st, t_offset offset, int whence)
{
return fseek (st, offset, whence);
}

t_offset sim_ftell (FILE *st)
{
return (t_offset)(ftell (st));
}

#endif

/* Windows */

#if defined (_WIN32)
#define S_SIM_IO_FSEEK_EXT_ 1
#include <sys/stat.h>

int sim_fseeko (FILE *st, t_offset offset, int whence)
{
fpos_t fileaddr;
struct _stati64 statb;

switch (whence) {

    case SEEK_SET:
        fileaddr = (fpos_t)offset;
        break;

    case SEEK_END:
        if (_fstati64 (_fileno (st), &statb))
            return (-1);
        fileaddr = statb.st_size + offset;
        break;
    case SEEK_CUR:
        if (fgetpos (st, &fileaddr))
            return (-1);
        fileaddr = fileaddr + offset;
        break;

    default:
        errno = EINVAL;
        return (-1);
        }

return fsetpos (st, &fileaddr);
}

t_offset sim_ftell (FILE *st)
{
fpos_t fileaddr;
if (fgetpos (st, &fileaddr))
    return (-1);
return (t_offset)fileaddr;
}

#endif                                                  /* end Windows */

/* Linux */

#if defined (__linux) || defined (__linux__) || defined (__hpux) || defined (_AIX)
#define S_SIM_IO_FSEEK_EXT_ 1
int sim_fseeko (FILE *st, t_offset xpos, int origin)
{
return fseeko64 (st, (off64_t)xpos, origin);
}

t_offset sim_ftell (FILE *st)
{
return (t_offset)(ftello64 (st));
}

#endif                                                  /* end Linux with LFS */

/* Apple OS/X */

#if defined (__APPLE__) || defined (__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) || defined (__CYGWIN__) 
#define S_SIM_IO_FSEEK_EXT_ 1
int sim_fseeko (FILE *st, t_offset xpos, int origin) 
{
return fseeko (st, (off_t)xpos, origin);
}

t_offset sim_ftell (FILE *st)
{
return (t_offset)(ftello (st));
}

#endif  /* end Apple OS/X */
#endif /* !DONT_DO_LARGEFILE */

/* Default: no OS-specific routine has been defined */

#if !defined (S_SIM_IO_FSEEK_EXT_)
int sim_fseeko (FILE *st, t_offset xpos, int origin)
{
return fseek (st, (long) xpos, origin);
}

t_offset sim_ftell (FILE *st)
{
return (t_offset)(ftell (st));
}
#endif

int sim_fseek (FILE *st, t_addr offset, int whence)
{
return sim_fseeko (st, (t_offset)offset, whence);
}

#if defined(_WIN32)
#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)
{
return -1;
}

struct SHMEM {
    HANDLE hMapping;
    size_t shm_size;
    void *shm_base;
    };

t_stat sim_shmem_open (const char *name, size_t size, SHMEM **shmem, void **addr)
{
*shmem = (SHMEM *)calloc (1, sizeof(**shmem));

if (*shmem == NULL)
    return SCPE_MEM;

(*shmem)->hMapping = INVALID_HANDLE_VALUE;
(*shmem)->shm_size = size;
(*shmem)->shm_base = NULL;
(*shmem)->hMapping = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)size, name);
if ((*shmem)->hMapping == INVALID_HANDLE_VALUE) {
    sim_shmem_close (*shmem);
    *shmem = NULL;
    return SCPE_OPENERR;
    }
(*shmem)->shm_base = MapViewOfFile ((*shmem)->hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if ((*shmem)->shm_base == NULL) {
    sim_shmem_close (*shmem);
    *shmem = NULL;
    return SCPE_OPENERR;
    }

*addr = (*shmem)->shm_base;
return SCPE_OK;
}

void sim_shmem_close (SHMEM *shmem)
{
if (shmem == NULL)
    return;
if (shmem->shm_base != NULL)
    UnmapViewOfFile (shmem->shm_base);
if (shmem->hMapping != INVALID_HANDLE_VALUE)
    CloseHandle (shmem->hMapping);
free (shmem);
}

#else /* !defined(_WIN32) */
#include <unistd.h>
int sim_set_fsize (FILE *fptr, t_addr size)
{
return ftruncate(fileno(fptr), (off_t)size);
}

#include <sys/stat.h>
#include <fcntl.h>

int sim_set_fifo_nonblock (FILE *fptr)
{
struct stat stbuf;

if (!fptr || fstat (fileno(fptr), &stbuf))
    return -1;
#if defined(S_IFIFO) && defined(O_NONBLOCK)
if ((stbuf.st_mode & S_IFIFO)) {
    int flags = fcntl(fileno(fptr), F_GETFL, 0);
    return fcntl(fileno(fptr), F_SETFL, flags | O_NONBLOCK);
    }
#endif
return -1;
}

#include <sys/mman.h>

struct SHMEM {
    int shm_fd;
    size_t shm_size;
    void *shm_base;
    };

t_stat sim_shmem_open (const char *name, size_t size, SHMEM **shmem, void **addr)
{
#ifdef HAVE_SHM_OPEN
*shmem = (SHMEM *)calloc (1, sizeof(**shmem));

*addr = NULL;
if (*shmem == NULL)
    return SCPE_MEM;

(*shmem)->shm_base = MAP_FAILED;
(*shmem)->shm_size = size;
(*shmem)->shm_fd = shm_open (name, O_RDWR, 0);
if ((*shmem)->shm_fd == -1) {
    (*shmem)->shm_fd = shm_open (name, O_CREAT | O_RDWR, 0660);
    if ((*shmem)->shm_fd == -1) {
        sim_shmem_close (*shmem);
        *shmem = NULL;
        return SCPE_OPENERR;
        }
    if (ftruncate((*shmem)->shm_fd, size)) {
        sim_shmem_close (*shmem);
        *shmem = NULL;
        return SCPE_OPENERR;
        }
    }
else {
    struct stat statb;

    if ((fstat ((*shmem)->shm_fd, &statb)) ||
        (statb.st_size != (*shmem)->shm_size)) {
        sim_shmem_close (*shmem);
        *shmem = NULL;
        return SCPE_OPENERR;
        }
    }
(*shmem)->shm_base = mmap(NULL, (*shmem)->shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, (*shmem)->shm_fd, 0);
if ((*shmem)->shm_base == MAP_FAILED) {
    sim_shmem_close (*shmem);
    *shmem = NULL;
    return SCPE_OPENERR;
    }
*addr = (*shmem)->shm_base;
return SCPE_OK;
#else
return SCPE_NOFNC;
#endif
}

void sim_shmem_close (SHMEM *shmem)
{
if (shmem == NULL)
    return;
if (shmem->shm_base != MAP_FAILED)
    munmap (shmem->shm_base, shmem->shm_size);
if (shmem->shm_fd != -1)
    close (shmem->shm_fd);
free (shmem);
}

#endif
Added src/sim_fio.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_fio.h: simulator file I/O library headers

   Copyright (c) 1993-2008, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   be used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   02-Feb-11    MP      Added sim_fsize_ex and sim_fsize_name_ex returning t_addr
                        Added export of sim_buf_copy_swapped and sim_buf_swap_data
   15-May-06    RMS     Added sim_fsize_name
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   02-Jan-04    RMS     Split out from SCP
*/

#ifndef SIM_FIO_H_
#define SIM_FIO_H_     0

#ifdef  __cplusplus
extern "C" {
#endif

#define FLIP_SIZE       (1 << 16)                       /* flip buf size */
#define fxread(a,b,c,d)         sim_fread (a, b, c, d)
#define fxwrite(a,b,c,d)        sim_fwrite (a, b, c, d)

int32 sim_finit (void);
#if (defined (__linux) || defined (__linux__) || defined (__hpux) || defined (_AIX) ||         \
     (defined (VMS) && (defined (__ALPHA) || defined (__ia64)) && (__DECC_VER >= 60590001)) || \
     ((defined(__sun) || defined(__sun__)) && defined(_LARGEFILE_SOURCE)) ||                   \
     defined (_WIN32) || defined (__APPLE__) || defined (__CYGWIN__) ||                        \
     defined (__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__)) && !defined (DONT_DO_LARGEFILE)
typedef t_int64        t_offset;
#else
typedef int32        t_offset;
#if !defined (DONT_DO_LARGEFILE)
#define DONT_DO_LARGEFILE 1
#endif
#endif
FILE *sim_fopen (const char *file, const char *mode);
int sim_fseek (FILE *st, t_addr offset, int whence);
int sim_fseeko (FILE *st, t_offset offset, int whence);
int sim_set_fsize (FILE *fptr, t_addr size);
int sim_set_fifo_nonblock (FILE *fptr);
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);
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 */
extern t_bool sim_toffset_64;       /* Large File (>2GB) file I/O support */
extern t_bool sim_end;              /* TRUE = little endian, FALSE = big endian */

#ifdef  __cplusplus
}
#endif

#endif
Added src/sim_frontpanel.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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_frontpanel.h: simulator frontpanel API definitions

   Copyright (c) 2015, 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:

   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
   MARK PIZZOLATO 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 name of Mark Pizzolato shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Mark Pizzolato.

   15-Jan-15    MP      Initial implementation
   01-Apr-15    MP      Added register indirect, mem_examine and mem_deposit
   03-Apr-15    MP      Added logic to pass simulator startup messages in
                        panel error text if the connection to the simulator
                        shuts down while it is starting.
   04-Apr-15    MP      Added mount and dismount routines to connect and 
                        disconnect removable media

   This module defines interface between a front panel application and a simh
   simulator.  Facilities provide ways to gather information from and to 
   observe and control the state of a simulator.

   Any application which wants to use this API needs to:
      1) include this file in the application code
      2) compile sim_frontpanel.c and sim_sock.c from the top level directory 
         of the simh source.
      3) link the sim_frontpanel and sim_sock object modules and libpthreads 
         into the application.
      4) Use a simh simulator built from the same version of simh that the
         sim_frontpanel and sim_sock modules came from.

*/

#ifndef SIM_FRONTPANEL_H_
#define SIM_FRONTPANEL_H_     0

#ifdef  __cplusplus
extern "C" {
#endif

#include <stdlib.h>

#if !defined(__VAX)         /* Unsupported platform */

#define SIM_FRONTPANEL_VERSION   2

/**

    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
              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).
              It should not start a simulator running.

 */

typedef struct PANEL PANEL;

PANEL *
sim_panel_start_simulator (const char *sim_path,
                           const char *sim_config,
                           size_t device_panel_count);

PANEL *
sim_panel_start_simulator_debug (const char *sim_path,
                                 const char *sim_config,
                                 size_t device_panel_count,
                                 const char *debug_file);

/**

    sim_panel_add_device_panel - creates a sub panel associated 
                                 with a specific simulator panel

        simulator_panel     the simulator panel to connect to
        device_name         the simulator's name for the device

 */
PANEL *
sim_panel_add_device_panel (PANEL *simulator_panel,
                            const char *device_name);

/**

    sim_panel_destroy   to shutdown a panel or sub panel.

    Note: destroying a simulator panel will also destroy any 
          related sub panels

 */
int
sim_panel_destroy (PANEL *panel);

/**

   The frontpanel API exposes the state of a simulator via access to 
   simh register variables that the simulator and its devices define.
   These registers certainly include any architecturally described 
   registers (PC, PSL, SP, etc.), but also include anything else
   the simulator uses as internal state to implement the running 
   simulator.

   The registers that a particular frontpanel application mught need 
   access to are described by the application by calling: 
   
   sim_panel_add_register
   sim_panel_add_register_array
and
   sim_panel_add_register_indirect

        name         the name the simulator knows this register by
        device_name  the device this register is part of.  Defaults to
                     the device of the panel (in a device panel) or the
                     default device in the simulator (usually the CPU).
        element_count number of elements in the register array
        size         the size (in local storage) of the buffer which will
                     receive the data in the simulator's register
        addr         a pointer to the location of the buffer which will 
                     be loaded with the data in the simulator's register

 */
int
sim_panel_add_register (PANEL *panel,
                        const char *name,
                        const char *device_name,
                        size_t size,
                        void *addr);

int
sim_panel_add_register_array (PANEL *panel,
                              const char *name,
                              const char *device_name,
                              size_t element_count,
                              size_t size,
                              void *addr);

int
sim_panel_add_register_indirect (PANEL *panel,
                                 const char *name,
                                 const char *device_name,
                                 size_t size,
                                 void *addr);
/**

    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 API.
    
       1)  The values can be polled (when ever it is desired) by calling
           sim_panel_get_registers().
       2)  The panel can call sim_panel_set_display_callback() 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.


   Note 1: The buffers described in a panel's register set will be 
           dynamically revised as soon as data is available from the 
           simulator.  The callback routine merely serves as a notification 
           that a complete register set has arrived.
   Note 2: The callback routine should, in general, not run for a long time
           or frontpanel interactions with the simulator may be disrupted.  
           Setting a flag, signaling an event or posting a message are 
           reasonable activities to perform in a callback routine.

 */
int
sim_panel_get_registers (PANEL *panel, unsigned long long *simulation_time);

/**


 */
typedef void (*PANEL_DISPLAY_PCALLBACK)(PANEL *panel, 
                                        unsigned long long simulation_time,
                                        void *context);

int
sim_panel_set_display_callback (PANEL *panel, 
                                PANEL_DISPLAY_PCALLBACK callback, 
                                void *context, 
                                int callbacks_per_second);

/**

    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_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_run (PANEL *panel);

int
sim_panel_exec_step (PANEL *panel);

/**

    When a front panel application wants to describe conditions that 
    should stop instruction execution an execution or an output
    breakpoint should be used.  To established or clear a breakpoint, 
    one of the following routines should be called:  
    
    sim_panel_break_set          - Establish a simulation breakpoint
    sim_panel_break_clear        - Cancel/Delete a previously defined
                                   breakpoint
    sim_panel_break_output_set   - Establish a simulator output 
                                   breakpoint
    sim_panel_break_output_clear - Cancel/Delete a previously defined
                                   output breakpoint
    
    Note: Any breakpoint switches/flags must be located at the 
          beginning of the condition string

 */

int
sim_panel_break_set (PANEL *panel, const char *condition);

int
sim_panel_break_clear (PANEL *panel, const char *condition);

int
sim_panel_break_output_set (PANEL *panel, const char *condition);

int
sim_panel_break_output_clear (PANEL *panel, const char *condition);


/**

    When a front panel application needs to change or access
    memory or a register one of the following routines should 
    be called:  
    
    sim_panel_gen_examine        - Examine register or memory
    sim_panel_gen_deposit        - Deposit to register or memory
    sim_panel_mem_examine        - Examine memory location
    sim_panel_mem_deposit        - Deposit to memory location
    sim_panel_set_register_value - Deposit to a register or memory 
                                   location

 */


/**

   sim_panel_gen_examine

        name_or_addr the name the simulator knows this register by
        size         the size (in local storage) of the buffer which will
                     receive the data returned when examining the simulator
        value        a pointer to the buffer which will be loaded with the
                     data returned when examining the simulator
 */

int
sim_panel_gen_examine (PANEL *panel, 
                       const char *name_or_addr,
                       size_t size,
                       void *value);
/**

   sim_panel_gen_deposit

        name_or_addr the name the simulator knows this register by
        size         the size (in local storage) of the buffer which
                     contains the data to be deposited into the simulator
        value        a pointer to the buffer which contains the data to 
                     be deposited into the simulator
 */

int
sim_panel_gen_deposit (PANEL *panel, 
                       const char *name_or_addr,
                       size_t size,
                       const void *value);

/**

   sim_panel_mem_examine

        addr_size    the size (in local storage) of the buffer which 
                     contains the memory address of the data to be examined
                     in the simulator
        addr         a pointer to the buffer containing the memory address
                     of the data to be examined in the simulator
        value_size   the size (in local storage) of the buffer which will
                     receive the data returned when examining the simulator
        value        a pointer to the buffer which will be loaded with the
                     data returned when examining the simulator
 */

int
sim_panel_mem_examine (PANEL *panel, 
                       size_t addr_size,
                       const void *addr,
                       size_t value_size,
                       void *value);

/**

   sim_panel_mem_deposit

        addr_size    the size (in local storage) of the buffer which 
                     contains the memory address of the data to be deposited
                     into the simulator
        addr         a pointer to the buffer containing the memory address
                     of the data to be deposited into the simulator
        value_size   the size (in local storage) of the buffer which will
                     contains the data to be deposited into the simulator
        value        a pointer to the buffer which contains the data to be
                     deposited into the simulator
 */

int
sim_panel_mem_deposit (PANEL *panel, 
                       size_t addr_size,
                       const void *addr,
                       size_t value_size,
                       const void *value);

/**
   sim_panel_set_register_value

        name        the name of a simulator register or a memory address
                    which is to receive a new value
        value       the new value in character string form.  The string 
                    must be in the native/natural radix that the simulator 
                    uses when referencing that register

 */
int
sim_panel_set_register_value (PANEL *panel,
                              const char *name,
                              const char *value);

/**

    When a front panel application may needs to change the media
    in a simulated removable media device one of the following 
    routines should be called:

    sim_panel_mount    - mounts the indicated media file on a device
    sim_panel_dismount - dismounts the currently mounted media file 
                         from a device

 */

/**
   sim_panel_mount

        device      the name of a simulator device/unit
        switches    any switches appropriate for the desire attach
        path        the path on the local system to be attached

 */
int
sim_panel_mount (PANEL *panel,
                 const char *device,
                 const char *switches,
                 const char *path);

/**
   sim_panel_dismount

        device      the name of a simulator device/unit

 */
int
sim_panel_dismount (PANEL *panel,
                    const char *device);


typedef enum {
    Halt,       /* Simulation is halted (instructions not being executed) */
    Run,        /* Simulation is executing instructions */
    Error       /* Panel simulator is in an error state and should be */
                /* closed (destroyed).  sim_panel_get_error might help */
                /* explain why */
    } OperationalState;

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

 */
void
sim_panel_set_debug_file (PANEL *panel, const char *debug_file);

#define DBG_XMT         1   /* Transmit Data */
#define DBG_RCV         2   /* Receive Data */

void
sim_panel_set_debug_mode (PANEL *panel, int debug_bits);

void
sim_panel_flush_debug (PANEL *panel);

#endif /* !defined(__VAX) */

#ifdef  __cplusplus
}
#endif

#endif /* SIM_FRONTPANEL_H_ */
Added src/sim_rev.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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_rev.h: simulator revisions and current rev level

   Copyright (c) 1993-2012, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.
*/

#ifndef SIM_REV_H_
#define SIM_REV_H_     0

#ifndef SIM_MAJOR
#define SIM_MAJOR       4
#endif
#ifndef SIM_MINOR
#define SIM_MINOR       0
#endif
#ifndef SIM_PATCH
#define SIM_PATCH       0
#endif
#ifndef SIM_DELTA
#define SIM_DELTA       0
#endif

#ifndef SIM_VERSION_MODE
#define SIM_VERSION_MODE "Beta"
#endif

#if defined(SIM_NEED_GIT_COMMIT_ID)
#include ".git-commit-id.h"
#endif

#if !defined(SIM_GIT_COMMIT_ID)
#define SIM_GIT_COMMIT_ID $Format:%H$
#endif

/*
  The comment section below reflects the manual editing process which was in place
  prior to the use of the git source control system on at https://gihub.com/simh/simh

  Details about all future fixes will be visible in the source control system's 
  history.

*/

/*
   V3.9 revision history

patch   date            module(s) and fix(es)

  0     01-May-2012     scp.c:
                        - added *nix READLINE support (Mark Pizzolato)
                        - fixed handling of DO with no arguments (Dave Bryan)
                        - fixed "SHOW DEVICE" with only one enabled unit (Dave Bryan)
                        - clarified some help messages (Mark Pizzolato)
                        - added "SHOW SHOW" and "SHOW <dev> SHOW" commands (Mark Pizzolato)
                        - fixed bug in deposit stride for numeric input (John Dundas)

                        sim_console.c
                        - added support for BREAK key on Windows (Mark Pizzolato)

                        sim_ether.c
                        - major revision (Dave Hittner and Mark Pizzolato)
                        - fixed array overrun which caused SEGFAULT on hosts with many
                          devices which libpcap can access.
                        - fixed duplicate MAC address detection to work reliably on switch
                          connected LANs

                        sim_tmxr.c:
                        - made telnet option negotiation more reliable, VAX simulator now 
                          works with PuTTY as console (Mark Pizzolato)

                        h316_cpu.c:
                        - fixed bugs in MPY, DIV introduced in 3.8-1 (from Theo Engel)
                        - fixed bugs in double precision, normalization, SC (from Adrian Wise)
                        - fixed XR behavior (from Adrian Wise)

                        hp2100 all peripherals (Dave Bryan):
                        - Changed I/O signal handlers for newly revised signal model
                        - Deprecated DEVNO modifier in favor of SC

                        hp2100_cpu.c (Dave Bryan):
                        - Minor speedup in "is_mapped"
                        - Added casts to cpu_mod, dmasio, dmapio, cpu_reset, dma_reset
                        - Fixed I/O return status bug for DMA cycles
                        - Failed I/O cycles now stop on failing instruction
                        - Revised DMA for new multi-card paradigm
                        - Consolidated DMA reset routines
                        - DMA channels renamed from 0,1 to 1,2 to match documentation
                        - Changed I/O instructions, handlers, and DMA for revised signal model
                        - Changed I/O dispatch table to use DIB pointers
                        - Removed DMA latency counter
                        - Fixed DMA requests to enable stealing every cycle
                        - Fixed DMA priority for channel 1 over channel 2
                        - Corrected comments for "cpu_set_idle"

                        hp2100_cpu.h:
                        - Changed declarations for VMS compiler

                        hp2100_cpu0.c (Dave Bryan):
                        - Removed DS note regarding PIF card (is now implemented)

                        hp2100_cpu4.c (Dave Bryan):
                        - Added OPSIZE casts to fp_accum calls in .FPWR/.TPWR

                        hp2100_cpu5.c (Dave Bryan):
                        - Added sign extension for dim count in "cpu_ema_resolve"
                        - Eliminated unused variable in "cpu_ema_vset"

                        hp2100_cpu6.c (Dave Bryan):
                        - DMA channels renamed from 0,1 to 1,2 to match documentation

                        hp2100_cpu7.c (Dave Bryan):
                        - Corrected "opsize" parameter type in vis_abs

                        hp2100_defs.h (Dave Bryan):
                        - Added hp_setsc, hp_showsc functions to support SC modifier
                        - DMA channels renamed from 0,1 to 1,2 to match documentation
                        - Revised I/O signal enum values for concurrent signals
                        - Revised I/O macros for new signal handling
                        - Added DA and DC device select code assignments

                        hp2100_di.c, hp2100_di.h (Dave Bryan):
                        - Implemented 12821A HP-IB Disc Interface

                        hp2100_di_da.c (Dave Bryan):
                        - Implemented 7906H/20H/25H ICD disc drives

                        hp2100_dp.c (Dave Bryan):
                        - Added CNTLR_TYPE cast to dp_settype
 
                        hp2100_ds.c (Dave Bryan):
                        - Rewritten to use the MAC/ICD disc controller library
                        - ioIOO now notifies controller service of parameter output
                        - Corrected SRQ generation and FIFO under/overrun detection
                        - Corrected Clear command to conform to the hardware
                        - Fixed Request Status to return Unit Unavailable if illegal
                        - Seek and Cold Load Read now Seek Check if seek in progress
                        - Remodeled command wait for seek completion
                        - Corrected status returns for disabled drive, auto-seek
                          beyond drive limits, Request Sector Address and Wakeup
                          with invalid or offline unit
                        - Address verification reenabled if auto-seek during
                          Read Without Verify

                        hp2100_fp1.c (Dave Bryan):
                        - Added missing precision on constant "one" in fp_trun
                        - Completed the comments for divide; no code changes

                        hp2100_ipl.c (Dave Bryan):
                        - Added CARD_INDEX casts to dib.card_index
                        - A failed STC may now be retried
                        - Consolidated reporting of consecutive CRS signals
                        - Revised for new multi-card paradigm

                        hp2100_lps.c (Dave Bryan):
                        - Revised detection of CLC at last DMA cycle
                        - Corrected 12566B (DIAG mode) jumper settings

                        hp2100_ms.c (Dave Bryan):
                        - Added CNTLR_TYPE cast to ms_settype
 
                        hp2100_mt.c (Dave Bryan):
                        - Removed redundant MTAB_VUN from "format" MTAB entry
                        - Fixed command scanning error in mtcio ioIOO handler

                        hp2100_stddev.c (Dave Bryan):
                        - Add TBG as a logical name for the CLK device
 
                        hp2100_sys.c (Dave Bryan):
                        - Deprecated DEVNO in favor of SC
                        - Added hp_setsc, hp_showsc functions to support SC modifier
                        - Added DA and dummy DC devices
                        - DMA channels renamed from 0,1 to 1,2 to match documentation
                        - Changed DIB access for revised signal model
 
                        hp_disclib.c, hp_disclib.h (Dave Bryan)
                        - Created MAC/ICD disc controller library

                        i1401_cd.c:
                        - fixed read stacker operation in column binary mode
                        - fixed punch stacker operation (Van Snyder)

                        id_pas.c:
                        - fixed TT_GET_MODE test to use TTUF_MODE_x (Michael Bloom)
                        - revised to use clock coscheduling

                        id_tt.c, id_ttc.p:
                        - revised to use clock coscheduling

                        id_uvc.c:
                        - added clock coscheduling routine

                        1401_cpu.c:
                        - reverted multiple tape indicator implementation
                        - fixed EOT indicator test not to clear indicator (Van Snyder)
                        - fixed divide not to clear word marks in quotient (Van Snyder)
                        - revised divide algorithm (Van Snyder)

                        i1401_mt.c:
                        - reverted multiple tape indicator implementation
                        - fixed END indicator test not to clear indicator (Van Snyder)
                        - fixed backspace over tapemark not to set EOR (Van Snyder)
                        - added no rewind option (Van Snyder)

                        i1401_sys.c:
                        - fixed misuse of & instead of && in decode (Peter Schorn)

                        pdp1_cpu.c:
                        - fixed misuse of & instead of && in Ea_ch (Michael Bloom)

                        pdp1_stddev.c:
                        - fixed unitialized variable in tty output service (Michael Bloom)

                        pdp10_fe.c:
                        - revised to use clock coscheduling

                        pdp11_defs.h:
                        - fixed priority of PIRQ vs IO; added INT_INTERNALn

                        pdp11_io.c:
                        - fixed Qbus interrupts to treat all IO devices (except clock) as BR4
                        - fixed order of int_internal (Jordi Guillaumes i Pons)

                        ppd11_rf.c
                        - fixed bug in updating mem addr extension (Peter Schorn)

                        pdp11_rk.c:
                        - fixed bug in read header (Walter F Mueller)

                        pdp11_rl.c:
                        - added debug support

                        pdp11_rq.c:
                        - added RD32 support

                        pdp11_tq.c: (Mark Pizzolato)
                        - set UNIT_SXC flag when a tape mark is encountered 
                          during forward motion read operations
                        - fixed logic which clears UNIT_SXC to check command modifier
                        - added CMF_WR flag to tq_cmf entry for OP_WTM
                        - made non-immediate rewind positioning operations take 2 seconds
                        - added UNIT_IDLE flag to tq units.
                        - fixed debug output of tape file positions when they are 64b
                        - added more debug output after positioning operations
                        - added textual display of the command being performed
                        - fixed comments about register addresses
                        
                        pdp11_ts.c:
                        - fixed t_addr printouts for 64b big-endian systems (Mark Pizzolato)

                        pdp11_tu.c:
                        - fixed t_addr printouts for 64b big-endian systems (Mark Pizzolato)

                        pdp11_vh.c: (Mark Pizzolato)
                        - fixed SET VH LINES=n to correctly adjust the number
                          of lines available to be 8, 16, 24, or 32.
                        - fixed performance issue avoiding redundant polling

                        pdp11_xq.c: (Mark Pizzolato)
                        - Fixed missing information from save/restore which
                          caused operations to not complete correctly after 
                          a restore until the OS reset the controller.
                        - Added address conflict check during attach.
                        - Fixed loopback processing to correctly handle forward packets.
                        - Fixed interrupt dispatch issue which caused delivered packets 
                          (in and out) to sometimes not interrupt the CPU after processing.
                        - Fixed the SCP visibile SA registers to always display the 
                          ROM mac address, even after it is changed by SET XQ MAC=.
                        - Added changes so that the Console DELQA diagnostic (>>>TEST 82) 
                          will succeed.
                        - Added DELQA-T (aka DELQA Plus) device emulation support.
                        - Added dropped frame statistics to record when the receiver discards
                          received packets due to the receiver being disabled, or due to the
                          XQ device's packet receive queue being full.
                        - Fixed bug in receive processing when we're not polling.  This could
                          cause receive processing to never be activated again if we don't 
                          read all available packets via eth_read each time we get the 
                          opportunity.
                        - Added the ability to Coalesce received packet interrupts.  This
                          is enabled by SET XQ POLL=DELAY=nnn where nnn is a number of 
                          microseconds to delay the triggering of an interrupt when a packet
                          is received.
                        - Added SET XQ POLL=DISABLE (aka SET XQ POLL=0) to operate without 
                          polling for packet read completion.
                        - Changed the sanity and id timer mechanisms to use a separate timer
                          unit so that transmit and recieve activities can be dealt with
                          by the normal xq_svc routine.
                          Dynamically determine the timer polling rate based on the 
                          calibrated tmr_poll and clk_tps values of the simulator.
                        - Enabled the SET XQ POLL to be meaningful if the simulator currently
                          doesn't support idling.
                        - Changed xq_debug_setup to use sim_debug instead of printf so that
                          all debug output goes to the same place.
                        - Restored the call to xq_svc after all successful calls to eth_write
                          to allow receive processing to happen before the next event
                          service time.  This must have been inadvertently commented out 
                          while other things were being tested.

                        pdp11_xu.c: (Mark Pizzolato)
                        - Added SHOW XU FILTERS modifier (Dave Hittner)
                        - Corrected SELFTEST command, enabling use by VMS 3.7, VMS 4.7, and Ultrix 1.1 (Dave Hittner)
                        - Added address conflict check during attach.
                        - Added loopback processing support
                        - Fixed the fact that no broadcast packets were received by the DEUNA
                        - Fixed transmitted packets to have the correct source MAC address.
                        - Fixed incorrect address filter setting calling eth_filter().

                        pdp18b_stddev.c:
                        - added clock coscheduling
                        - revised TTI to use clock coscheduling and to fix perpetual CAF bug
                        
                        pdp18b_ttx.c:
                        - revised to use clock coscheduling

                        pdp8_clk.c:
                        - added clock coscheduling

                        pdp8_fpp.c: (Rick Murphy)
                        - many bug fixes; now functional

                        pdp8_tt.c:
                        - revised to use clock coscheduling and to fix perpetual CAF bug

                        pdp8_ttx.c:
                        - revised to use clock cosheduling

                        pdp8_sys.c:
                        - added link to FPP

                        pdp8_td.c:
                        - fixed SDLC to clear AC (Dave Gesswein)

                        sds_mt.c:
                        - fixed bug in scan function decode (Peter Schorn)

                        vax_cpu.c:
                        - revised idle design (Mark Pizzolato)
                        - fixed bug in SET CPU IDLE
                        - fixed failure to clear PSL<tp> in BPT, XFC

                        vax_cpu1.c:
                        - revised idle design Mark Pizzolato)
                        - added VEC_QMODE test in interrupt handler

                        vax_fpa.c:
                        - fixed integer overflow bug in EMODx (Camiel Vanderhoeven)
                        - fixed POLYx normalizing before add mask bug (Camiel Vanderhoeven)
                        - fixed missing arguments in 32b floating add (Mark Pizzolato)

                        vax_octa.c (Camiel Vanderhoeven)
                        - fixed integer overflow bug in EMODH
                        - fixed POLYH normalizing before add mask bug

                        vax_stddev.c:
                        - revised to use clock coscheduling

                        vax_syscm.c:
                        - fixed t_addr printouts for 64b big-endian systems (Mark Pizzolato)

                        vax_sysdev.c:
                        - added power clear call to boot routine (Mark Pizzolato)

                        vax780_sbi.c:
                        - added AUTORESTART switch support (Mark Pizzolato)

                        vax780_stddev.c
                        - added REBOOT support (Mark Pizzolato)
                        - revised to use clock coscheduling

                        vaxmod_def.h
                        - moved all Qbus devices to BR4; deleted RP definitions


   V3.8 revision history

  1     08-Feb-09       scp.c:
                        - revised RESTORE unit logic for consistency
                        - "detach_all" ignores error status returns if shutting down (Dave Bryan)
                        - DO cmd missing params now default to null string (Dave Bryan)
                        - DO cmd sub_args now allows "\\" to specify literal backslash (Dave Bryan)
                        - decommitted MTAB_VAL
                        - fixed implementation of MTAB_NC
                        - fixed warnings in help printouts
                        - fixed "SHOW DEVICE" with only one enabled unit (Dave Bryan)  

                        sim_tape.c:
                        - fixed signed/unsigned warning in sim_tape_set_fmt (Dave Bryan)

                        sim_tmxr.c, sim_tmxr.h:
                        - added line connection order to tmxr_poll_conn,
                          added tmxr_set_lnorder and tmxr_show_lnorder (Dave Bryan)
                        - print device and line to which connection was made (Dave Bryan)
                        - added three new standardized SHOW routines

                        all terminal multiplexers:
                        - revised for new common SHOW routines in TMXR library
                        - rewrote set size routines not to use MTAB_VAL

                        hp2100_cpu.c (Dave Bryan):
                        - VIS and IOP are now mutually exclusive on 1000-F
                        - Removed A/B shadow register variables
                        - Moved hp_setdev, hp_showdev to hp2100_sys.c
                        - Moved non-existent memory checks to WritePW
                        - Fixed mp_dms_jmp to accept lower bound, check write protection
                        - Corrected DMS violation register set conditions
                        - Refefined ABORT to pass address, moved def to hp2100_cpu.h
                        - Combined dms and dms_io routines
                        - JSB to 0/1 with W5 out and fence = 0 erroneously causes MP abort
                        - Unified I/O slot dispatch by adding DIBs for CPU, MP, and DMA
                        - Rewrote device I/O to model backplane signals
                        - EDT no longer passes DMA channel
                        - Added SET CPU IDLE/NOIDLE, idle detection for DOS/RTE
                        - Breakpoints on interrupt trap cells now work

                        hp2100_cpu0.c (Dave Bryan):
                        - .FLUN and self-tests for VIS and SIGNAL are NOP if not present
                        - Moved microcode function prototypes to hp2100_cpu1.h
                        - Removed option-present tests (now in UIG dispatchers)
                        - Added "user microcode" dispatcher for unclaimed instructions

                        hp2100_cpu1.c (Dave Bryan):
                        - Moved microcode function prototypes to hp2100_cpu1.h
                        - Moved option-present tests to UIG dispatchers
                        - Call "user microcode" dispatcher for unclaimed UIG instructions

                        hp2100_cpu2.c (Dave Bryan):
                        - Moved microcode function prototypes to hp2100_cpu1.h
                        - Removed option-present tests (now in UIG dispatchers)
                        - Updated mp_dms_jmp calling sequence
                        - Fixed DJP, SJP, and UJP jump target validation
                        - RVA/B conditionally updates dms_vr before returning value
                        
                        hp2100_cpu3.c (Dave Bryan):
                        - Moved microcode function prototypes to hp2100_cpu1.h
                        - Removed option-present tests (now in UIG dispatchers)
                        - Updated mp_dms_jmp calling sequence

                        hp2100_cpu4.c, hp2100_cpu7.c (Dave Bryan):
                        - Moved microcode function prototypes to hp2100_cpu1.h
                        - Removed option-present tests (now in UIG dispatchers)

                        hp2100_cpu5.c (Dave Bryan):
                        - Moved microcode function prototypes to hp2100_cpu1.h
                        - Removed option-present tests (now in UIG dispatchers)
                        - Redefined ABORT to pass address, moved def to hp2100_cpu.h
                        - Rewrote device I/O to model backplane signals

                        hp2100_cpu6.c (Dave Bryan):
                        - Corrected .SIP debug formatting
                        - Moved microcode function prototypes to hp2100_cpu1.h
                        - Removed option-present tests (now in UIG dispatchers)
                        - Rewrote device I/O to model backplane signals

                        hp2100 all peripherals (Dave Bryan):
                        - Rewrote device I/O to model backplane signals

                        hp2100_baci.c (Dave Bryan):
                        - Fixed STC,C losing interrupt request on BREAK
                        - Changed Telnet poll to connect immediately after reset or attach
                        - Added REG_FIT to register variables < 32-bit size
                        - Moved fmt_char() function to hp2100_sys.c

                        hp2100_dp.c, hp2100_dq.c (Dave Bryan):
                        - Added REG_FIT to register variables < 32-bit size

                        hp2100_dr.c (Dave Bryan):
                        - Revised drc_boot to use ibl_copy

                        hp2100_fp1.c (Dave Bryan):
                        - Quieted bogus gcc warning in fp_exec

                        hp2100_ipl.c (Dave Bryan):
                        - Changed socket poll to connect immediately after reset or attach
                        - Revised EDT handler to refine completion delay conditions
                        - Revised ipl_boot to use ibl_copy

                        hp2100_lpt.c (Dave Bryan):
                        - Changed CTIME register width to match documentation

                        hp2100_mpx.c (Dave Bryan):
                        - Implemented 12792C eight-channel terminal multiplexer

                        hp2100_ms.c (Dave Bryan):
                        - Revised to use AR instead of saved_AR in boot

                        hp2100_mt.c (Dave Bryan):
                        - Fixed missing flag after CLR command
                        - Moved write enable and format commands from MTD to MTC
                        
                        hp2100_mux.c (Dave Bryan):
                        - SHOW MUX CONN/STAT with SET MUX DIAG is no longer disallowed
                        - Changed Telnet poll to connect immediately after reset or attach
                        - Added LINEORDER support
                        - Added BREAK deferral to allow RTE break-mode to work

                        hp2100_pif.c (Dave Bryan):
                        - Implemented 12620A/12936A Privileged Interrupt Fences

                        hp2100_sys.c (Dave Bryan):
                        - Fixed IAK instruction dual-use mnemonic display
                        - Moved hp_setdev, hp_showdev from hp2100_cpu.c
                        - Changed sim_load to use WritePW instead of direct M[] access
                        - Added PIF device
                        - Moved fmt_char() function from hp2100_baci.c
                        - Added MPX device

                        hp2100_cpu.h (Dave Bryan):
                        - Rearranged declarations with hp2100_cpu.c and hp2100_defs.h
                        - Added mp_control to CPU state externals

                        hp2100_cpu1.h (Dave Bryan):
                        - Moved microcode function prototypes here

                        hp2100_defs.h (Dave Bryan):
                        - Added POLL_FIRST to indicate immediate connection attempt
                        - Rearranged declarations with hp2100_cpu.h
                        - Added PIF device
                        - Declared fmt_char() function
                        - Added MPX device

                        i1401_cpu.c:
                        - fixed bug in ZA and ZS (Bob Abeles)
                        - fixed tape indicator implementation (Bob Abeles)
                        - added missing magtape modifier A (Van Snyder)

                        i1401_mt.c:
                        - added -n (no rewind) option to BOOT (Van Snyder)
                        - fixed bug to mask input to 6b on read (Bob Abeles)

                        lgp_stddev.c:
                        - changed encode character from # to !, due to overlap

                        pdp11_cpu.c:
                        - fixed failure to clear cpu_bme on RESET (Walter Mueller)

                        pdp11_dz.c:
                        - added MTAB_NC modifier on SET LOG command (Walter Mueller)

                        pdp11_io.c, vax_io.c, vax780_uba.c:
                        - revised to use PDP-11 I/O library

                        pdp11_io_lib.c:
                        - created common library for Unibus/Qbus support routines

                        pdp11_cis.c, vax_cis.c:
                        - fixed bug in ASHP left overflow calc (Word/NibbleLShift)
                        - fixed bug in DIVx (LntDstr calculation)

                        sds_lp.c:
                        - fixed loss of carriage control position on space op

                        vax_stddev.c, vax780_stddev.c
                        - modified to resync TODR on any clock reset

  0     15-Jun-08       scp.c:
                        - fixed bug in local/global register search (Mark Pizzolato)
                        - fixed bug in restore of RO units (Mark Pizzolato)
                        - added SET/SHO/NO BR with default argument (Dave Bryan)

                        sim_tmxr.c
                        - worked around Telnet negotiation problem with QCTerm (Dave Bryan)

                        gri_defs.h, gri_cpu.c, gri_sys.c:
                        - added GRI-99 support

                        hp2100_baci.c (Dave Bryan):
                        - Implemented 12966A Buffered Asynchronous Communications Interface simulator

                        hp2100_cpu.c (Dave Bryan):
                        - Memory ex/dep and bkpt type default to current map mode
                        - Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA
                        - Corrected MP W5 (JSB) jumper action, SET/SHOW reversal,
                          mp_mevff clear on interrupt with I/O instruction in trap cell
                        - Removed DBI support from 1000-M (was temporary for RTE-6/VM)
                        - Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags
                        - Enabled SIGNAL instructions, SIG debug flag
                        - Fixed single stepping through interrupts

                        hp2100_cpu0.c (Dave Bryan and Holger Veit):
                        - Removed and implemented "cpu_rte_vma" and "cpu_rte_os"
                        - Removed and implemented "cpu_vis" and "cpu_signal"
                        - Removed and implemented "cpu_rte_ema"

                        hp2100_cpu1.c (Dave Bryan):
                        - Added fprint_ops, fprint_regs for debug printouts
                        - Enabled DIAG as NOP on 1000 F-Series
                        - Fixed VIS and SIGNAL to depend on the FPP and HAVE_INT64

                        hp2100_cpu3.c (Dave Bryan):
                        - Fixed unsigned divide bug in .DDI
                        - Fixed unsigned multiply bug in .DMP
                        - Added implementation of DBI self-test

                        hp2100_cpu4.c (Dave Bryan):
                        - Fixed B register return bug in /CMRT

                        hp2100_cpu5.c (Holger Veit):
                        - Implemented RTE-6/VM Virtual Memory Area firmware
                        - Implemented RTE-IV Extended Memory Area firmware

                        hp2100_cpu6.c (Dave Bryan):
                        - Implemented RTE-6/VM OS accelerator firmware

                        hp2100_cpu7.c (Holger Veit):
                        - Implemented Vector Instruction Set and SIGNAL/1000 firmware

                        hp2100_ds.c (Dave Bryan):
                        - Corrected and verified ioCRS action
                        - Corrected DPTR register definition from FLDATA to DRDATA

                        hp2100_fp.c (Mark Pizzolato)
                        - Corrected fp_unpack mantissa high-word return

                        hp2100_fp1.c (Dave Bryan):
                        - Reworked "complement" to avoid inlining bug in gcc-4.x
                        - Fixed uninitialized return in fp_accum when setting

                        hp2100_mux.c (Dave Bryan):
                        - Sync mux poll with console poll for idle compatibility

                        hp2100_stddev.c (Dave Bryan):
                        - Fixed PTR trailing null counter for tape re-read
                        - Added IPTICK register to CLK to display CPU instr/tick
                        - Corrected and verified ioCRS actions
                        - Changed TTY console poll to 10 msec. real time
                        - Synchronized CLK with TTY if set for 10 msec.
                        - Added UNIT_IDLE to TTY and CLK
                        - Removed redundant control char handling definitions
                        - Changed TTY output wait from 100 to 200 for MSU BASIC

                        hp2100_sys.c (Dave Bryan):
                        - Added BACI device
                        - Added RTE OS/VMA/EMA mnemonics
                        - Changed fprint_sym to handle step with irq pending

                        hp2100_cpu.h (Dave Bryan):
                        - Added calc_defer() prototype
                        - Added extern sim_deb, cpu_dev, DEB flags for debug printouts
                        - Added extern intaddr, mp_viol, mp_mevff, calc_int, dev_ctl,
                          ReadIO, WriteIO for RTE-6/VM microcode support

                        hp2100_cpu1.h (Dave Bryan):
                        - Corrected OP_AFF to OP_AAFF for SIGNAL/1000
                        - Removed unused operand patterns
                        - Added fprint_ops, fprint_regs for debug printouts
                        - Revised OP_KKKAKK operand profile to OP_CCCACC for $LOC

                        hp2100_defs.h (Dave Bryan):
                        - Added BACI device
                        - Added 16/32-bit unsigned-to-signed conversions
                        - Changed TMR_MUX to TMR_POLL for idle support
                        - Added POLLMODE, sync_poll() declaration
                        - Added I_MRG, I_ISZ, I_IOG, I_STF, and I_SFS instruction masks
                        - Added I_MRG_I, I_JSB, I_JSB_I, and I_JMP instruction masks

                        nova_defs.h (Bruce Ray):
                        - added support for third-party 64KW memory

                        nova_clk.c (Bruce Ray):
                        - renamed to RTC, to match DG literature

                        nova_cpu.c (Bruce Ray):
                        - added support for third-party 64KW memory
                        - added Nova 3 "secret" instructions
                        - added CPU history support

                        nova_dkp.c (Bruce Ray):
                        - renamed to DKP, to match DG literature
                        - fixed numerous bugs in both documented and undocumented behavior
                        - changed bootstrap code to DG official sequence

                        nova_dsk.c (Bruce Ray):
                        - renamed to DSK, to match DG literature
                        - added support for undocumented behavior
                        - changed bootstrap code to DG official sequence

                        nova_mta.c (Bruce Ray):
                        - renamed to MTA, to match DG literature
                        - changed bootstrap code to DG official sequence

                        nova_plt.c, nova_pt.c (Bruce Ray):
                        - added 7B/8B support

                        nova_sys.c (Bruce Ray):
                        - fixed mistaken instruction mnemonics

                        pdp11_cpu.c, pdp11_io.c, pdp11_rh.c:
                        - fixed DMA memory address limit test (John Dundas)
                        - fixed MMR0 treatment in RESET (Walter Mueller)

                        pdp11_cpumod.h, pdp11_cpumod.c:
                        - fixed write behavior of 11/70 MBRK, LOSIZE, HISIZE (Walter Mueller)
                        - added support to set default state of KDJ11B,E clock control register

                        pdp11_dc.c:
                        - added support for DC11

                        pdp11_defs.h:
                        - added KE, KG, RC, DC support
                        - renamed DL11 devices

                        pdp11_dl.c:
                        - renamed devices to DLI/DLO, to match DC11
                        - added modem control

                        pdp11_io.c:
                        - added autoconfigure support for DC11

                        pdp11_ke.c:
                        - added support for KE11A

                        pdp11_kg.c (John Dundas):
                        - added support for KG11A

                        pdp11_rc.c (John Dundas):
                        - added support for RC11

                        pdp11_sys.c:
                        - modified to allow -A, -B use with 8b devices
                        - added KE, KG, RC, DC support
                        - renamed DL11 devices

                        vax_cmode.c, vax_io.c, vax780_uba.c:
                        - fixed declarations (Mark Pizzolato)


   V3.7 revision history 

  3     02-Sep-07       scp.c:
                        - fixed bug in SET THROTTLE command

                        pdp10_cpu.c:
                        - fixed non-portable usage in SHOW HISTORY routine

                        pdp11_ta.c:
                        - forward op at BOT skips initial file gap

                        pdp8_ct.c:
                        - forward op at BOT skips initial file gap
                        - fixed handling of BEOT

                        vax_cpu.c:
                        - fixed bug in read access g-format indexed specifiers

  2     12-Jul-07       sim_ether.c (Dave Hittner):
                        - fixed non-ethernet device removal loop (Naoki Hamada)
                        - added dynamic loading of wpcap.dll;
                        - corrected exceed max index bug in ethX lookup
                        - corrected failure to look up ethernet device names in
                          the registry on Windows XP x64

                        sim_timer.c:
                        - fixed idle timer event selection algorithm
  
                        h316_lp.c:
                        - fixed loss of last print line (Theo Engel)

                        h316_mt.c:
                        - fixed bug in write without stop (Theo Engel)

                        h316_stddev.c:
                        - fixed bug in clock increment (Theo Engel)

                        i1401_cpu.c:
                        - added recognition of overlapped operation modifiers
                        - remove restriction on load-mode binary tape operations

                        i1401_mt.c:
                        - fixed read tape mark operation (Van Snyder)
                        - remove restriction on load-mode binary tape operations

                        pdp1_cpu.c:
                        - fixed typo in SBS clear (Norm Lastovica)

                        pdp11_rh.c, pdp11_rp.c, pdp11_tu.c:
                        - CS1 DVA is in the device, not the MBA

                        pdp8_ct.c:
                        - fixed typo (Norm Lastovica)

                        vax_cpu.c:
                        - revised idle detector

  1     14-May-07       scp.c:
                        - modified sim_instr invocation to call sim_rtcn_init_all
                        - fixed bug in get_sim_opt (reported by Don North)
                        - fixed bug in RESTORE with changed memory size
                        - added global 'RESTORE in progress' flag
                        - fixed breakpoint actions in DO command file processing
                          (Dave Bryan)

                        all CPU's with clocks:
                        - removed clock initialization (now done in SCP)

                        hp2100_cpu.c (Dave Bryan):
                        - EDT passes input flag and DMA channel in dat parameter

                        hp2100_ipl.c (Dave Bryan):
                        - IPLI EDT delays DMA completion interrupt for TSB

                        hp2100_mux.c (Dave Bryan):
                        - corrected "mux_sta" size from 16 to 21 elements
                        - fixed "muxc_reset" to clear lines 16-20
                        - fixed control card OTx to set current channel number
                        - fixed to set "muxl_ibuf" in response to a transmit interrupt
                        - changed "mux_xbuf", "mux_rbuf" declarations from 8 to 16 bits
                        - fixed to set "mux_rchp" when a line break is received
                        - fixed incorrect "odd_par" table values
                        - reversed test in "RCV_PAR" to return "LIL_PAR" on odd parity
                        - rixed mux reset (ioCRS) to clear port parameters
                        - fixed to use PUT_DCH instead of PUT_CCH for data channel status
                        - added DIAG/TERM modifiers to implement diagnostic mode

                        pdp11_cpumod.c:
                        - changed memory size routine to work with RESTORE

                        pdp11_hk.c:
                        - NOP and DCLR (at least) do not check drive type
                        - MR2 and MR3 only updated on NOP

                        pdp10_tu.c, pdp11_tu.c:
                        - TMK sets FCE only on read (Naoki Hamada)

                        pdp11_xu.c:
                        - added missing FC_RMAL command
                        - cleared multicast on write

                        vax_moddefs.h, vax_cpu1.c:
                        - separated PxBR and SBR mbz checks

                        vax780_defs.h
                        - separated PxBR and SBR mbz checks
                        - modified mbz checks to reflect 780 microcode patches
                          (Naoki Hamada)

                        vax_mmu.c:
                        - added address masking to all SBR-based memory reads

  0     30-Jan-07       scp.c:
                        - implemented throttle commands
                        - added -e to control error processing in DO command files
                          (Dave Bryan)

                        sim_console.c:
                        - fixed handling of non-printable characters in KSR mode

                        sim_tape.c:
                        - fixed bug in reverse operations for P7B-format tapes
                        - fixed bug in reverse operations across erase gaps

                        sim_timer.c:
                        - added throttle support
                        - added idle support (based on work by Mark Pizzolato)

                        gri_stddev.c, h316_stddev.c, pdp18b_tt1.c
                        - fixed handling of non-printable characters in KSR mode

                        hp2100_cpu.c, hp2100_cpu0.c, hp2100_cpu1.c, hp2100_cpu2.c,
                        hp2100_cpu3.c, hp2100_cpu4.c (Dave Bryan):
                        - reorganized CPU modules for easier addition of new instructions
                        - added Double Integer instructions, 1000-F CPU, 2114 and
                          2115 CPUs, 12K and 24K memory sizes, 12607B and 12578A DMA
                          controllers, and 21xx binary loader protection
                        - fixed DMS self-test instruction execution on 1000-M
                        - fixed indirect interrupt holdoff logic

                        hp2100_ds.c:
                        - fixed REQUEST STATUS to clear status-1 (Dave Bryan)

                        hp2100_fp1.c:
                        - Added Floating Point Processor (Dave Bryan)

                        hp2100_lps.c:
                        - fixed diag-mode CLC response

                        i7094_cpu.c:
                        - fixed new bug in halt IO wait loop
                        - added IFT, EFT expanded core test instructions

                        id16_cpu.c, id32_cpu.c:
                        - removed separate multiplexor clock
                        - added idle support

                        id_pas.c:
                        - synced multiplexor poll to real-time clock

                        id_tt.c, id_ttp.c:
                        - fixed handling of non-printable characters in KSR mode
                        - synced keyboard poll to real-time clock

                        id_uvc.c:
                        - changed line-time clock to be free-running

                        pdp1_cpu.c:
                        - added 16-channel sequence break system (API) support
                        - added PDP-1D support

                        pdp1_clk.c:
                        - first release

                        pdp1_dcs.c:
                        - first release

                        pdp1_stddev.c:
                        - separated TTI, TTO for API support

                        pdp1_sys.c:
                        - added PDP-1D, 16-channel SBS, clock, DCS support
                        - fixed bugs in character input, block loader

                        pdp10_cpu.c:
                        - added idle support

                        pdp10_defs.h, pdp10_sys.c:
                        - added CR support

                        pdp10_fe.c, pdp10_tim.c:
                        - synced keyboard poll to real-time clock

                        pdp11_cr.c:
                        - revised for PDP-10 compatibility (CD11 only)

                        pdp11_cpu.c:
                        - added idle support
                        - fixed bug in ASH -32 C value

                        pdp11_rf.c:
                        - fixed unit mask (John Dundas)

                        pdp11_stddev.c, vax_stddev.c, vax780_stddev.c:
                        - synced keyboard poll to real-time clock
                        - added clock coscheduling support

                        pdp11_ta.c:
                        - first release

                        pdp11_vh.c:
                        - synced service poll to real-time clock
                        - changed device to be off by default

                        pdp11_dz.c, pdp11_xq.c, pdp11_xu.c:
                        - synced service poll to real-time clock

                        pdp11_sys.c:
                        - fixed operand order in EIS instructions (W.F.J. Mueller)
                        - added TA11 support

                        pdp18b_cpu.c:
                        - fixed incorrect value of PC on instruction fetch mem mmgt error
                        - fixed PDP-15 handling of mem mmgt traps (sets API 3)
                        - fixed PDP-15 handling of CAL API 4 (sets only if 0-3 inactive)
                        - fixed PDP-15 CAF to clear memory management mode register
                        - fixed boundary test in KT15/XVM (reported by Andrew Warkentin)
                        - added XVM RDCLK instruction
                        - added idle support and infinite loop detection

                        pdp18b_rf.c:
                        - fixed bug, DSCD does not clear function register

                        pdp18b_stddev.c:
                        - added PDP-15 program-selectable duplex handling instruction
                        - fixed PDP-15 handling of reader out-of-tape
                        - fixed handling of non-printable characters in KSR mode
                        - added XVM RDCLK instruction
                        - changed real-time clock to be free running
                        - synced keyboard poll to real-time clock

                        pdp18b_tt1.c
                        - fixed handling of non-printable characters in KSR mode

                        pdp18b_sys.c:
                        - added XVM RDCLK instruction

                        pdp8_cpu.c:
                        - fixed SC value after DVI overflow (Don North)
                        - added idle support and infinite loop detection

                        pdp8_ct.c:
                        - first release

                        pdp8_clk.c:
                        - changed real-time clock to be free running

                        pdp8_sys.c:
                        - added TA8E support
                        - added ability to disambiguate overlapping IOT definitions

                        pdp8_tt.c:
                        - fixed handling of non-printable characters in KSR mode
                        - synced keyboard poll to real-time clock

                        vax_cpu.c, vax_cpu1.c:
                        - added idle support

                        vax_syscm.c:
                        - fixed operand order in EIS instructions (W.F.J. Mueller)


   V3.6 revision history 

  1     25-Jul-06       sim_console.c:
                        - implemented SET/SHOW PCHAR

                        all DECtapes:
                        - fixed conflict in ATTACH switches

                        hp2100_ms.c (Dave Bryan):
                        - added CAPACITY as alternate for REEL
                        - fixed EOT test for unlimited reel size

                        i1620_cd.c (Tom McBride):
                        - fixed card reader fgets call
                        - fixed card reader boot sequence

                        i7094_cd.c:
                        - fixed problem with 80 column full cards

                        i7094_cpu.c:
                        - fixed bug in halt IO wait loop

                        i7094_sys.c:
                        - added binary loader (courtesy of Dave Pitt)

                        pdp1_cpu.c:
                        - fixed bugs in MUS and DIV

                        pdp11_cis.c:
                        - added interrupt tests to character instructions
                        - added 11/44 stack probe test to MOVCx (only)

                        pdp11_dl.c:
                        - first release

                        pdp11_rf.c:
                        - first release

                        pdp11_stddev.c:
                        - added UC support to TTI, TTO

                        pdp18b_cpu.c:
                        - fixed RESET to clear AC, L, and MQ

                        pdp18b_dt.c:
                        - fixed checksum calculation bug for Type 550

                        pdp18b_fpp.c:
                        - fixed bugs in left shift, multiply

                        pdp18b_stddev.c:
                        - fixed Baudot letters/figures inversion for PDP-4
                        - fixed letters/figures tracking for PDP-4
                        - fixed PDP-4/PDP-7 default terminal to be local echo

                        pdp18b_sys.c:
                        - added Fiodec, Baudot display
                        - generalized LOAD to handle HRI, RIM, and BIN files

                        pdp8_ttx.c:
                        - fixed bug in DETACH routine

  0     15-May-06       scp.c:
                        - revised save file format to save options, unit capacity

                        sim_tape.c, sim_tape.h:
                        - added support for finite reel size
                        - fixed bug in P7B write record

                        most magtapes:
                        - added support for finite reel size

                        h316_cpu.c: fixed bugs in LLL, LRL (Theo Engel)

                        h316_lp.c: fixed bug in blanks backscanning (Theo Engel)

                        h316_stddev.c: fixed bugs in punch state handling (Theo Engel)

                        i1401_cpu.c: fixed bug in divide (reported by Van Snyder)

                        i16_cpu.c: fixed bug in DH (Mark Hittinger)

                        i32_cpu.c:
                        - fixed bug in DH (Mark Hittinger)
                        - added support for 8 register banks in 8/32

                        i7094: first release

                        id_io.c: fixed bug, GO preserves EXA and SSTA (Davis Johnson)

                        id_idc.c:
                        - fixed WD/WH handling (Davis Johnson)
                        - fixed bug, nop command should be ignored (Davis Johnson)

                        nova_cpu.c: fixed bug in DIVS (Mark Hittinger)

                        pdp11_cis.c: (all reported by John Dundas)
                        - fixed bug in decode table
                        - fixed bug in ASHP
                        - fixed bug in write decimal string with mmgt enabled
                        - fixed bug in 0-length strings in multiply/divide

                        pdp11_cpu.c: fixed order of operand fetching in XOR for SDSD models

                        pdp11_cr.c: added CR11/CD11 support

                        pdp11_tc.c:
                        - fixed READ to set extended data bits in TCST (Alan Frisbie)

                        vax780_fload.c: added FLOAD command

                        vax780_sbi.c: fixed writes to ACCS

                        vax780_stddev.c: revised timer logic for EVKAE (reported by Tim Stark)

                        vax_cis.c: (all reported by Tim Stark)
                        - fixed MOVTC, MOVTUC to preserve cc's through page faults
                        - fixed MOVTUC to stop on translated == escape
                        - fixed CVTPL to set registers before destination reg write
                        - fixed CVTPL to set correct cc bit on overflow
                        - fixed EDITPC to preserve cc's through page faults
                        - fixed EDITPC EO$BLANK_ZERO count, cc test
                        - fixed EDITPC EO$INSERT to insert fill instead of blank
                        - fixed EDITPC EO$LOAD_PLUS/MINUS to skip character

                        vax_cpu.c:
                        - added KESU capability to virtual examine
                        - fixed bugs in virtual examine
                        - rewrote CPU history function for improved usability
                        (bugs below reported by Tim Stark)
                        - fixed fault cleanup to clear PSL<tp>
                        - fixed ADAWI r-mode to preserve dst<31:16>
                        - fixed ACBD/G to test correct operand
                        - fixed access checking on modify-class specifiers
                        - fixed branch address calculation in CPU history
                        - fixed bug in reported VA on faulting cross-page write

                        vax_cpu1.c: (all reported by Tim Stark)
                        - added access check on system PTE for 11/780
                        - added mbz check in LDPCTX for 11/780

                        vax_cmode.c: (all reported by Tim Stark)
                        - fixed omission of SXT
                        - fixed order of operand fetching in XOR

                        vax_fpa.c: (all reported by Tim Stark)
                        - fixed POLYD, POLYG to clear R4, R5
                        - fixed POLYD, POLYG to set R3 correctly
                        - fixed POLYD, POLYG to not exit prematurely if arg = 0
                        - fixed POLYD, POLYG to do full 64b multiply
                        - fixed POLYF, POLYD, POLYG to remove truncation on add
                        - fixed POLYF, POLYD, POLYG to mask mul reslt to 31b/63b/63b
                        - fixed fp add routine to test for zero via fraction
                          to support "denormal" argument from POLYF, POLYD, POLYG
                        - fixed bug in 32b floating multiply routine
                        - fixed bug in 64b extended modulus routine

                        vax_mmu.c:
                        - added access check on system PTE for 11/780

                        vax_octa.c: (all reported by Tim Stark)
                        - fixed MNEGH to test negated sign, clear C
                        - fixed carry propagation in qp_inc, qp_neg, qp_add
                        - fixed pack routines to test for zero via fraction
                        - fixed ACBH to set cc's on result
                        - fixed POLYH to set R3 correctly
                        - fixed POLYH to not exit prematurely if arg = 0
                        - fixed POLYH to mask mul reslt to 127b
                        - fixed fp add routine to test for zero via fraction
                          to support "denormal" argument from POLYH
                        - fixed EMODH to concatenate 15b of 16b extension
                        - fixed bug in reported VA on faulting cross-page write


   V3.5 revision history 

patch   date            module(s) and fix(es)

  2     07-Jan-06       scp.c:
                        - added breakpoint spaces
                        - added REG_FIT support

                        sim_console.c: added ASCII character processing routines

                        sim_tape.c, sim_tape.h:
                        - added write support for P7B format
                        - fixed bug in write forward (Dave Bryan)

                        h316_stddev.c, hp2100_stddev.c, hp2100_mux.c, id_tt.c,
                        id_ttp.c, id_pas.c, pdp8_tt.c, pdp8_ttx.c, pdp11_stddev.c,
                        pdp11_dz.c, pdp18b_stddev.c, pdp18b_tt1.c, vax_stddev,
                        gri_stddev.c:
                        - revised to support new character handling routines

                        pdp10_rp.c: fixed DCLR not to clear disk address

                        pdp11_hk.c: fixed overlapped seek interaction with NOP, etc

                        pdp11_rh.c: added enable/disable routine

                        pdp11_rq.c, pdp11_tm.c, pdp11_tq.c, pdp11_ts.c
                        - widened address display to 64b when USE_ADDR64

                        pdp11_rp.c:
                        - fixed DCLR not to clear disk address
                        - fixed device enable/disable logic to include Massbus adapter
                        - widened address display to 64b when USE_ADDR64

                        pdp11_tu.c:
                        - fixed device enable/disable logic to include Massbus adapter
                        - widened address display to 64b when USE_ADDR64
                        - changed default adapter to TM03 (for VMS)

                        pdp8_df.c, pdp8_dt.c, pdp8_rf.c:
                        - fixed unaligned access bug (Doug Carman)

                        pdp8_rl.c: fixed IOT 61 decoding bug (David Gesswein)

                        vax_cpu.c:
                        - fixed breakpoint detection when USE_ADDR64 option is active
                        - fixed CVTfi to trap on integer overflow if PSW<iv> set

  1     15-Oct-05       All CPU's, other sources: fixed declaration inconsistencies
                        (Sterling Garwood)

                        i1401_cpu.c: added control for old/new character encodings

                        i1401_cd.c, i1401_lpt.c, i1401_tty.c:
                        - changed character encodings to be consistent with 7094
                        - changed column binary format to be consistent with 7094
                        - added choice of business or Fortran set for output encoding

                        i1401_sys.c: changed WM character to ` under new encodings

                        i1620_cd.c, i1620_lpt.c, i1620_tty.c:
                        - changed character encodings to be consistent with 7094

                        pdp10_cpu.c: changed MOVNI to eliminate gcc warning

                        pdp11_io.c: fixed bug in autoconfiguration (missing XU)

                        vax_io.c: fixed bug in autoconfiguration (missing XU)

                        vax_fpa.c: fixed bug in 32b structure definitions (Jason Stevens)

  0     1-Sep-05        Note: most source modules have been edited to improve
                        readability and to fix declaration and cast problems in C++

                        all instruction histories: fixed reversed arguments to calloc

                        scp.c: revised to trim trailing spaces on file inputs

                        sim_sock.c: fixed SIGPIPE error on Unix

                        sim_ether.c: added Windows user-defined adapter names (Timothe Litt)

                        sim_tape.c: fixed misallocation of TPC map array

                        sim_tmxr.c: added support for SET <unit> DISCONNECT

                        hp2100_mux.c: added SET MUXLn DISCONNECT

                        i1401_cpu.c:
                        - fixed SSB-SSG clearing on RESET (reported by Ralph Reinke)
                        - removed error stops in MCE

                        i1401_cd.c: fixed read, punch to ignore modifier on 1, 4 char inst
                        (reported by Van Snyder)

                        id_pas.c:
                        - fixed bug in SHOW CONN/STATS
                        - added SET PASLn DISCONNECT

                        pdp10_ksio.c: revised for new autoconfiguration interface

                        pdp11_cpu.c: replaced WAIT clock queue check with API call

                        pdp11_cpumod.c: added additional 11/60 registers

                        pdp11_io.c: revised autoconfiguration algorithm and interface

                        pdp11_dz.c: revised for new autoconfiguration interface

                        pdp11_vh.c:
                        - revised for new autoconfiguration interface
                        - fixed bug in vector display routine

                        pdp11_xu.c: fixed runt packet processing (Tim Chapman)

                        pdp18b_cpu.c, pdp18b_sys.c:
                        - removed spurious AAS instruction

                        pdp18b_tt1.c:
                        - fixed bug in SHOW CONN/STATS
                        - fixed bug in SET LOG/NOLOG
                        - added SET TTOXn DISCONNECT

                        pdp8_ttx.c:
                        - fixed bug in SHOW CONN/STATS
                        - fixed bug in SET LOG/NOLOG
                        - added SET TTOXn DISCONNECT

                        sds_mux.c:
                        - fixed bug in SHOW CONN/STATS
                        - added SET MUXLn DISCONNECT

                        vaxmod_defs.h: added QDSS support

                        vax_io.c: revised autoconfiguration algorithm and interface

   V3.4 revision history 

  0     01-May-04       scp.c:
                        - fixed ASSERT code
                        - revised syntax for SET DEBUG (Dave Bryan)
                        - revised interpretation of fprint_sym, fparse_sym returns
                        - moved DETACH sanity tests into detach_unit

                        sim_sock.h and sim_sock.c:
                        - added test for WSAEINPROGRESS (Tim Riker)

                        many: revised detach routines to test for attached state

                        hp2100_cpu.c: reorganized CPU options (Dave Bryan)

                        hp2100_cpu1.c: reorganized EIG routines (Dave Bryan)

                        hp2100_fp1.c: added FFP support (Dave Bryan)

                        id16_cpu.c:
                        - fixed bug in show history routine (Mark Hittinger)
                        - revised examine/deposit to do words rather than bytes

                        id32_cpu.c:
                        - fixed bug in initial memory allocation
                        - fixed bug in show history routine (Mark Hittinger)
                        - revised examine/deposit to do words rather than bytes

                        id16_sys.c, id32_sys:
                        - revised examine/deposit to do words rather than bytes

                        pdp10_tu.c:
                        - fixed bug, ERASE and WREOF should not clear done (reported
                          by Rich Alderson)
                        - fixed error reporting

                        pdp11_tu.c: fixed error reporting

   V3.3 revision history 

  2     08-Mar-05       scp.c: added ASSERT command (Dave Bryan)

                        h316_defs.h: fixed IORETURN macro

                        h316_mt.c: fixed error reporting from OCP (Philipp Hachtmann)

                        h316_stddev.c: fixed bug in OCP '0001 (Philipp Hachtmann)

                        hp2100_cpu.c: split out EAU and MAC instructions

                        hp2100_cpu1.c: (Dave Bryan)
                        - fixed missing MPCK on JRS target
                        - removed EXECUTE instruction (is NOP in actual microcode)

                        hp2100_fp: (Dave Bryan)
                        - fixed missing negative overflow renorm in StoreFP

                        i1401_lp.c: fixed bug in write_line (reported by Van Snyder)

                        id32_cpu.c: fixed branches to mask new PC (Greg Johnson)

                        pdp11_cpu.c: fixed bugs in RESET for 11/70 (reported by Tim Chapman)

                        pdp11_cpumod.c:
                        - fixed bug in SHOW MODEL (Sergey Okhapkin)
                        - made SYSID variable for 11/70 (Tim Chapman)
                        - added MBRK write case for 11/70 (Tim Chapman)

                        pdp11_rq: added RA60, RA71, RA81 disks

                        pdp11_ry: fixed bug in boot code (reported by Graham Toal)

                        vax_cpu.c: fixed initial state of cpu_extmem

  1     05-Jan-05       h316_cpu.c: fixed bug in DIV

                        h316_stddev.c:
                        - fixed bug in SKS '104 (reported by Philipp Hachtmann)
                        - fixed bug in SKS '504
                        - adder reader/punch ASCII file support
                        - added Teletype reader/punch support

                        h316_dp.c: fixed bug in skip on !seeking

                        h316_mt.c: fixed bug in DMA/DMC support

                        h316_lp.c: fixed bug in DMA/DMC support

                        hp2100_cpu.c:
                        - fixed DMA reset to clear alternate CTL flop (Dave Bryan)
                        - fixed DMA reset to not clear control words (Dave Bryan)
                        - fixed SBS, CBS, TBS to do virtual reads
                        - separated A/B from M[0/1], for DMA IO (Dave Bryan)
                        - added SET CPU 21MX-M, 21MX-E (Dave Brian)
                        - disabled TIMER/EXECUTE/DIAG instructions for 21MX-M (Dave Bryan)
                        - added post-processor to maintain T/M consistency (Dave Bryan)

                        hp2100_ds.c: first release

                        hp2100_lps.c (all changes from Dave Bryan)
                        - added restart when set online, etc.
                        - fixed col count for non-printing chars

                        hp2100_lpt.c (all changes from Dave Bryan)
                        - added restart when set online, etc.

                        hp2100_sys.c (all changes from Dave Bryan):
                        - added STOP_OFFLINE, STOP_PWROFF messages

                        i1401_sys.c: added address argument support (Van Snyder)

                        id_mt.c: added read-only file support

                        lgp_cpu.c, lgp_sys.c: modified VM pointer setup

                        pdp11_cpu.c: fixed WAIT to work in all modes (John Dundas)

                        pdp11_tm.c, pdp11_ts.c: added read-only file support

                        sds_mt.c: added read-only file support

  0     23-Nov-04       scp.c:
                        - added reset_all_p (powerup)
                        - fixed comma-separated SET options (Dave Bryan)
                        - changed ONLINE/OFFLINE to ENABLED/DISABLED (Dave Bryan)
                        - modified to flush device buffers on stop (Dave Bryan)
                        - changed HELP to suppress duplicate command displays

                        sim_console.c:
                        - moved SET/SHOW DEBUG under CONSOLE hierarchy

                        hp2100_cpu.c: (all fixes by Dave Bryan)
                        - moved MP into its own device; added MP option jumpers
                        - modified DMA to allow disabling
                        - modified SET CPU 2100/2116 to truncate memory > 32K
                        - added -F switch to SET CPU to force memory truncation
                        - fixed S-register behavior on 2116
                        - fixed LIx/MIx behavior for DMA on 2116 and 2100
                        - fixed LIx/MIx behavior for empty I/O card slots
                        - modified WRU to be REG_HRO
                        - added BRK and DEL to save console settings
                        - fixed use of "unsigned int16" in cpu_reset

                        hp2100_dp.c: (all fixes by Dave Bryan)
                        - fixed enable/disable from either device
                        - fixed ANY ERROR status for 12557A interface
                        - fixed unattached drive status for 12557A interface
                        - status cmd without prior STC DC now completes (12557A)
                        - OTA/OTB CC on 13210A interface also does CLC CC
                        - fixed RAR model
                        - fixed seek check on 13210 if sector out of range

                        hp2100_dq.c: (all fixes by Dave Bryan)
                        - fixed enable/disable from either device
                        - shortened xtime from 5 to 3 (drive avg 156KW/second)
                        - fixed not ready/any error status
                        - fixed RAR model

                        hp2100_dr.c: (all fixes by Dave Bryan)
                        - fixed enable/disable from either device
                        - fixed sector return in status word
                        - provided protected tracks and "Writing Enabled" status bit
                        - fixed DMA last word write, incomplete sector fill value
                        - added "parity error" status return on writes for 12606
                        - added track origin test for 12606
                        - added SCP test for 12606
                        - fixed 12610 SFC operation
                        - added "Sector Flag" status bit
                        - added "Read Inhibit" status bit for 12606
                        - fixed current-sector determination
                        - added TRACKPROT modifier

                        hp2100_ipl.c, hp2100_ms.c: (all fixes by Dave Bryan)
                        - fixed enable/disable from either device

                        hp2100_lps.c: (all fixes by Dave Bryan)
                        - added SET OFFLINE/ONLINE, POWEROFF/POWERON
                        - fixed status returns for error conditions
                        - fixed handling of non-printing characters
                        - fixed handling of characters after column 80
                        - improved timing model accuracy for RTE
                        - added fast/realistic timing
                        - added debug printouts

                        hp2100_lpt.c: (all fixes by Dave Bryan)
                        - added SET OFFLINE/ONLINE, POWEROFF/POWERON
                        - fixed status returns for error conditions
                        - fixed TOF handling so form remains on line 0

                        hp2100_stddev.c (all fixes by Dave Bryan)
                        - added paper tape loop mode, DIAG/READER modifiers to PTR
                        - added PV_LEFT to PTR TRLLIM register
                        - modified CLK to permit disable

                        hp2100_sys.c: (all fixes by Dave Bryan)
                        - added memory protect device
                        - fixed display of CCA/CCB/CCE instructions

                        i1401_cpu.c: added =n to SHOW HISTORY

                        id16_cpu.c: added instruction history

                        id32_cpu.c: added =n to SHOW HISTORY

                        pdp10_defs.h: revised Unibus DMA API's

                        pdp10_ksio.c: revised Unibus DMA API's

                        pdp10_lp20.c: revised Unibus DMA API's

                        pdp10_rp.c: replicated register state per drive

                        pdp10_tu.c:
                        - fixed to set FCE on short record
                        - fixed to return bit<15> in drive type
                        - fixed format specification, 1:0 are don't cares
                        - implemented write check
                        - TMK is cleared by new motion command, not DCLR
                        - DONE is set on data transfers, ATA on non data transfers

                        pdp11_defs.h: 
                        - revised Unibus/Qbus DMA API's
                        - added CPU type and options flags

                        pdp11_cpumod.h, pdp11_cpumod.c:
                        - new routines for setting CPU type and options

                        pdp11_io.c: revised Unibus/Qbus DMA API's

                        all PDP-11 DMA peripherals:
                        - revised Unibus/Qbus DMA API's

                        pdp11_hk.c: CS2 OR must be zero for M+

                        pdp11_rh.c, pdp11_rp.c, pdp11_tu.c:
                        - split Massbus adapter from controllers
                        - replicated RP register state per drive
                        - added TM02/TM03 with TE16/TU45/TU77 drives

                        pdp11_rq.c, pdp11_tq.c:
                        - provided different default timing for PDP-11, VAX
                        - revised to report CPU bus type in stage 1
                        - revised to report controller type reflecting bus type
                        - added -L switch (LBNs) to RAUSER size specification

                        pdp15_cpu.c: added =n to SHOW HISTORY

                        pdp15_fpp.c:
                        - fixed URFST to mask low 9b of fraction
                        - fixed exception PC setting

                        pdp8_cpu.c: added =n to SHOW HISTORY

                        vax_defs.h:
                        - added octaword, compatibility mode support

                        vax_moddefs.h: 
                        - revised Unibus/Qbus DMA API's

                        vax_cpu.c:
                        - moved processor-specific code to vax_sysdev.c
                        - added =n to SHOW HISTORY

                        vax_cpu1.c:
                        - moved processor-specific IPR's to vax_sysdev.c
                        - moved emulation trap to vax_cis.c
                        - added support for compatibility mode

                        vax_cis.c: new full VAX CIS instruction emulator

                        vax_octa.c: new full VAX octaword and h_floating instruction emulator

                        vax_cmode.c: new full VAX compatibility mode instruction emulator

                        vax_io.c:
                        - revised Unibus/Qbus DMA API's

                        vax_io.c, vax_stddev.c, vax_sysdev.c:
                        - integrated powerup into RESET (with -p)

                        vax_sys.c:
                        - fixed bugs in parsing indirect displacement modes
                        - fixed bugs in displaying and parsing character data

                        vax_syscm.c: added display and parse for compatibility mode

                        vax_syslist.c:
                        - split from vax_sys.c
                        - removed PTR, PTP

   V3.2 revision history 

  3     03-Sep-04       scp.c:
                        - added ECHO command (Dave Bryan)
                        - qualified RESTORE detach with SIM_SW_REST

                        sim_console: added OS/2 EMX fixes (Holger Veit)

                        sim_sock.h: added missing definition for OS/2 (Holger Veit)

                        hp2100_cpu.c: changed error stops to report PC not PC + 1
                        (Dave Bryan)

                        hp2100_dp.c: functional and timing fixes (Dave Bryan)
                        - controller sets ATN for all commands except read status
                        - controller resumes polling for ATN interrupts after read status
                        - check status on unattached drive set busy and not ready
                        - check status tests wrong unit for write protect status
                        - drive on line sets ATN, will set FLG if polling

                        hp2100_dr.c: fixed CLC to stop operation (Dave Bryan)

                        hp2100_ms.c: functional and timing fixes (Dave Bryan)
                        - fixed erroneous execution of rejected command
                        - fixed erroneous execution of select-only command
                        - fixed erroneous execution of clear command
                        - fixed odd byte handling for read
                        - fixed spurious odd byte status on 13183A EOF
                        - modified handling of end of medium
                        - added detailed timing, with fast and realistic modes
                        - added reel sizes to simulate end of tape
                        - added debug printouts

                        hp2100_mt.c: modified handling of end of medium (Dave Bryan)

                        hp2100_stddev.c: added tab to control char set (Dave Bryan)

                        pdp11_rq.c: VAX controllers luns start at 0 (Andreas Cejna)

                        vax_cpu.c: fixed bug in EMODD/G, second word of quad dst not probed

  2     17-Jul-04       scp.c: fixed problem ATTACHing to read only files
                        (John Dundas)

                        sim_console.c: revised Windows console code (Dave Bryan)

                        sim_fio.c: fixed problem in big-endian read
                        (reported by Scott Bailey)

                        gri_cpu.c: updated MSR, EAO functions

                        hp_stddev.c: generalized handling of control char echoing
                        (Dave Bryan)

                        vax_sys.c: fixed bad block initialization routine

  1     10-Jul-04       scp.c: added SET/SHOW CONSOLE subhierarchy

                        hp2100_cpu.c: fixes and added features (Dave Bryan)
                        - SBT increments B after store
                        - DMS console map must check dms_enb
                        - SFS x,C and SFC x,C work
                        - MP violation clears automatically on interrupt
                        - SFS/SFC 5 is not gated by protection enabled
                        - DMS enable does not disable mem prot checks
                        - DMS status inconsistent at simulator halt
                        - Examine/deposit are checking wrong addresses
                        - Physical addresses are 20b not 15b
                        - Revised DMS to use memory rather than internal format
                        - Added instruction printout to HALT message
                        - Added M and T internal registers
                        - Added N, S, and U breakpoints
                        Revised IBL facility to conform to microcode
                        Added DMA EDT I/O pseudo-opcode
                        Separated DMA SRQ (service request) from FLG

                        all HP2100 peripherals:
                        - revised to make SFS x,C and SFC x,C work
                        - revised to separate SRQ from FLG

                        all HP2100 IBL bootable peripherals:
                        - revised boot ROMs to use IBL facility
                        - revised SR values to preserve SR<5:3>

                        hp2100_lps.c, hp2100_lpt.c: fixed timing
                        
                        hp2100_dp.c: fixed interpretation of SR<0>

                        hp2100_dr.c: revised boot code to use IBL algorithm

                        hp2100_mt.c, hp2100_ms.c: fixed spurious timing error after CLC
                         (Dave Bryan)

                        hp2100_stddev.c:
                        - fixed input behavior during typeout for RTE-IV
                        - suppressed nulls on TTY output for RTE-IV

                        hp2100_sys.c: added SFS x,C and SFC x,C to print/parse routines

                        pdp10_fe.c, pdp11_stddev.c, pdp18b_stddev.c, pdp8_tt.c, vax_stddev.c:
                        - removed SET TTI CTRL-C option

                        pdp11_tq.c:
                        - fixed bug in reporting write protect (reported by Lyle Bickley)
                        - fixed TK70 model number and media ID (Robert Schaffrath)

                        pdp11_vh.c: added DHQ11 support (John Dundas)

                        pdp11_io.c, vax_io.c: fixed DHQ11 autoconfigure (John Dundas)

                        pdp11_sys.c, vax_sys.c: added DHQ11 support (John Dundas)

                        vax_cpu.c: fixed bug in DIVBx, DIVWx (reported by Peter Trimmel)

  0     04-Apr-04       scp.c:
                        - added sim_vm_parse_addr and sim_vm_fprint_addr
                        - added REG_VMAD
                        - moved console logging to SCP
                        - changed sim_fsize to use descriptor rather than name
                        - added global device/unit show modifiers
                        - added device debug support (Dave Hittner)
                        - moved device and unit flags, updated save format

                        sim_ether.c:
                        - further generalizations (Dave Hittner, Mark Pizzolato)

                        sim_tmxr.h, sim_tmxr.c:
                        - added tmxr_linemsg
                        - changed TMXR definition to support variable number of lines

                        sim_libraries:
                        - new console library (sim_console.h, sim_console.c)
                        - new file I/O library (sim_fio.h, sim_fio.c)
                        - new timer library (sim_timer.h, sim_timer.c)

                        all terminal multiplexors: revised for tmxr library changes

                        all DECtapes:
                        - added STOP_EOR to enable end-of-reel stop
                        - revised for device debug support

                        all variable-sized devices: revised for sim_fsize change

                        eclipse_cpu.c, nova_cpu.c: fixed device enable/disable support
                           (Bruce Ray)

                        nova_defs.h, nova_sys.c, nova_qty.c:
                        - added QTY and ALM support (Bruce Ray)

                        id32_cpu.c, id_dp.c: revised for device debug support

                        lgp: added LGP-30 [LGP-21] simulator

                        pdp1_sys.c: fixed bug in LOAD (Mark Crispin)

                        pdp10_mdfp.c:
                        - fixed bug in floating unpack
                        - fixed bug in FIXR (Philip Stone, fixed by Chris Smith)

                        pdp11_dz.c: added per-line logging

                        pdp11_rk.c:
                        - added formatting support
                        - added address increment inhibit support
                        - added transfer overrun detection

                        pdp11_hk.c, pdp11_rp.c: revised for device debug support

                        pdp11_rq.c: fixed bug in interrupt control (Tom Evans)

                        pdp11_ry.c: added VAX support

                        pdp11_tm.c, pdp11_tq.c, pdp11_ts.c: revised for device debug support

                        pdp11_xu.c: replaced stub with real implementation (Dave Hittner)

                        pdp18b_cpu.c:
                        - fixed bug in XVM g_mode implementation
                        - fixed bug in PDP-15 indexed address calculation
                        - fixed bug in PDP-15 autoindexed address calculation

                        pdp18b_fpp.c: fixed bugs in instruction decode

                        pdp18b_stddev.c:
                        - fixed clock response to CAF
                        - fixed bug in hardware read-in mode bootstrap

                        pdp18b_sys.c: fixed XVM instruction decoding errors

                        pdp18b_tt1.c: added support for 1-16 additional terminals

                        vax_moddef.h, vax_cpu.c, vax_sysdev.c:
                        - added extended physical memory support (Mark Pizzolato)
                        - added RXV21 support

                        vax_cpu1.c:
                        - added PC read fault in EXTxV
                        - fixed PC write fault in INSV

   V3.1 revision history

  0     29-Dec-03       sim_defs.h, scp.c: added output stall status

                        all console emulators: added output stall support

                        sim_ether.c (Dave Hittner, Mark Pizzolato, Anders Ahgren):
                        - added Alpha/VMS support
                        - added FreeBSD, Mac OS/X support
                        - added TUN/TAP support
                        - added DECnet duplicate address detection

                        all memory buffered devices (fixed head disks, floppy disks):
                        - cleaned up buffer copy code

                        all DECtapes:
                        - fixed reverse checksum in read all
                        - added DECtape off reel message
                        - simplified timing

                        eclipse_cpu.c (Charles Owen):
                        - added floating point support
                        - added programmable interval timer support
                        - bug fixes

                        h316_cpu.c:
                        - added instruction history
                        - added DMA/DMC support
                        - added device ENABLE/DISABLE support
                        - change default to HSA option included

                        h316_dp.c: added moving head disk support

                        h316_fhd.c: added fixed head disk support

                        h316_mt.c: added magtape support

                        h316_sys.c: added new device support

                        nova_dkp.c (Charles Owen):
                        - fixed bug in flag clear sequence
                        - added diagnostic mode support for disk sizing

`                       nova_mt.c (Charles Owen):
                        - fixed bug, space operations return record count
                        - fixed bug, reset doesn't cancel rewind

                        nova_sys.c: added floating point, timer support (Charles Owen)

                        i1620_cpu.c: fixed bug in branch digit (Dave Babcock)

                        pdp1_drm.c:
                        - added parallel drum support
                        - fixed bug in serial drum instructin decoding

                        pdp1_sys.c: added parallel drum support, mnemonics

                        pdp11_cpu.c:
                        - added autoconfiguration controls
                        - added support for 18b-only Qbus devices
                        - cleaned up addressing/bus definitions

                        pdp11_rk.c, pdp11_ry.c, pdp11_tm.c, pdp11_hk.c:
                        - added Q18 attribute

                        pdp11_io.c:
                        - added autoconfiguration controls
                        - fixed bug in I/O configuration (Dave Hittner)

                        pdp11_rq.c:
                        - revised MB->LBN conversion for greater accuracy
                        - fixed bug with multiple RAUSER drives

                        pdp11_tc.c: changed to be off by default (base config is Qbus)

                        pdp11_xq.c (Dave Hittner, Mark Pizzolato):
                        - fixed second controller interrupts
                        - fixed bugs in multicast and promiscuous setup
  
                        pdp18b_cpu.c:
                        - added instruction history
                        - fixed PDP-4,-7,-9 autoincrement bug
                        - change PDP-7,-9 default to API option included

                        pdp8_defs.h, pdp8_sys.c:
                        - added DECtape off reel message
                        - added support for TSC8-75 (ETOS) option
                        - added support for TD8E controller

                        pdp8_cpu.c: added instruction history

                        pdp8_rx.c:
                        - fixed bug in RX28 read status (Charles Dickman)
                        - fixed double density write

                        pdp8_td.c: added TD8E controller

                        pdp8_tsc.c: added TSC8-75 option

                        vax_cpu.c:
                        - revised instruction history for dynamic sizing
                        - added autoconfiguration controls

                        vax_io.c:
                        - added autoconfiguration controls
                        - fixed bug in I/O configuration (Dave Hittner)

                        id16_cpu.c: revised instruction decoding

                        id32_cpu.c:
                        - revised instruction decoding
                        - added instruction history

   V3.0 revision history 

  2     15-Sep-03       scp.c:
                        - fixed end-of-file problem in dep, idep
                        - fixed error on trailing spaces in dep, idep

                        pdp1_stddev.c
                        - fixed system hang if continue after PTR error
                        - added PTR start/stop functionality
                        - added address switch functionality to PTR BOOT

                        pdp1_sys.c: added multibank capability to LOAD
  
                        pdp18b_cpu.c:
                        - fixed priorities in PDP-15 API (PI between 3 and 4)
                        - fixed sign handling in PDP-15 unsigned mul/div
                        - fixed bug in CAF, must clear API subsystem

                        i1401_mt.c:
                        - fixed tape read end-of-record handling based on real 1401
                        - added diagnostic read (space forward)

                        i1620_cpu.c
                        - fixed bug in immediate index add (Michael Short)

  1     27-Jul-03       pdp1_cpu.c: updated to detect indefinite I/O wait

                        pdp1_drm.c: fixed incorrect logical, missing activate, break

                        pdp1_lp.c:
                        - fixed bugs in instruction decoding, overprinting
                        - updated to detect indefinite I/O wait

                        pdp1_stddev.c:
                        - changed RIM loader to be "hardware"
                        - updated to detect indefinite I/O wait

                        pdp1_sys.c: added block loader format support to LOAD

                        pdp10_rp.c: fixed bug in read header

                        pdp11_rq: fixed bug in user disk size (Chaskiel M Grundman)

                        pdp18b_cpu.c:
                        - added FP15 support
                        - added XVM support
                        - added EAE support to the PDP-4
                        - added PDP-15 "re-entrancy ECO"
                        - fixed memory protect/skip interaction
                        - fixed CAF to only reset peripherals

                        pdp18b_fpp.c: added FP15

                        pdp18b_lp.c: fixed bug in Type 62 overprinting

                        pdp18b_rf.c: fixed bug in set size routine

                        pdp18b_stddev.c:
                        - increased PTP TIME for PDP-15 operating systems
                        - added hardware RIM loader for PDP-7, PDP-9, PDP-15

                        pdp18b_sys.c: added FP15, KT15, XVM instructions

                        pdp8b_df.c, pdp8_rf.c: fixed bug in set size routine

                        hp2100_dr.c:
                        - fixed drum sizes
                        - fixed variable capacity interaction with SAVE/RESTORE

                        i1401_cpu.c: revised fetch to model hardware more closely

                        ibm1130: fixed bugs found by APL 1130

                        nova_dsk.c: fixed bug in set size routine

                        altairz80: fixed bug in real-time clock on Windows host

  0     15-Jun-03       scp.c:
                        - added ASSIGN/DEASSIGN
                        - changed RESTORE to detach files
                        - added u5, u6 unit fields
                        - added USE_ADDR64 support
                        - changed some structure fields to unsigned

                        scp_tty.c: added extended file seek

                        sim_sock.c: fixed calling sequence in stubs

                        sim_tape.c:
                        - added E11 and TPC format support
                        - added extended file support

                        sim_tmxr.c: fixed bug in SHOW CONNECTIONS

                        all magtapes:
                        - added multiformat support
                        - added extended file support

                        i1401_cpu.c:
                        - fixed mnemonic, instruction lengths, and reverse
                           scan length check bug for MCS
                        - fixed MCE bug, BS off by 1 if zero suppress
                        - fixed chaining bug, D lost if return to SCP
                        - fixed H branch, branch occurs after continue
                        - added check for invalid 8 character MCW, LCA

                        i1401_mt.c: fixed load-mode end of record response

                        nova_dsk.c: fixed variable size interaction with restore

                        pdp1_dt.c: fixed variable size interaction with restore

                        pdp10_rp.c: fixed ordering bug in attach

                        pdp11_cpu.c:
                        - fixed bug in MMR1 update (Tim Stark)
                        - fixed bug in memory size table

                        pdp11_lp.c, pdp11_rq.c: added extended file support

                        pdp11_rl.c, pdp11_rp.c, pdp11_ry.c: fixed ordering bug in attach

                        pdp11_tc.c: fixed variable size interaction with restore

                        pdp11_xq.c:
                        - corrected interrupts on IE state transition (code by Tom Evans)
                        - added interrupt clear on soft reset (first noted by Bob Supnik)
                        - removed interrupt when setting XL or RL (multiple people)
                        - added SET/SHOW XQ STATS
                        - added SHOW XQ FILTERS
                        - added ability to split received packet into multiple buffers
                        - added explicit runt & giant packet processing

                        vax_fpa.c:
                        - fixed integer overflow bug in CVTfi
                        - fixed multiple bugs in EMODf

                        vax_io.c: optimized byte and word DMA routines

                        vax_sysdev.c:
                        - added calibrated delay to ROM reads (Mark Pizzolato)
                        - fixed calibration problems in interval timer (Mark Pizzolato)

                        pdp1_dt.c: fixed variable size interaction with restore

                        pdp18b_dt.c: fixed variable size interaction with restore

                        pdp18b_mt.c: fixed bug in MTTR

                        pdp18b_rf.c: fixed variable size interaction with restore

                        pdp8_df.c, pdp8_rf.c: fixed variable size interaction
                        with restore

                        pdp8_dt.c: fixed variable size interaction with restore

                        pdp8_mt.c: fixed bug in SKTR

                        hp2100_dp.c,hp2100_dq.c:
                        - fixed bug in read status (13210A controller)
                        - fixed bug in seek completion

                        id_pt.c: fixed type declaration (Mark Pizzolato)

                        gri_cpu.c: fixed bug in SC queue pointer management

   V2.10 revision history

  4     03-Mar-03       scp.c
                        - added .ini startup file capability
                        - added multiple breakpoint actions
                        - added multiple switch evaluation points
                        - fixed bug in multiword deposits to file

                        sim_tape.c: magtape simulation library

                        h316_stddev.c: added set line frequency command

                        hp2100_mt.c, hp2100_ms.c: revised to use magtape library

                        i1401_mt.c: revised to use magtape library

                        id_dp.c, id_idc.c: fixed cylinder overflow on writes

                        id_mt.c:
                        - fixed error handling to stop selector channel
                        - revised to use magtape library

                        id16_sys.c, id32_sys.c: added relative addressing support

                        id_uvc.c:
                        - added set frequency command to line frequency clock
                        - improved calibration algorithm for precision clock

                        nova_clk.c: added set line frequency command

                        nova_dsk.c: fixed autosizing algorithm

                        nova_mt.c: revised to use magtape library

                        pdp10_tu.c: revised to use magtape library

                        pdp11_cpu.c: fixed bug in MMR1 update (Tim Stark)

                        pdp11_stddev.c
                        - added set line frequency command
                        - added set ctrl-c command

                        pdp11_rq.c:
                        - fixed ordering problem in queue process
                        - fixed bug in vector calculation for VAXen
                        - added user defined drive support

                        pdp11_ry.c: fixed autosizing algorithm

                        pdp11_tm.c, pdp11_ts.c: revised to use magtape library

                        pdp11_tq.c:
                        - fixed ordering problem in queue process
                        - fixed overly restrictive test for bad modifiers
                        - fixed bug in vector calculation for VAXen
                        - added variable controller, user defined drive support
                        - revised to use magtape library

                        pdp18b_cpu.c: fixed three EAE bugs (Hans Pufal)

                        pdp18b_mt.c:
                        - fixed bugs in BOT error handling, interrupt handling
                        - revised to use magtape library

                        pdp18b_rf.c:
                        - removed 22nd bit from disk address
                        - fixed autosizing algorithm

                        pdp18b_stddev.c:
                        - added set line frequency command
                        - added set ctrl-c command

                        pdp18b_sys.c: fixed FMTASC printouts (Hans Pufal)

                        pdp8_clk.c: added set line frequency command

                        pdp8_df.c, pdp8_rf.c, pdp8_rx.c: fixed autosizing algorithm

                        pdp8_mt.c:
                        - fixed bug in BOT error handling
                        - revised to use magtape library

                        pdp8_tt.c: added set ctrl-c command

                        sds_cpu.c: added set line frequency command

                        sds_mt.c: revised to use magtape library

                        vax_stddev.c: added set ctrl-c command

  3     06-Feb-03       scp.c:
                        - added dynamic extension of the breakpoint table
                        - added breakpoint actions

                        hp2100_cpu.c: fixed last cycle bug in DMA output (found by
                        Mike Gemeny)

                        hp2100_ipl.c: individual links are full duplex (found by
                        Mike Gemeny)

                        pdp11_cpu.c: changed R, SP to track PSW<rs,cm> respectively

                        pdp18b_defs.h, pdp18b_sys.c: added RB09 fixed head disk,
                        LP09 printer

                        pdp18b_rf.c:
                        - fixed IOT decoding (Hans Pufal)
                        - fixed address overrun logic
                        - added variable number of platters and autosizing

                        pdp18b_rf.c:
                        - fixed IOT decoding
                        - fixed bug in command initiation

                        pdp18b_rb.c: new RB09 fixed head disk

                        pdp18b_lp.c: new LP09 line printer

                        pdp8_df.c: added variable number of platters and autosizing

                        pdp8_rf.c: added variable number of platters and autosizing

                        nova_dsk.c: added variable number of platters and autosizing

                        id16_cpu.c: fixed bug in SETM, SETMR (Mark Pizzolato)

  2     15-Jan-03       scp.c:
                        - added dynamic memory size flag and RESTORE support
                        - added EValuate command
                        - added get_ipaddr routine
                        - added ! (OS command) feature (Mark Pizzolato)
                        - added BREAK support to sim_poll_kbd (Mark Pizzolato)

                        sim_tmxr.c:
                        - fixed bugs in IAC+IAC handling (Mark Pizzolato)
                        - added IAC+BRK handling (Mark Pizzolato)

                        sim_sock.c:
                        - added use count for Windows start/stop
                        - added sim_connect_sock

                        pdp1_defs.h, pdp1_cpu.c, pdp1_sys.c, pdp1_drm.c:
                        added Type 24 serial drum

                        pdp18_defs.h: added PDP-4 drum support

                        hp2100_cpu.c: added 21MX IOP support

                        hp2100_ipl.c: added HP interprocessor link support

                        pdp11_tq.c: fixed bug in transfer end packet length

                        pdp11_xq.c:
                        - added VMScluster support (thanks to Mark Pizzolato)
                        - added major performance enhancements (thanks to Mark Pizzolato)
                        - added local packet processing
                        - added system id broadcast

                        pdp11_stddev.c: changed default to 7b (for early UNIX)

                        vax_cpu.c, vax_io.c, vax_stddev.c, vax_sysdev.c:
                        added console halt capability (Mark Pizzolato)

                        all terminals and multiplexors: added BREAK support

  1     21-Nov-02       pdp1_stddev.c: changed typewriter to half duplex
                        (Derek Peschel)

                        pdp10_tu.c:
                        - fixed bug in bootstrap (reported by Michael Thompson)
                        - fixed bug in read (reported by Harris Newman)

  0     15-Nov-02       SCP and libraries
                        scp.c:
                        - added Telnet console support
                        - removed VT emulation support
                        - added support for statically buffered devices
                        - added HELP <command>
                        - fixed bugs in set_logon, ssh_break (David Hittner)
                        - added VMS file optimization (Robert Alan Byer)
                        - added quiet mode, DO with parameters, GUI interface,
                           extensible commands (Brian Knittel)
                        - added DEVICE context and flags
                        - added central device enable/disable support
                        - modified SAVE/GET to save and restore flags
                        - modified boot routine calling sequence
                        scp_tty.c:
                        - removed VT emulation support
                        - added sim_os_sleep, renamed sim_poll_kbd, sim_putchar
                        sim_tmxr.c:
                        - modified for Telnet console support
                        - fixed bug in binary (8b) support
                        sim_sock.c: modified for Telnet console support
                        sim_ether.c: new library for Ethernet (David Hittner)

                        all magtapes:
                        - added support for end of medium
                        - cleaned up BOT handling

                        all DECtapes: added support for RT11 image file format

                        most terminals and multiplexors:
                        - added support for 7b vs 8b character processing

                        PDP-1
                        pdp1_cpu.c, pdp1_sys.c, pdp1_dt.c: added PDP-1 DECtape support

                        PDP-8
                        pdp8_cpu.c, all peripherals:
                        - added variable device number support
                        - added new device enabled/disable support
                        pdp8_rx.c: added RX28/RX02 support

                        PDP-11
                        pdp11_defs.h, pdp11_io.c, pdp11_sys.c, all peripherals:
                        - added variable vector support
                        - added new device enable/disable support
                        - added autoconfiguration support
                        all bootstraps: modified to support variable addresses
                        dec_mscp.h, pdp11_tq.c: added TK50 support
                        pdp11_rq.c:
                        - added multicontroller support
                        - fixed bug in HBE error log packet
                        - fixed bug in ATP processing
                        pdp11_ry.c: added RX211/RX02 support
                        pdp11_hk.c: added RK611/RK06/RK07 support
                        pdp11_tq.c: added TMSCP support
                        pdp11_xq.c: added DEQNA/DELQA support (David Hittner)
                        pdp11_pclk.c: added KW11P support
                        pdp11_ts.c:
                        - fixed bug in CTL decoding
                        - fixed bug in extended status XS0_MOT
                        pdp11_stddev.c: removed paper tape to its own module

                        PDP-18b
                        pdp18b_cpu.c, all peripherals:
                        - added variable device number support
                        - added new device enabled/disabled support

                        VAX
                        dec_dz.h: fixed bug in number of boards calculation
                        vax_moddefs.h, vax_io.c, vax_sys.c, all peripherals:
                        - added variable vector support
                        - added new device enable/disable support
                        - added autoconfiguration support
                        vax_sys.c:
                        - generalized examine/deposit
                        - added TMSCP, multiple RQDX3, DEQNA/DELQA support
                        vax_stddev.c: removed paper tape, now uses PDP-11 version
                        vax_sysdev.c:
                        - allowed NVR to be attached to file
                        - removed unused variables (David Hittner)

                        PDP-10
                        pdp10_defs.h, pdp10_ksio.c, all peripherals:
                        - added variable vector support
                        - added new device enable/disable support
                        pdp10_defs.h, pdp10_ksio.c: added support for standard PDP-11
                           peripherals, added RX211 support
                        pdp10_pt.c: rewritten to reference common implementation

                        Nova, Eclipse:
                        nova_cpu.c, eclipse_cpu.c, all peripherals:
                        - added new device enable/disable support

                        HP2100
                        hp2100_cpu:
                        - fixed bugs in the EAU, 21MX, DMS, and IOP instructions
                        - fixed bugs in the memory protect and DMS functions
                        - created new options to enable/disable EAU, MPR, DMS
                        - added new device enable/disable support
                        hp2100_fp.c:
                        - recoded to conform to 21MX microcode algorithms
                        hp2100_stddev.c:
                        - fixed bugs in TTY reset, OTA, time base generator
                        - revised BOOT support to conform to RBL loader
                        - added clock calibration
                        hp2100_dp.c:
                        - changed default to 13210A
                        - added BOOT support
                        hp2100_dq.c:
                        - finished incomplete functions, fixed head switching
                        - added BOOT support
                        hp2100_ms.c:
                        - fixed bugs found by diagnostics
                        - added 13183 support
                        - added BOOT support
                        hp2100_mt.c:
                        - fixed bugs found by diagnostics
                        - disabled by default
                        hp2100_lpt.c: implemented 12845A controller
                        hp2100_lps.c:
                        - renamed 12653A controller
                        - added diagnostic mode for MPR, DCPC diagnostics
                        - disabled by default

                        IBM 1620: first release

   V2.9 revision history

  11    20-Jul-02       i1401_mt.c: on read, end of record stores group mark
                           without word mark (Van Snyder)

                        i1401_dp.c: reworked address generation and checking

                        vax_cpu.c: added infinite loop detection and halt to
                           boot ROM option (Mark Pizzolato)

                        vax_fpa.c: changed function names to prevent conflict
                           with C math library

                        pdp11_cpu.c: fixed bug in MMR0 update logic (from
                           John Dundas)

                        pdp18b_stddev.c: added "ASCII mode" for reader and
                           punch (Hans Pufal)

                        gri_*.c: added GRI-909 simulator

                        scp.c: added DO echo, DO exit (Brian Knittel)

                        scp_tty.c: added Windows priority hacking (from
                           Mark Pizzolato)

  10    15-Jun-02       scp.c: fixed error checking on calls to fxread/fxwrite
                           (Norm Lastovic)

                        scp_tty.c, sim_vt.h, sim_vt.c: added VTxxx emulation
                           support for Windows (Fischer Franz)

                        sim_sock.c: added OS/2 support (Holger Veit)

                        pdp11_cpu.c: fixed bugs (John Dundas)
                        - added special case for PS<15:12> = 1111 to MFPI
                        - removed special case from MTPI
                        - added masking of relocation adds 

                        i1401_cpu.c:
                        - added multiply/divide
                        - fixed bugs (Van Snyder)
                           o 5 and 7 character H, 7 character doesn't branch
                           o 8 character NOP
                           o 1401-like memory dump

                        i1401_dp.c: added 1311 disk

  9     04-May-02       pdp11_rq: fixed bug in polling routine

  8     03-May-02       scp.c:
                        - changed LOG/NOLOG to SET LOG/NOLOG
                        - added SHOW LOG
                        - added SET VT/NOVT and SHOW VT for VT emulation
  
                        sim_sock.h: changed VMS stropt.h include to ioctl.h

                        vax_cpu.c
                        - added TODR powerup routine to set date, time on boot
                        - fixed exception flows to clear trap request
                        - fixed register logging in autoincrement indexed

                        vax_stddev.c: added TODR powerup routine
                        
                        vax_cpu1.c: fixed exception flows to clear trap request

  7     30-Apr-02       scp.c: fixed bug in clock calibration when (real) clock
                           jumps forward due too far (Jonathan Engdahl)
  
                        pdp11_cpu.c: fixed bugs, added features (John Dundas
                           and Wolfgang Helbig)
                        - added HTRAP and BPOK to maintenance register
                        - added trap on kernel HALT if MAINT<HTRAP> set
                        - fixed red zone trap, clear odd address and nxm traps
                        - fixed RTS SP, don't increment restored SP
                        - fixed TSTSET, write dst | 1 rather than prev R0 | 1
                        - fixed DIV, set N=0,Z=1 on div by zero (J11, 11/70)
                        - fixed DIV, set set N=Z=0 on overfow (J11, 11/70)
                        - fixed ASH, ASHC, count = -32 used implementation-
                           dependent 32 bit right shift
                        - fixed illegal instruction test to detect 000010
                        - fixed write-only page test

                        pdp11_rp.c: fixed SHOW ADDRESS command

                        vaxmod_defs.h: fixed DZ vector base and number of lines

                        dec_dz.h:
                        - fixed interrupt acknowledge routines
                        - fixed SHOW ADDRESS command

                        all magtape routines: added test for badly formed
                           record length (suggested by Jonathan Engdahl)

  6     18-Apr-02       vax_cpu.c: fixed CASEL condition codes

                        vax_cpu1.c: fixed vfield pos > 31 test to be unsigned

                        vax_fpu.c: fixed EDIV overflow test for 0 quotient

  5     14-Apr-02       vax_cpu1.c:
                        - fixed interrupt, prv_mode set to 0 (Tim Stark)
                        - fixed PROBEx to mask mode to 2b (Kevin Handy)

  4     1-Apr-02        pdp11_rq.c: fixed bug, reset cleared write protect status

                        pdp11_ts.c: fixed bug in residual frame count after space

  3     15-Mar-02       pdp11_defs.h: changed default model to KDJ11A (11/73)

                        pdp11_rq.c: adjusted delays for M+ timing bugs

                        hp2100_cpu.c, pdp10_cpu.c, pdp11_cpu.c: tweaked abort
                           code for ANSI setjmp/longjmp compliance

                        hp2100_cpu.c, hp2100_fp.c, hp2100_stddev.c, hp2100_sys.c:
                           revised to allocate memory dynamically

  2     01-Mar-02       pdp11_cpu.c:
                        - fixed bugs in CPU registers
                        - fixed double operand evaluation order for M+

                        pdp11_rq.c: added delays to initialization for
                           RSX11M+ prior to V4.5

  1     20-Feb-02       scp.c: fixed bug in clock calibration when (real)
                        time runs backwards

                        pdp11_rq.c: fixed bug in host timeout logic

                        pdp11_ts.c: fixed bug in message header logic

                        pdp18b_defs.h, pdp18b_dt.c, pdp18b_sys.c: added
                           PDP-7 DECtape support

                        hp2100_cpu.c:
                        - added floating point and DMS
                        - fixed bugs in DIV, ASL, ASR, LBT, SBT, CBT, CMW

                        hp2100_sys.c: added floating point, DMS

                        hp2100_fp.c: added floating point

                        ibm1130: added Brian Knittel's IBM 1130 simulator

  0     30-Jan-02       scp.c:
                        - generalized timer package for multiple timers
                        - added circular register arrays
                        - fixed bugs, line spacing in modifier display
                        - added -e switch to attach
                        - moved device enable/disable to simulators

                        scp_tty.c: VAX specific fix (Robert Alan Byer)

                        sim_tmxr.c, sim_tmxr.h:
                        - added tmxr_fstats, tmxr_dscln
                        - renamed tmxr_fstatus to tmxr_fconns

                        sim_sock.c, sim_sock.h: added VMS support (from
                        Robert Alan Byer)

                        pdp_dz.h, pdp18b_tt1.c, nova_tt1.c:
                        - added SET DISCONNECT
                        - added SHOW STATISTICS

                        pdp8_defs.h: fixed bug in interrupt enable initialization

                        pdp8_ttx.c: rewrote as unified multiplexor

                        pdp11_cpu.c: fixed calc_MMR1 macro (Robert Alan Byer)

                        pdp11_stddev.c: fixed bugs in KW11L (John Dundas)

                        pdp11_rp.c: fixed bug in 18b mode boot

                        pdp11 bootable I/O devices: fixed register setup at boot
                           exit (Doug Carman)

                        hp2100_cpu.c:
                        - fixed DMA register tables (Bill McDermith)
                        - fixed SZx,SLx,RSS bug (Bill McDermith)
                        - fixed flop restore logic (Bill McDermith)

                        hp2100_mt.c: fixed bug on write of last character

                        hp2100_dq,dr,ms,mux.c: added new disk, magtape, and terminal
                           multiplexor controllers

                        i1401_cd.c, i1401_mt.c: new zero footprint bootstraps
                           (Van Snyder)

                        i1401_sys.c: fixed symbolic display of H, NOP with no trailing
                           word mark (Van Snyder)

                        most CPUs:
                        - replaced OLDPC with PC queue
                        - implemented device enable/disable locally

   V2.8 revision history

5       25-Dec-01       scp.c: fixed bug in DO command (John Dundas)

                        pdp10_cpu.c:
                        - moved trap-in-progress to separate variable
                        - cleaned up declarations
                        - cleaned up volatile state for GNU C longjmp

                        pdp11_cpu.c: cleaned up declarations
  
                        pdp11_rq.c: added RA-class disks

4       17-Dec-01       pdp11_rq.c: added delayed processing of packets

3       16-Dec-01       pdp8_cpu.c:
                        - mode A EAE instructions didn't clear GTF
                        - ASR shift count > 24 mis-set GTF
                        - effective shift count == 32 didn't work

2       07-Dec-01       scp.c: added breakpoint package

                        all CPU's: revised to use new breakpoint package

1       05-Dec-01       scp.c: fixed bug in universal register name logic

0       30-Nov-01       Reorganized simh source and documentation tree

                        scp: Added DO command, universal registers, extended
                           SET/SHOW logic

                        pdp11: overhauled PDP-11 for DMA map support, shared
                           sources with VAX, dynamic buffer allocation

                        18b pdp: overhauled interrupt structure

                        pdp8: added RL8A

                        pdp10: fixed two ITS-related bugs (Dave Conroy)

   V2.7 revision history

patch   date            module(s) and fix(es)

15      23-Oct-01       pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: fixed bugs
                           error interrupt handling

                        pdp10_defs.h, pdp10_ksio.c, pdp10_fe.c, pdp10_fe.c,
                        pdp10_rp.c, pdp10_tu.c: reworked I/O page interface
                           to use symbolic base addresses and lengths

14      20-Oct-01       dec_dz.h, sim_tmxr_h, sim_tmxr.c: fixed bug in Telnet
                           state handling (Thord Nilson), removed
                           tmxr_getchar, added tmxr_rqln and tmxr_tqln

13      18-Oct-01       pdp11_tm.c: added stub diagnostic register clock
                           for RSTS/E (Thord Nilson)

12      15-Oct-01       pdp11_defs.h, pdp11_cpu.c, pdp11_tc.c, pdp11_ts.c,
                           pdp11_rp.c: added operations logging

11      8-Oct-01        scp.c: added sim_rev.h include and version print

                        pdp11_cpu.c: fixed bug in interrupt acknowledge,
                           multiple outstanding interrupts caused the lowest
                           rather than the highest to be acknowledged

10      7-Oct-01        pdp11_stddev.c: added monitor bits (CSR<7>) for full
                           KW11L compatibility, needed for RSTS/E autoconfiguration

9       6-Oct-01        pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: rewrote interrupt
                           logic from RH11/RH70 schematics, to mimic hardware quirks

                        dec_dz.c: fixed bug in carrier detect logic, carrier
                           detect was being cleared on next modem poll

8       4-Oct-01        pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: undid edit of
                           28-Sep-01; real problem was level-sensitive nature of
                           CS1_SC, but CS1_SC can only trigger an interrupt if
                           DONE is set

7       2-Oct-01        pdp11_rp.c, pdp10_rp.c: CS1_SC is evaluated as a level-
                           sensitive, rather than an edge-sensitive, input to
                           interrupt request

6       30-Sep-01       pdp11_rp.c, pdp10_rp.c: separated out CS1<5:0> to per-
                           drive registers

                        pdp10_tu.c: based on above, cleaned up handling of
                           non-existent formatters, fixed non-data transfer
                           commands clearing DONE

5       28-Sep-01       pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: controller should
                           interrupt if ATA or SC sets when IE is set, was
                           interrupting only if DON = 1 as well

4       27-Sep-01       pdp11_ts.c:
                        - NXM errors should return TC4 or TC5; were returning TC3
                        - extended features is part of XS2; was returned in XS3
                        - extended characteristics (fifth) word needed for RSTS/E

                        pdp11_tc.c: stop, stop all do cause an interrupt

                        dec_dz.h: scanner should find a ready output line, even
                           if there are no connections; needed for RSTS/E autoconfigure

                        scp.c:
                        - added routine sim_qcount for 1130
                        - added "simulator exit" detach routine for 1130

                        sim_defs.h: added header for sim_qcount

3       20-Sep-01       pdp11_ts.c: boot code binary was incorrect

2       19-Sep-01       pdp18b_cpu.c: EAE should interpret initial count of 00
                           as 100

                        scp.c: modified Macintosh support

1       17-Sep-01       pdp8_ttx.c: new module for PDP-8 multi-terminal support

                        pdp18b_tt1.c: modified to use sim_tmxr library

                        nova_tt1.c: modified to use sim_tmxr library

                        dec_dz.h: added autodisconnect support

                        scp.c: removed old multiconsole support

                        sim_tmxr.c: modified calling sequence for sim_putchar_ln

                        sim_sock.c: added Macintosh sockets support
*/

#endif
Added src/sim_serial.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_serial.c: OS-dependent serial port routines

   Copyright (c) 2008, J. David Bryan

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

   The author gratefully acknowledges the assistance of Holger Veit with the
   UNIX-specific code and testing.

   07-Oct-08    JDB     [serial] Created file


   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
   returns error codes indicating that the functions failed, inhibiting serial
   port support in SIMH.

   The following routines are provided:

     sim_open_serial        open a serial port
     sim_config_serial      change baud rate and character framing configuration
     sim_control_serial     manipulate and/or return the modem bits on a serial port
     sim_read_serial        read from a serial port
     sim_write_serial       write to a serial port
     sim_close_serial       close a serial port
     sim_show_serial        shows the available host serial ports


   The calling sequences are as follows:


   SERHANDLE sim_open_serial (char *name)
   --------------------------------------

   The serial port referenced by the OS-dependent "name" is opened.  If the open
   is successful, and "name" refers to a serial port on the host system, then a
   handle to the port is returned.  If not, then the value INVALID_HANDLE is
   returned.


   t_stat sim_config_serial (SERHANDLE port, const char *config)
   -------------------------------------------------------------

   The baud rate and framing parameters (character size, parity, and number of
   stop bits) of the serial port associated with "port" are set.  If any
   "config" field value is unsupported by the host system, or if the combination
   of values (e.g., baud rate and number of stop bits) is unsupported, SCPE_ARG
   is returned.  If the configuration is successful, SCPE_OK is returned.


   sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
   -------------------------------------------------------------------------------------------------

   The DTR and RTS line of the serial port is set or cleared as indicated in 
   the respective bits_to_set or bits_to_clear parameters.  If the 
   incoming_bits parameter is not NULL, then the modem status bits DCD, RNG, 
   DSR and CTS are returned.

   If unreasonable or nonsense bits_to_set or bits_to_clear bits are 
   specified, then the return status is SCPE_ARG;
   If an error occurs, SCPE_IOERR is returned.


   int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
   ----------------------------------------------------------------------------

   A non-blocking read is issued for the serial port indicated by "port" to get
   at most "count" bytes into the string "buffer".  If a serial line break was
   detected during the read, the variable pointed to by "brk" is set to 1.  If
   the read is successful, the actual number of characters read is returned.  If
   no characters were available, then the value 0 is returned.  If an error
   occurs, then the value -1 is returned.


   int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
   ------------------------------------------------------------------

   A write is issued to the serial port indicated by "port" to put "count"
   characters from "buffer".  If the write is successful, the actual number of
   characters written is returned.  If an error occurs, then the value -1 is
   returned.


   void sim_close_serial (SERHANDLE port)
   --------------------------------------

   The serial port indicated by "port" is closed.


   int sim_serial_devices (int max, SERIAL_LIST* list)
   ---------------------------------------------------

   enumerates the available host serial ports


   t_stat sim_show_serial (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, const void* desc)
   ---------------------------------

   displays the available host serial ports

*/


#include "sim_defs.h"
#include "sim_serial.h"
#include "sim_tmxr.h"

#include <ctype.h>

#define SER_DEV_NAME_MAX     256                        /* maximum device name size */
#define SER_DEV_DESC_MAX     256                        /* maximum device description size */
#define SER_DEV_CONFIG_MAX    64                        /* maximum device config size */
#define SER_MAX_DEVICE        64                        /* maximum serial devices */

typedef struct serial_list {
    char    name[SER_DEV_NAME_MAX];
    char    desc[SER_DEV_DESC_MAX];
    } SERIAL_LIST;

typedef struct serial_config {                          /* serial port configuration */
    uint32 baudrate;                                    /* baud rate */
    uint32 charsize;                                    /* character size in bits */
    char   parity;                                      /* parity (N/O/E/M/S) */
    uint32 stopbits;                                    /* 0/1/2 stop bits (0 implies 1.5) */
    } SERCONFIG;

static int       sim_serial_os_devices (int max, SERIAL_LIST* list);
static SERHANDLE sim_open_os_serial    (char *name);
static void      sim_close_os_serial   (SERHANDLE port);
static t_stat    sim_config_os_serial  (SERHANDLE port, SERCONFIG config);


static struct open_serial_device {
    SERHANDLE port;
    TMLN *line;
    char name[SER_DEV_NAME_MAX];
    char config[SER_DEV_CONFIG_MAX];
    } *serial_open_devices = NULL;
static int serial_open_device_count = 0;

static struct open_serial_device *_get_open_device (SERHANDLE port)
{
int i;

for (i=0; i<serial_open_device_count; ++i)
    if (serial_open_devices[i].port == port)
        return &serial_open_devices[i];
return NULL;
}

static struct open_serial_device *_get_open_device_byname (const char *name)
{
int i;

for (i=0; i<serial_open_device_count; ++i)
    if (0 == strcmp(name, serial_open_devices[i].name))
        return &serial_open_devices[i];
return NULL;
}

static struct open_serial_device *_serial_add_to_open_list (SERHANDLE port, TMLN *line, const char *name, const char *config)
{
serial_open_devices = (struct open_serial_device *)realloc(serial_open_devices, (++serial_open_device_count)*sizeof(*serial_open_devices));
memset(&serial_open_devices[serial_open_device_count-1], 0, sizeof(serial_open_devices[serial_open_device_count-1]));
serial_open_devices[serial_open_device_count-1].port = port;
serial_open_devices[serial_open_device_count-1].line = line;
strncpy(serial_open_devices[serial_open_device_count-1].name, name, sizeof(serial_open_devices[serial_open_device_count-1].name)-1);
if (config)
    strncpy(serial_open_devices[serial_open_device_count-1].config, config, sizeof(serial_open_devices[serial_open_device_count-1].config));
return &serial_open_devices[serial_open_device_count-1];
}

static void _serial_remove_from_open_list (SERHANDLE port)
{
int i, j;

for (i=0; i<serial_open_device_count; ++i)
    if (serial_open_devices[i].port == port) {
        for (j=i+1; j<serial_open_device_count; ++j)
            serial_open_devices[j-1] = serial_open_devices[j];
        --serial_open_device_count;
        break;
        }
}

/* Generic error message handler.

   This routine should be called for unexpected errors.  Some error returns may
   be expected, e.g., a "file not found" error from an "open" routine.  These
   should return appropriate status codes to the caller, allowing SCP to print
   an error message if desired, rather than printing this generic error message.
*/

static void sim_error_serial (const char *routine, int error)
{
sim_printf ("Serial: %s fails with error %d\n", routine, error);
return;
}

/* Used when sorting a list of serial port names */
static int _serial_name_compare (const void *pa, const void *pb)
{
const SERIAL_LIST *a = (const SERIAL_LIST *)pa;
const SERIAL_LIST *b = (const SERIAL_LIST *)pb;

return strcmp(a->name, b->name);
}

static int sim_serial_devices (int max, SERIAL_LIST *list)
{
int i, j, ports = sim_serial_os_devices(max, list);

/* Open ports may not show up in the list returned by sim_serial_os_devices 
   so we add the open ports to the list removing duplicates before sorting 
   the resulting list */

for (i=0; i<serial_open_device_count; ++i) {
    for (j=0; j<ports; ++j)
        if (0 == strcmp(serial_open_devices[i].name, list[j].name))
            break;
    if (j<ports)
        continue;
    if (ports >= max)
        break;
    strcpy(list[ports].name, serial_open_devices[i].name);
    strcpy(list[ports].desc, serial_open_devices[i].config);
    ++ports;
    }
if (ports) /* Order the list returned alphabetically by the port name */
    qsort (list, ports, sizeof(list[0]), _serial_name_compare);
return ports;
}

static char* sim_serial_getname (int number, char* name)
{
SERIAL_LIST  list[SER_MAX_DEVICE];
int count = sim_serial_devices(SER_MAX_DEVICE, list);

if (count <= number)
    return NULL;
strcpy(name, list[number].name);
return name;
}

static char* sim_serial_getname_bydesc (char* desc, char* name)
{
SERIAL_LIST  list[SER_MAX_DEVICE];
int count = sim_serial_devices(SER_MAX_DEVICE, list);
int i;
size_t j=strlen(desc);

for (i=0; i<count; i++) {
    int found = 1;
    size_t k = strlen(list[i].desc);

    if (j != k)
        continue;
    for (k=0; k<j; k++)
        if (tolower(list[i].desc[k]) != tolower(desc[k]))
            found = 0;
    if (found == 0)
        continue;

    /* found a case-insensitive description match */
    strcpy(name, list[i].name);
    return name;
    }
/* not found */
return NULL;
}

static char* sim_serial_getname_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)) {
        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)) {
        found = 1;
        strcpy(temp, list[i].desc);
        }
    }
  return (found ? temp : NULL);
}

t_stat sim_show_serial (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc)
{
SERIAL_LIST  list[SER_MAX_DEVICE];
int number = sim_serial_devices(SER_MAX_DEVICE, list);

fprintf(st, "Serial devices:\n");
if (number == -1)
    fprintf(st, "  serial support not available in simulator\n");
else
if (number == 0)
    fprintf(st, "  no serial devices are available\n");
else {
    size_t min, len;
    int i;
    for (i=0, min=0; i<number; i++)
        if ((len = strlen(list[i].name)) > min)
            min = len;
    for (i=0; i<number; i++)
        fprintf(st," ser%d\t%-*s%s%s%s\n", i, (int)min, list[i].name, list[i].desc[0] ? " (" : "", list[i].desc, list[i].desc[0] ? ")" : "");
    }
if (serial_open_device_count) {
    int i;
    char desc[SER_DEV_DESC_MAX], *d;

    fprintf(st,"Open Serial Devices:\n");
    for (i=0; i<serial_open_device_count; i++) {
        d = sim_serial_getdesc_byname(serial_open_devices[i].name, desc);
        fprintf(st, " %s\tLn%02d %s%s%s%s\tConfig: %s\n", serial_open_devices[i].line->mp->dptr->name, (int)(serial_open_devices[i].line->mp->ldsc-serial_open_devices[i].line),
                    serial_open_devices[i].line->destination, d ? " {" : "", d ? d : "", d ? ")" : "", serial_open_devices[i].line->serconfig);
        }
    }
return SCPE_OK;
}

SERHANDLE sim_open_serial (char *name, TMLN *lp, t_stat *stat)
{
char temp1[1024], devname [1024];
char *savname = name;
SERHANDLE port = INVALID_HANDLE;
CONST char *config;
t_stat status;

config = get_glyph_nc (name, devname, ';');             /* separate port name from optional config params */

if ((config == NULL) || (*config == '\0'))
    config = "9600-8N1";

if (stat)
    *stat = SCPE_OK;

/* translate name of type "serX" to real device name */
if ((strlen(devname) <= 5)
    && (tolower(devname[0]) == 's')
    && (tolower(devname[1]) == 'e')
    && (tolower(devname[2]) == 'r')
    && (isdigit(devname[3]))
    && (isdigit(devname[4]) || (devname[4] == '\0'))
   ) {
    int num = atoi(&devname[3]);
    savname = sim_serial_getname(num, temp1);
    if (savname == NULL) {                              /* didn't translate */
        if (stat)
            *stat = SCPE_OPENERR;
        return INVALID_HANDLE;
        }
    }
else {
    /* are they trying to use device description? */
    savname = sim_serial_getname_bydesc(devname, temp1);
    if (savname == NULL) {                              /* didn't translate */
        /* probably is not serX and has no description */
        savname = sim_serial_getname_byname(devname, temp1);
        if (savname == NULL) /* didn't translate */
            savname = devname;
        }
    }

if (_get_open_device_byname (savname)) {
    if (stat)
        *stat = SCPE_OPENERR;
    return INVALID_HANDLE;
    }

port = sim_open_os_serial (savname);

if (port == INVALID_HANDLE) {
    if (stat)
        *stat = SCPE_OPENERR;
    return port;
    }

status = sim_config_serial (port, config);              /* set serial configuration */

if (status != SCPE_OK) {                                /* port configuration error? */
    sim_close_serial (port);                            /* close the port */
    if (stat)
        *stat = status;
    port = INVALID_HANDLE;                              /* report error */
    }

if ((port != INVALID_HANDLE) && (*config) && (lp)) {
    lp->serconfig = (char *)realloc (lp->serconfig, 1 + strlen (config));
    strcpy (lp->serconfig, config);
    }
if (port != INVALID_HANDLE)
    _serial_add_to_open_list (port, lp, savname, config);

return port;
}

void sim_close_serial (SERHANDLE port)
{
sim_close_os_serial (port);
_serial_remove_from_open_list (port);
}

t_stat sim_config_serial  (SERHANDLE port, CONST char *sconfig)
{
CONST char *pptr;
CONST char *sptr, *tptr;
SERCONFIG config = { 0 };
t_bool arg_error = FALSE;
t_stat r;
struct open_serial_device *dev;

if ((sconfig == NULL) || (*sconfig == '\0'))
    sconfig = "9600-8N1";                               /* default settings */
pptr = sconfig;

config.baudrate = (uint32)strtotv (pptr, &sptr, 10);    /* parse baud rate */
arg_error = (pptr == sptr);                             /* check for bad argument */

if (*sptr)                                              /* separator present? */
    sptr++;                                             /* skip it */

config.charsize = (uint32)strtotv (sptr, &tptr, 10);    /* parse character size */
arg_error = arg_error || (sptr == tptr);                /* check for bad argument */

if (*tptr)                                              /* parity character present? */
    config.parity = (char)toupper (*tptr++);            /* save parity character */

config.stopbits = (uint32)strtotv (tptr, &sptr, 10);    /* parse number of stop bits */
arg_error = arg_error || (tptr == sptr);                /* check for bad argument */

if (arg_error)                                          /* bad conversions? */
    return SCPE_ARG;                                    /* report argument error */
if (strcmp (sptr, ".5") == 0)                           /* 1.5 stop bits requested? */
    config.stopbits = 0;                                /* code request */

r = sim_config_os_serial (port, config);
dev = _get_open_device (port);
if (dev) {
    dev->line->serconfig = (char *)realloc (dev->line->serconfig, 1 + strlen (sconfig));
    strcpy (dev->line->serconfig, sconfig);
    }
return r;
}

#if defined (_WIN32)

/* Windows serial implementation */

/* Enumerate the available serial ports.

   The serial port names are extracted from the appropriate place in the 
   windows registry (HKLM\HARDWARE\DEVICEMAP\SERIALCOMM\).  The resulting
   list is sorted alphabetically by device name (COMn).  The device description 
   is set to the OS internal name for the COM device.

*/

struct SERPORT {
    HANDLE hPort;
    DWORD dwEvtMask;
    OVERLAPPED oReadSync;
    OVERLAPPED oWriteReady;
    OVERLAPPED oWriteSync;
    };

static int sim_serial_os_devices (int max, SERIAL_LIST* list)
{
int ports = 0;
HKEY hSERIALCOMM;

memset(list, 0, max*sizeof(*list));
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hSERIALCOMM) == ERROR_SUCCESS) {
    DWORD dwIndex = 0;
    DWORD dwType;
    DWORD dwValueNameSize = sizeof(list[ports].desc);
    DWORD dwDataSize = sizeof(list[ports].name);

    /* Enumerate all the values underneath HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM */
    while (RegEnumValueA(hSERIALCOMM, dwIndex, list[ports].desc, &dwValueNameSize, NULL, &dwType, (BYTE *)list[ports].name, &dwDataSize) == ERROR_SUCCESS) {
        /* String values with non-zero size are the interesting ones */
        if ((dwType == REG_SZ) && (dwDataSize > 0)) {
            if (ports < max)
                ++ports;
            else
                break;
            }
        /* Besure to clear the working entry before trying again */
        memset(list[ports].name, 0, sizeof(list[ports].name));
        memset(list[ports].desc, 0, sizeof(list[ports].desc));
        dwValueNameSize = sizeof(list[ports].desc);
        dwDataSize = sizeof(list[ports].name);
        ++dwIndex;
        }
    RegCloseKey(hSERIALCOMM);
    }
return ports;
}

/* Open a serial port.

   The serial port designated by "name" is opened, and the handle to the port is
   returned.  If an error occurs, INVALID_HANDLE is returned instead.  After
   opening, the port is configured with the default communication parameters
   established by the system, and the timeouts are set for immediate return on a
   read request to enable polling.

   Implementation notes:

    1. We call "GetDefaultCommConfig" to obtain the default communication
       parameters for the specified port.  If the name does not refer to a
       communications port (serial or parallel), the function fails.

    2. There is no way to limit "CreateFile" just to serial ports, so we must
       check after the port is opened.  The "GetCommState" routine will return
       an error if the handle does not refer to a serial port.

    3. Calling "GetDefaultCommConfig" for a serial port returns a structure
       containing a DCB.  This contains the default parameters.  However, some
       of the DCB fields are not set correctly, so we cannot use this directly
       in a call to "SetCommState".  Instead, we must copy the fields of
       interest to a DCB retrieved from a call to "GetCommState".
*/

static SERHANDLE sim_open_os_serial (char *name)
{
HANDLE hPort;
SERHANDLE port;
DCB dcb;
COMMCONFIG commdefault;
DWORD error;
DWORD commsize = sizeof (commdefault);
COMMTIMEOUTS cto;

if (!GetDefaultCommConfig (name, &commdefault, &commsize)) {    /* get default comm parameters */
    error = GetLastError ();                                    /* function failed; get error */

    if (error != ERROR_INVALID_PARAMETER)                       /* not a communications port name? */
        sim_error_serial ("GetDefaultCommConfig", (int) error); /* no, so report unexpected error */

    return INVALID_HANDLE;                                      /* indicate bad port name */
    }

hPort = CreateFile (name, GENERIC_READ | GENERIC_WRITE, /* open the port */
                   0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

if (hPort == INVALID_HANDLE_VALUE) {                    /* open failed? */
    error = GetLastError ();                            /* get error code */

    if ((error != ERROR_FILE_NOT_FOUND) &&              /* bad filename? */
        (error != ERROR_ACCESS_DENIED))                 /* already open? */
        sim_error_serial ("CreateFile", (int) error);   /* no, so report unexpected error */

    return INVALID_HANDLE;                              /* indicate bad port name */
    }

port = (SERHANDLE)calloc (1, sizeof(*port));            /* instantiate the SERHANDLE */
port->hPort = hPort;

if (!GetCommState (port->hPort, &dcb)) {                /* get the current comm parameters */
    error = GetLastError ();                            /* function failed; get error */

    if (error != ERROR_INVALID_PARAMETER)               /* not a serial port name? */
        sim_error_serial ("GetCommState", (int) error); /* no, so report unexpected error */

    sim_close_os_serial (port);                         /* close port */
    return INVALID_HANDLE;                              /*   and indicate bad port name */
    }

dcb.BaudRate = commdefault.dcb.BaudRate;                /* copy default parameters of interest */
dcb.Parity   = commdefault.dcb.Parity;
dcb.ByteSize = commdefault.dcb.ByteSize;
dcb.StopBits = commdefault.dcb.StopBits;
dcb.fOutX    = commdefault.dcb.fOutX;
dcb.fInX     = commdefault.dcb.fInX;

dcb.fDtrControl = DTR_CONTROL_DISABLE;                  /* disable DTR initially until poll connects */

if (!SetCommState (port->hPort, &dcb)) {                /* configure the port with default parameters */
    sim_error_serial ("SetCommState",                   /* function failed; report unexpected error */
                      (int) GetLastError ());
    sim_close_os_serial (port);                         /* close port */
    return INVALID_HANDLE;                              /*   and indicate failure to caller */
    }

cto.ReadIntervalTimeout         = MAXDWORD;             /* set port to return immediately on read */
cto.ReadTotalTimeoutMultiplier  = 0;                    /* i.e., to enable polling */
cto.ReadTotalTimeoutConstant    = 0;
cto.WriteTotalTimeoutMultiplier = 0;
cto.WriteTotalTimeoutConstant   = 0;

if (!SetCommTimeouts (port->hPort, &cto)) {             /* configure port timeouts */
    sim_error_serial ("SetCommTimeouts",                /* function failed; report unexpected error */
                      (int) GetLastError ());
    sim_close_os_serial (port);                         /* close port */
    return INVALID_HANDLE;                              /*   and indicate failure to caller */
    }

/* Create an event object for use by WaitCommEvent. */

port->oWriteReady.hEvent = CreateEvent(NULL,            /* default security attributes */
                                       TRUE,            /* manual-reset event */
                                       TRUE,            /* signaled */
                                       NULL);           /* no name */
if (port->oWriteReady.hEvent == NULL) {
    sim_error_serial ("CreateEvent",                    /* function failed; report unexpected error */
                      (int) GetLastError ());
    sim_close_os_serial (port);                         /* close port */
    return INVALID_HANDLE;                              /*   and indicate failure to caller */
    }

port->oReadSync.hEvent = CreateEvent(NULL,              /* default security attributes */
                                     TRUE,              /* manual-reset event */
                                     FALSE,             /* not signaled */
                                     NULL);             /* no name */
if (port->oReadSync.hEvent == NULL) {
    sim_error_serial ("CreateEvent",                    /* function failed; report unexpected error */
                      (int) GetLastError ());
    sim_close_os_serial (port);                         /* close port */
    return INVALID_HANDLE;                              /*   and indicate failure to caller */
    }

port->oWriteSync.hEvent = CreateEvent(NULL,             /* default security attributes */
                                      TRUE,             /* manual-reset event */
                                      FALSE,            /* not signaled */
                                      NULL);            /* no name */
if (port->oWriteSync.hEvent == NULL) {
    sim_error_serial ("CreateEvent",                    /* function failed; report unexpected error */
                      (int) GetLastError ());
    sim_close_os_serial (port);                         /* close port */
    return INVALID_HANDLE;                              /*   and indicate failure to caller */
    }

if (!SetCommMask (port->hPort, EV_TXEMPTY)) {
    sim_error_serial ("SetCommMask",                    /* function failed; report unexpected error */
                      (int) GetLastError ());
    sim_close_os_serial (port);                         /* close port */
    return INVALID_HANDLE;                              /*   and indicate failure to caller */
    }

return port;                                            /* return port handle on success */
}


/* Configure a serial port.

   Port parameters are configured as specified in the "config" structure.  If
   "config" contains an invalid configuration value, or if the host system
   rejects the configuration (e.g., by requesting an unsupported combination of
   character size and stop bits), SCPE_ARG is returned to the caller.  If an
   unexpected error occurs, SCPE_IOERR is returned.  If the configuration
   succeeds, SCPE_OK is returned.

   Implementation notes:

    1. We do not enable input parity checking, as the multiplexer library has no
       way of communicating parity errors back to the target simulator.

    2. A zero value for the "stopbits" field of the "config" structure implies
       1.5 stop bits.
*/

static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config)
{
static const struct {
    char parity;
    BYTE parity_code;
    } parity_map [] =
        { { 'E', EVENPARITY }, { 'M', MARKPARITY  }, { 'N', NOPARITY },
          { 'O', ODDPARITY  }, { 'S', SPACEPARITY } };

static const int32 parity_count = sizeof (parity_map) / sizeof (parity_map [0]);

DCB dcb;
DWORD error;
int32 i;

if (!GetCommState (port->hPort, &dcb)) {                /* get the current comm parameters */
    sim_error_serial ("GetCommState",                   /* function failed; report unexpected error */
                      (int) GetLastError ());
    return SCPE_IOERR;                                  /* return failure status */
    }

dcb.BaudRate = config.baudrate;                         /* assign baud rate */

if (config.charsize >= 5 && config.charsize <= 8)       /* character size OK? */
    dcb.ByteSize = (BYTE)config.charsize;               /* assign character size */
else
    return SCPE_ARG;                                    /* not a valid size */

for (i = 0; i < parity_count; i++)                      /* assign parity */
    if (config.parity == parity_map [i].parity) {       /* match mapping value? */
        dcb.Parity = parity_map [i].parity_code;        /* assign corresponding code */
        break;
        }

if (i == parity_count)                                  /* parity assigned? */
    return SCPE_ARG;                                    /* not a valid parity specifier */

if (config.stopbits == 1)                               /* assign stop bits */
    dcb.StopBits = ONESTOPBIT;
else if (config.stopbits == 2)
    dcb.StopBits = TWOSTOPBITS;
else if (config.stopbits == 0)                          /* 0 implies 1.5 stop bits */
    dcb.StopBits = ONE5STOPBITS;
else
    return SCPE_ARG;                                    /* not a valid number of stop bits */

if (!SetCommState (port->hPort, &dcb)) {                /* set the configuration */
    error = GetLastError ();                            /* check for error */

    if (error == ERROR_INVALID_PARAMETER)               /* invalid configuration? */
        return SCPE_ARG;                                /* report as argument error */

    sim_error_serial ("SetCommState", (int) error);     /* function failed; report unexpected error */
    return SCPE_IOERR;                                  /* return failure status */
    }

return SCPE_OK;                                         /* return success status */
}


/* Control a serial port.

   The DTR and RTS line of the serial port is set or cleared as indicated in 
   the respective bits_to_set or bits_to_clear parameters.  If the 
   incoming_bits parameter is not NULL, then the modem status bits DCD, RNG, 
   DSR and CTS are returned.

   If unreasonable or nonsense bits_to_set or bits_to_clear bits are 
   specified, then the return status is SCPE_ARG;
   If an error occurs, SCPE_IOERR is returned.
*/

t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
{
if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) ||         /* Assure only settable bits */
    (bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
    (bits_to_set & bits_to_clear))                  /* and can't set and clear the same bits */
    return SCPE_ARG;
if (bits_to_set&TMXR_MDM_DTR)
    if (!EscapeCommFunction (port->hPort, SETDTR)) {
        sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
        return SCPE_IOERR;
        }
if (bits_to_clear&TMXR_MDM_DTR)
    if (!EscapeCommFunction (port->hPort, CLRDTR)) {
        sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
        return SCPE_IOERR;
        }
if (bits_to_set&TMXR_MDM_RTS)
    if (!EscapeCommFunction (port->hPort, SETRTS)) {
        sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
        return SCPE_IOERR;
        }
if (bits_to_clear&TMXR_MDM_RTS)
    if (!EscapeCommFunction (port->hPort, CLRRTS)) {
        sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
        return SCPE_IOERR;
        }
if (incoming_bits) {
    DWORD ModemStat;
    if (GetCommModemStatus (port->hPort, &ModemStat)) {
        sim_error_serial ("GetCommModemStatus", (int) GetLastError ());
        return SCPE_IOERR;
        }
    *incoming_bits = ((ModemStat&MS_CTS_ON)  ? TMXR_MDM_CTS : 0) |
                     ((ModemStat&MS_DSR_ON)  ? TMXR_MDM_DSR : 0) |
                     ((ModemStat&MS_RING_ON) ? TMXR_MDM_RNG : 0) |
                     ((ModemStat&MS_RLSD_ON) ? TMXR_MDM_DCD : 0);
    }
return SCPE_OK;
}


/* Read from a serial port.

   The port is checked for available characters.  If any are present, they are
   copied to the passed buffer, and the count of characters is returned.  If no
   characters are available, 0 is returned.  If an error occurs, -1 is returned.
   If a BREAK is detected on the communications line, the corresponding flag in
   the "brk" array is set.

   Implementation notes:

    1. The "ClearCommError" function will set the CE_BREAK flag in the returned
       errors value if a BREAK has occurred.  However, we do not know where in
       the serial stream it happened, as CE_BREAK isn't associated with a
       specific character.  Because the "brk" array does want a flag associated
       with a specific character, we guess at the proper location by setting
       the "brk" entry corresponding to the first NUL in the character stream.
       If no NUL is present, then the "brk" entry associated with the first
       character is set.
*/

int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
{
DWORD read;
DWORD commerrors;
COMSTAT cs;
char *bptr;

if (!ClearCommError (port->hPort, &commerrors, &cs)) {  /* get the comm error flags  */
    sim_error_serial ("ClearCommError",                 /* function failed; report unexpected error */
                      (int) GetLastError ());
    return -1;                                          /* return failure to caller */
    }

if (!ReadFile (port->hPort, (LPVOID) buffer,            /* read any available characters */
               (DWORD) count, &read, &port->oReadSync)) {
    sim_error_serial ("ReadFile",                       /* function failed; report unexpected error */
                      (int) GetLastError ());
    return -1;                                          /* return failure to caller */
    }

if (commerrors & CE_BREAK) {                            /* was a BREAK detected? */
    bptr = (char *) memchr (buffer, 0, read);           /* search for the first NUL in the buffer */

    if (bptr)                                           /* was one found? */
        brk = brk + (bptr - buffer);                    /* calculate corresponding position */

    *brk = 1;                                           /* set the BREAK flag */
    }

return read;                                            /* return the number of characters read */
}


/* Write to a serial port.

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

   The serial port is closed.  Errors are ignored.
*/

static void sim_close_os_serial (SERHANDLE port)
{
if (port->oWriteReady.hEvent)
    CloseHandle (port->oWriteReady.hEvent);               /* close the event handle */
if (port->oReadSync.hEvent)
    CloseHandle (port->oReadSync.hEvent);               /* close the event handle */
if (port->oWriteSync.hEvent)
    CloseHandle (port->oWriteSync.hEvent);              /* close the event handle */
if (port->hPort)
    CloseHandle (port->hPort);                          /* close the port */
free (port);
}



#elif defined (__unix__) || defined(__APPLE__) || defined(__hpux)

struct SERPORT {
    int port;
    };

#if defined(__linux) || defined(__linux__)
#include <dirent.h>
#include <libgen.h>
#include <unistd.h>
#include <sys/stat.h>
#endif /* __linux__ */

/* UNIX implementation */

/* Enumerate the available serial ports.

   The serial port names generated by attempting to open /dev/ttyS0 thru
   /dev/ttyS63 and /dev/ttyUSB0 thru /dev/ttyUSB63 and /dev/tty.serial0
   thru /dev/tty.serial63.  Ones we can open and are ttys (as determined 
   by isatty()) are added to the list.  The list is sorted alphabetically 
   by device name.

*/

static int sim_serial_os_devices (int max, SERIAL_LIST* list)
{
int i;
int port;
int ports = 0;

memset(list, 0, max*sizeof(*list));
#if defined(__linux) || defined(__linux__)
if (1) {
    struct dirent **namelist;
    struct stat st;

    i = scandir("/sys/class/tty/", &namelist, NULL, NULL);

    while (i--) {
        if (strcmp(namelist[i]->d_name, ".") &&
            strcmp(namelist[i]->d_name, "..")) {
            char path[1024], devicepath[1024], driverpath[1024];

            sprintf (path, "/sys/class/tty/%s", namelist[i]->d_name);
            sprintf (devicepath, "/sys/class/tty/%s/device", namelist[i]->d_name);
            sprintf (driverpath, "/sys/class/tty/%s/device/driver", namelist[i]->d_name);
            if ((lstat(devicepath, &st) == 0) && S_ISLNK(st.st_mode)) {
                char buffer[1024];

                memset (buffer, 0, sizeof(buffer));
                if (readlink(driverpath, buffer, sizeof(buffer)) > 0) {
                    sprintf (list[ports].name, "/dev/%s", basename (path));
                    port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK);     /* open the port */
                    if (port != -1) {                   /* open OK? */
                        if (isatty (port))              /* is device a TTY? */
                            ++ports;
                        close (port);
                        }
                    }
                }
            }
        free (namelist[i]);
        }
    free (namelist);
    }
#elif defined(__hpux)
for (i=0; (ports < max) && (i < 64); ++i) {
    sprintf (list[ports].name, "/dev/tty%dp%d", i/8, i%8);
    port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK);     /* open the port */
    if (port != -1) {                                   /* open OK? */
        if (isatty (port))                              /* is device a TTY? */
            ++ports;
        close (port);
        }
    }
#else /* Non Linux/HP-UX, just try some well known device names */
for (i=0; (ports < max) && (i < 64); ++i) {
    sprintf (list[ports].name, "/dev/ttyS%d", i);
    port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK);     /* open the port */
    if (port != -1) {                                   /* open OK? */
        if (isatty (port))                              /* is device a TTY? */
            ++ports;
        close (port);
        }
    }
for (i=0; (ports < max) && (i < 64); ++i) {
    sprintf (list[ports].name, "/dev/ttyUSB%d", i);
    port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK);     /* open the port */
    if (port != -1) {                                   /* open OK? */
        if (isatty (port))                              /* is device a TTY? */
            ++ports;
        close (port);
        }
    }
for (i=1; (ports < max) && (i < 64); ++i) {
    sprintf (list[ports].name, "/dev/tty.serial%d", i);
    port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK);     /* open the port */
    if (port != -1) {                                   /* open OK? */
        if (isatty (port))                              /* is device a TTY? */
            ++ports;
        close (port);
        }
    }
for (i=0; (ports < max) && (i < 64); ++i) {
    sprintf (list[ports].name, "/dev/tty%02d", i);
    port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK);     /* open the port */
    if (port != -1) {                                   /* open OK? */
        if (isatty (port))                              /* is device a TTY? */
            ++ports;
        close (port);
        }
    }
for (i=0; (ports < max) && (i < 8); ++i) {
    sprintf (list[ports].name, "/dev/ttyU%d", i);
    port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK);     /* open the port */
    if (port != -1) {                                   /* open OK? */
        if (isatty (port))                              /* is device a TTY? */
            ++ports;
        close (port);
        }
    }
#endif
return ports;
}

/* Open a serial port.

   The serial port designated by "name" is opened, and the handle to the port is
   returned.  If an error occurs, INVALID_HANDLE is returned instead.  After
   opening, the port is configured to "raw" mode.

   Implementation notes:

    1. We use a non-blocking open to allow for polling during reads.

    2. There is no way to limit "open" just to serial ports, so we must check
       after the port is opened.  We do this with a combination of "isatty" and
       "tcgetattr".

    3. We configure with PARMRK set and IGNBRK and BRKINT cleared.  This will
       mark a communication line BREAK condition in the input stream with the
       three-character sequence \377 \000 \000.  This is detected during
       reading.
*/

static SERHANDLE sim_open_os_serial (char *name)
{
static const tcflag_t i_clear = IGNBRK |                /* ignore BREAK */
                                BRKINT |                /* signal on BREAK */
                                INPCK  |                /* enable parity checking */
                                ISTRIP |                /* strip character to 7 bits */
                                INLCR  |                /* map NL to CR */
                                IGNCR  |                /* ignore CR */
                                ICRNL  |                /* map CR to NL */
                                IXON   |                /* enable XON/XOFF output control */
                                IXOFF;                  /* enable XON/XOFF input control */

static const tcflag_t i_set   = PARMRK |                /* mark parity errors and line breaks */
                                IGNPAR;                 /* ignore parity errors */

static const tcflag_t o_clear = OPOST;                  /* post-process output */

static const tcflag_t o_set   = 0;

static const tcflag_t c_clear = HUPCL;                  /* hang up line on last close */

static const tcflag_t c_set   = CREAD |                 /* enable receiver */
                                CLOCAL;                 /* ignore modem status lines */

static const tcflag_t l_clear = ISIG    |               /* enable signals */
                                ICANON  |               /* canonical input */
                                ECHO    |               /* echo characters */
                                ECHOE   |               /* echo ERASE as an error correcting backspace */
                                ECHOK   |               /* echo KILL */
                                ECHONL  |               /* echo NL */
                                NOFLSH  |               /* disable flush after interrupt */
                                TOSTOP  |               /* send SIGTTOU for background output */
                                IEXTEN;                 /* enable extended functions */

static const tcflag_t l_set   = 0;
int port;
SERHANDLE serport;
struct termios tio;

port = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK);     /* open the port */

if (port == -1) {                                       /* open failed? */
    if (errno != ENOENT && errno != EACCES)             /* file not found or can't open? */
        sim_error_serial ("open", errno);               /* no, so report unexpected error */

    return INVALID_HANDLE;                              /* indicate failure to caller */
    }

if (!isatty (port)) {                                   /* is device a TTY? */
    close (port);                                       /* no, so close it */
    return INVALID_HANDLE;                              /*   and return failure to caller */
    }

if (tcgetattr (port, &tio)) {                           /* get the terminal attributes */
    sim_error_serial ("tcgetattr", errno);              /* function failed; report unexpected error */
    close (port);                                       /* close the port */
    return INVALID_HANDLE;                              /*   and return failure to caller */
    }

// which of these methods is best?

#if 1

tio.c_iflag = (tio.c_iflag & ~i_clear) | i_set;           /* configure the serial line for raw mode */
tio.c_oflag = (tio.c_oflag & ~o_clear) | o_set;
tio.c_cflag = (tio.c_cflag & ~c_clear) | c_set;
tio.c_lflag = (tio.c_lflag & ~l_clear) | l_set;
#ifdef VMIN
tio.c_cc[VMIN] = 1;
#endif
#ifdef VTIME
tio.c_cc[VTIME] = 0;
#endif

#elif 0

tio.c_iflag &= ~(IGNBRK | BRKINT | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF);
tio.c_iflag |= PARMRK | IGNPAR;
tio.c_oflag &= ~(OPOST);
tio.c_cflag &= ~(HUPCL);
tio.c_cflag |= CREAD | CLOCAL;
tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | NOFLSH | TOSTOP | IEXTEN);

#elif 0

tio.c_iflag = PARMRK | IGNPAR;
tio.c_oflag = 0;
tio.c_cflag = tio.c_cflag | CLOCAL | CREAD;
tio.c_lflag = 0;

#endif

if (tcsetattr (port, TCSANOW, &tio)) {                  /* set the terminal attributes */
    sim_error_serial ("tcsetattr", errno);              /* function failed; report unexpected error */
    close (port);                                       /* close the port */
    return INVALID_HANDLE;                              /*   and return failure to caller */
    }

serport = (SERHANDLE)calloc (1, sizeof(*serport));
serport->port = port;
return serport;                                         /* return port fd for success */
}


/* Configure a serial port.

   Port parameters are configured as specified in the "config" structure.  If
   "config" contains an invalid configuration value, or if the host system
   rejects the configuration (e.g., by requesting an unsupported combination of
   character size and stop bits), SCPE_ARG is returned to the caller.  If an
   unexpected error occurs, SCPE_IOERR is returned.  If the configuration
   succeeds, SCPE_OK is returned.

   Implementation notes:

    1. 1.5 stop bits is not a supported configuration.

*/

static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config)
{
struct termios tio;
int32 i;

static const struct {
    uint32  rate;
    speed_t rate_code;
    } baud_map [] =
        { { 50,     B50     }, { 75,     B75     }, { 110,    B110    }, {  134,   B134   },
          { 150,    B150    }, { 200,    B200    }, { 300,    B300    }, {  600,   B600   },
          { 1200,   B1200   }, { 1800,   B1800   }, { 2400,   B2400   }, {  4800,  B4800  },
          { 9600,   B9600   }, { 19200,  B19200  }, { 38400,  B38400  }, {  57600, B57600 },
          { 115200, B115200 } };

static const int32 baud_count = sizeof (baud_map) / sizeof (baud_map [0]);

static const tcflag_t charsize_map [4] = { CS5, CS6, CS7, CS8 };


if (tcgetattr (port->port, &tio)) {                     /* get the current configuration */
    sim_error_serial ("tcgetattr", errno);              /* function failed; report unexpected error */
    return SCPE_IOERR;                                  /* return failure status */
    }

for (i = 0; i < baud_count; i++)                        /* assign baud rate */
    if (config.baudrate == baud_map [i].rate) {         /* match mapping value? */
        cfsetispeed(&tio, baud_map [i].rate_code);      /* set input rate */
        cfsetospeed(&tio, baud_map [i].rate_code);      /* set output rate */
        break;
        }

if (i == baud_count)                                    /* baud rate assigned? */
    return SCPE_ARG;                                    /* invalid rate specified */

if ((config.charsize >= 5) && (config.charsize <= 8))   /* character size OK? */
    tio.c_cflag = (tio.c_cflag & ~CSIZE) |              /* replace character size code */
                charsize_map [config.charsize - 5];
else
    return SCPE_ARG;                                    /* not a valid size */

switch (config.parity) {                                /* assign parity */
    case 'E':
        tio.c_cflag = (tio.c_cflag & ~PARODD) | PARENB; /* set for even parity */
        break;

    case 'N':
        tio.c_cflag = tio.c_cflag & ~PARENB;            /* set for no parity */
        break;

    case 'O':
        tio.c_cflag = tio.c_cflag | PARODD | PARENB;    /* set for odd parity */
        break;

    default:
        return SCPE_ARG;                                /* not a valid parity specifier */
    }

if (config.stopbits == 1)                               /* one stop bit? */
    tio.c_cflag = tio.c_cflag & ~CSTOPB;                /* clear two-bits flag */
else if (config.stopbits == 2)                          /* two stop bits? */
    tio.c_cflag = tio.c_cflag | CSTOPB;                 /* set two-bits flag */
else                                                    /* some other number? */
    return SCPE_ARG;                                    /* not a valid number of stop bits */

if (tcsetattr (port->port, TCSAFLUSH, &tio)) {          /* set the new configuration */
    sim_error_serial ("tcsetattr", errno);              /* function failed; report unexpected error */
    return SCPE_IERR;                                   /* return failure status */
    }

return SCPE_OK;                                         /* configuration set successfully */
}


/* Control a serial port.

   The DTR and RTS line of the serial port is set or cleared as indicated in 
   the respective bits_to_set or bits_to_clear parameters.  If the 
   incoming_bits parameter is not NULL, then the modem status bits DCD, RNG, 
   DSR and CTS are returned.

   If unreasonable or nonsense bits_to_set or bits_to_clear bits are 
   specified, then the return status is SCPE_ARG;
   If an error occurs, SCPE_IOERR is returned.
*/

t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
{
int bits;

if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) ||         /* Assure only settable bits */
    (bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
    (bits_to_set & bits_to_clear))                  /* and can't set and clear the same bits */
    return SCPE_ARG;
if (bits_to_set) {
    bits = ((bits_to_set&TMXR_MDM_DTR) ? TIOCM_DTR : 0) |
           ((bits_to_set&TMXR_MDM_RTS) ? TIOCM_RTS : 0);
    if (ioctl (port->port, TIOCMBIS, &bits)) {      /* set the desired bits */
        sim_error_serial ("ioctl", errno);          /* report unexpected error */
        return SCPE_IOERR;                          /* return failure status */
        }
    }
if (bits_to_clear) {
    bits = ((bits_to_clear&TMXR_MDM_DTR) ? TIOCM_DTR : 0) |
           ((bits_to_clear&TMXR_MDM_RTS) ? TIOCM_RTS : 0);
    if (ioctl (port->port, TIOCMBIC, &bits)) {      /* clear the desired bits */
        sim_error_serial ("ioctl", errno);          /* report unexpected error */
        return SCPE_IOERR;                          /* return failure status */
        }
    }
if (incoming_bits) {
    if (ioctl (port->port, TIOCMGET, &bits)) {      /* get the modem bits */
        sim_error_serial ("ioctl", errno);          /* report unexpected error */
        return SCPE_IOERR;                          /* return failure status */
        }
    *incoming_bits = ((bits&TIOCM_CTS) ? TMXR_MDM_CTS : 0) |
                     ((bits&TIOCM_DSR) ? TMXR_MDM_DSR : 0) |
                     ((bits&TIOCM_RNG) ? TMXR_MDM_RNG : 0) |
                     ((bits&TIOCM_CAR) ? TMXR_MDM_DCD : 0);
    }

return SCPE_OK;
}


/* Read from a serial port.

   The port is checked for available characters.  If any are present, they are
   copied to the passed buffer, and the count of characters is returned.  If no
   characters are available, 0 is returned.  If an error occurs, -1 is returned.
   If a BREAK is detected on the communications line, the corresponding flag in
   the "brk" array is set.

   Implementation notes:

    1. A character with a framing or parity error is indicated in the input
       stream by the three-character sequence \377 \000 \ccc, where "ccc" is the
       bad character.  A communications line BREAK is indicated by the sequence
       \377 \000 \000.  A received \377 character is indicated by the
       two-character sequence \377 \377.  If we find any of these sequences,
       they are replaced by the single intended character by sliding the
       succeeding characters backward by one or two positions.  If a BREAK
       sequence was encountered, the corresponding location in the "brk" array
       is determined, and the flag is set.  Note that there may be multiple
       sequences in the buffer.
*/

int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
{
int read_count;
char *bptr, *cptr;
int32 remaining;

read_count = read (port->port, (void *) buffer, (size_t) count);/* read from the serial port */

if (read_count == -1)                                       /* read error? */
    if (errno == EAGAIN)                                    /* no characters available? */
        return 0;                                           /* return 0 to indicate */
    else                                                    /* some other problem */
        sim_error_serial ("read", errno);                   /* report unexpected error */

else {                                                      /* read succeeded */
    cptr = buffer;                                          /* point at start of buffer */
    remaining = read_count - 1;                             /* stop search one char from end of string */

    while (remaining > 0 &&                                 /* still characters to search? */
           (bptr = (char*)memchr (cptr, '\377', remaining))) {/* search for start of PARMRK sequence */
        remaining = remaining - (bptr - cptr) - 1;          /* calc characters remaining */

        if (*(bptr + 1) == '\377') {                        /* is it a \377 \377 sequence? */
            memmove (bptr + 1, bptr + 2, remaining);        /* slide string backward to leave first \377 */
            remaining = remaining - 1;                      /* drop remaining count */
            read_count = read_count - 1;                    /*   and read count by char eliminated */
            }

        else if (remaining > 0 && *(bptr + 1) == '\0') {    /* is it a \377 \000 \ccc sequence? */
            memmove (bptr, bptr + 2, remaining);            /* slide string backward to leave \ccc */
            remaining = remaining - 2;                      /* drop remaining count */
            read_count = read_count - 2;                    /*   and read count by chars eliminated */

            if (*bptr == '\0')                              /* is it a BREAK sequence? */
                *(brk + (bptr - buffer)) = 1;               /* set corresponding BREAK flag */
            }

        cptr = bptr + 1;                                    /* point at remainder of string */
        }
    }

return (int32) read_count;                                  /* return the number of characters read */
}


/* Write to a serial port.

   "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)
{
int written;

written = write (port->port, (void *) buffer, (size_t) count);/* write the buffer to the serial port */

if (written == -1) {
    if (errno == EWOULDBLOCK)
        written = 0;                                        /* not an error, but nothing written */
#if defined(EAGAIN)
    else if (errno == EAGAIN)
        written = 0;                                        /* not an error, but nothing written */
#endif
    else                                                    /* unexpected error? */
        sim_error_serial ("write", errno);                  /* report it */
    }

return (int32) written;                                     /* return number of characters written */
}


/* Close a serial port.

   The serial port is closed.  Errors are ignored.
*/

static void sim_close_os_serial (SERHANDLE port)
{
close (port->port);                                           /* close the port */
free (port);
}


#elif defined (VMS)

/* VMS implementation */

#if defined(__VAX)
#define sys$assign SYS$ASSIGN
#define sys$qiow SYS$QIOW
#define sys$dassgn SYS$DASSGN
#define sys$device_scan SYS$DEVICE_SCAN
#define sys$getdviw SYS$GETDVIW
#endif

#include <descrip.h>
#include <ttdef.h>
#include <tt2def.h>
#include <iodef.h>
#include <ssdef.h>
#include <dcdef.h>
#include <dvsdef.h>
#include <dvidef.h>
#include <starlet.h>
#include <unistd.h>

typedef struct {
    unsigned short sense_count;
    unsigned char sense_first_char;
    unsigned char sense_reserved;
    unsigned int stat;
    unsigned int stat2; } SENSE_BUF;

typedef struct {
    unsigned short status;
    unsigned short count;
    unsigned int dev_status; } IOSB;

typedef struct {
    unsigned short buffer_size;
    unsigned short item_code;
    void *buffer_address;
    void *return_length_address;
    } ITEM;

struct SERPORT {
    uint32 port;
    IOSB write_iosb;
    };

/* Enumerate the available serial ports.

   The serial port names generated by attempting to open /dev/ttyS0 thru
   /dev/ttyS53 and /dev/ttyUSB0 thru /dev/ttyUSB0.  Ones we can open and
   are ttys (as determined by isatty()) are added to the list.  The list 
   is sorted alphabetically by device name.

*/

static int sim_serial_os_devices (int max, SERIAL_LIST* list)
{
$DESCRIPTOR (wild, "*");
char devstr[sizeof(list[0].name)];
$DESCRIPTOR (device, devstr);
int ports;
IOSB iosb;
uint32 status;
uint32 devsts;
#define UCB$M_TEMPLATE 0x2000       /* Device is a template device */
#define UCB$M_ONLINE   0x0010       /* Device is online */
uint32 devtype;
uint32 devdepend;
#define DEV$M_RTM 0x20000000
uint32 devnamlen = 0;
t_bool done = FALSE;
uint32 context[2];
uint32 devclass = DC$_TERM; /* Only interested in terminal devices */
ITEM select_items[] = { {sizeof (devclass), DVS$_DEVCLASS, &devclass, NULL},
                        {                  0,               0,        NULL, NULL}};
ITEM valid_items[] =  { {    sizeof (devsts),        DVI$_STS,     &devsts, NULL},
                        {     sizeof(devstr),     DVI$_DEVNAM,      devstr, &devnamlen},
                        {    sizeof(devtype),    DVI$_DEVTYPE,    &devtype, NULL},
                        {  sizeof(devdepend),  DVI$_DEVDEPEND,  &devdepend, NULL},
                        {                  0,               0,        NULL, NULL}};

memset(context, 0, sizeof(context));
memset(devstr, 0, sizeof(devstr));
memset(list, 0, max*sizeof(*list));
for (ports=0; (ports < max); ++ports) {
    device.dsc$w_length = sizeof (devstr) - 1;
    status = sys$device_scan (&device,
                              &device.dsc$w_length,
                              &wild,
                              select_items,
                              &context);
    switch (status) {
        case SS$_NOSUCHDEV:
        case SS$_NOMOREDEV:
            done = TRUE;
            break;
        default:
            if (0 == (status&1))
                done = TRUE;
            else {
                status = sys$getdviw (0, 0, &device, valid_items, &iosb, NULL, 0, NULL);
                if (status == SS$_NORMAL)
                    status = iosb.status;
                if (status != SS$_NORMAL) {
                    done = TRUE;
                    break;
                    }
                device.dsc$w_length = devnamlen;
                if ((0 == (devsts & UCB$M_TEMPLATE)) &&
                    (0 != (devsts & UCB$M_ONLINE)) &&
                    (0 == (devdepend & DEV$M_RTM))) {
                    devstr[device.dsc$w_length] = '\0';
                    strcpy (list[ports].name, devstr);
                    while (list[ports].name[0] == '_')
                        strcpy (list[ports].name, list[ports].name+1);
                    }
                else
                    --ports;
                }
            break;
        }
    if (done)
        break;
    }
return ports;
}

/* Open a serial port.

   The serial port designated by "name" is opened, and the handle to the port is
   returned.  If an error occurs, INVALID_HANDLE is returned instead.  After
   opening, the port is configured to "raw" mode.

   Implementation notes:

    1. We use a non-blocking open to allow for polling during reads.

    2. There is no way to limit "open" just to serial ports, so we must check
       after the port is opened.  We do this with sys$getdvi.

*/

static SERHANDLE sim_open_os_serial (char *name)
{
uint32 status;
uint32 chan = 0;
IOSB iosb;
$DESCRIPTOR (devnam, name);
uint32 devclass;
ITEM items[] = { {sizeof (devclass), DVI$_DEVCLASS, &devclass, NULL},
                 {                0,             0,      NULL, NULL}};
SENSE_BUF start_mode = { 0 };
SENSE_BUF run_mode = { 0 };
SERHANDLE port;

devnam.dsc$w_length = strlen (devnam.dsc$a_pointer);
status = sys$assign (&devnam, &chan, 0, 0);
if (status != SS$_NORMAL) 
    return INVALID_HANDLE;
status = sys$getdviw (0, chan, NULL, items, &iosb, NULL, 0, NULL);
if ((status != SS$_NORMAL)      || 
    (iosb.status != SS$_NORMAL) ||
    (devclass != DC$_TERM)) {
    sys$dassgn (chan);
    return INVALID_HANDLE;
    }
status = sys$qiow (0, chan, IO$_SENSEMODE, &iosb, 0, 0,
    &start_mode, sizeof (start_mode), 0, 0, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) {
    sys$dassgn (chan);
    return INVALID_HANDLE;
    }
run_mode = start_mode;
run_mode.stat = start_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC | TT$M_HALFDUP);
run_mode.stat2 = start_mode.stat2 | TT2$M_PASTHRU;
status = sys$qiow (0, chan, IO$_SETMODE, &iosb, 0, 0,
    &run_mode, sizeof (run_mode), 0, 0, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) {
    sys$dassgn (chan);
    return INVALID_HANDLE;
    }
port = (SERHANDLE)calloc (1, sizeof(*port));
port->port = chan;
port->write_iosb.status = 1;
return port;                                            /* return channel for success */
}


/* Configure a serial port.

   Port parameters are configured as specified in the "config" structure.  If
   "config" contains an invalid configuration value, or if the host system
   rejects the configuration (e.g., by requesting an unsupported combination of
   character size and stop bits), SCPE_ARG is returned to the caller.  If an
   unexpected error occurs, SCPE_IOERR is returned.  If the configuration
   succeeds, SCPE_OK is returned.

   Implementation notes:

    1. 1.5 stop bits is not a supported configuration.

*/

static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config)
{
int32 i;
SENSE_BUF sense;
uint32 status, speed, parity, charsize, stopbits;
IOSB iosb;
static const struct {
    uint32  rate;
    uint32  rate_code;
    } baud_map [] =
        { { 50,     TT$C_BAUD_50     }, { 75,     TT$C_BAUD_75     }, { 110,    TT$C_BAUD_110    }, {  134,   TT$C_BAUD_134   },
          { 150,    TT$C_BAUD_150    }, { 300,    TT$C_BAUD_300    }, {  600,   TT$C_BAUD_600    }, {  1200,  TT$C_BAUD_1200  },
          { 1800,   TT$C_BAUD_1800   }, { 2000,   TT$C_BAUD_2000   }, { 2400,   TT$C_BAUD_2400   }, {  3600,  TT$C_BAUD_3600  },
          { 4800,   TT$C_BAUD_4800   }, { 7200,   TT$C_BAUD_7200   }, { 9600,   TT$C_BAUD_9600   }, { 19200,  TT$C_BAUD_19200 },
          { 38400,  TT$C_BAUD_38400  }, { 57600,  TT$C_BAUD_57600  }, { 76800,  TT$C_BAUD_76800  }, { 115200, TT$C_BAUD_115200} };

static const int32 baud_count = sizeof (baud_map) / sizeof (baud_map [0]);

status = sys$qiow (0, port->port, IO$_SENSEMODE, &iosb, 0, 0, &sense, sizeof(sense), 0, NULL, 0, 0);
if (status == SS$_NORMAL)
    status = iosb.status;
if (status != SS$_NORMAL) {
    sim_error_serial ("config-SENSEMODE", status);      /* report unexpected error */
    return SCPE_IOERR;
    }

for (i = 0; i < baud_count; i++)                        /* assign baud rate */
    if (config.baudrate == baud_map [i].rate) {         /* match mapping value? */
        speed = baud_map [i].rate_code << 8 |           /* set input rate */
                baud_map [i].rate_code;                 /* set output rate */
        break;
        }

if (i == baud_count)                                    /* baud rate assigned? */
    return SCPE_ARG;                                    /* invalid rate specified */

if (config.charsize >= 5 && config.charsize <= 8)       /* character size OK? */
    charsize = TT$M_ALTFRAME | config.charsize;         /* set character size */
else
    return SCPE_ARG;                                    /* not a valid size */

switch (config.parity) {                                /* assign parity */
    case 'E':
        parity = TT$M_ALTRPAR | TT$M_PARITY;            /* set for even parity */
        break;

    case 'N':
        parity = TT$M_ALTRPAR;                          /* set for no parity */
        break;

    case 'O':
        parity = TT$M_ALTRPAR | TT$M_PARITY | TT$M_ODD; /* set for odd parity */
        break;

    default:
        return SCPE_ARG;                                /* not a valid parity specifier */
    }


switch (config.stopbits) {
    case 1:                                             /* one stop bit? */
        stopbits = 0;
        break;
    case 2:                                             /* two stop bits? */
        if ((speed & 0xff) <= TT$C_BAUD_150) {          /* Only valid for */
            stopbits = TT$M_TWOSTOP;                    /* speeds 150baud or less */
            break;
            }
    default:
        return SCPE_ARG;                                /* not a valid number of stop bits */
    }

status = sys$qiow (0, port->port, IO$_SETMODE, &iosb, 0, 0,
    &sense, sizeof (sense), speed, 0, parity | charsize | stopbits, 0);
if (status == SS$_NORMAL)
    status = iosb.status;
if (status != SS$_NORMAL) {
    sim_error_serial ("config-SETMODE", status);        /* report unexpected error */
    return SCPE_IOERR;
    }
return SCPE_OK;                                         /* configuration set successfully */
}


/* Control a serial port.

   The DTR and RTS line of the serial port is set or cleared as indicated in 
   the respective bits_to_set or bits_to_clear parameters.  If the 
   incoming_bits parameter is not NULL, then the modem status bits DCD, RNG, 
   DSR and CTS are returned.

   If unreasonable or nonsense bits_to_set or bits_to_clear bits are 
   specified, then the return status is SCPE_ARG;
   If an error occurs, SCPE_IOERR is returned.
*/

t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
{
uint32 status;
IOSB iosb;
uint32 bits[2] = {0, 0};

if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) ||         /* Assure only settable bits */
    (bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
    (bits_to_set & bits_to_clear))                  /* and can't set and clear the same bits */
    return SCPE_ARG;
if (bits_to_set)
    bits[0] |= (((bits_to_set&TMXR_MDM_DTR) ? TT$M_DS_DTR : 0) |
                ((bits_to_set&TMXR_MDM_RTS) ? TT$M_DS_RTS : 0)) << 16;
if (bits_to_clear)
    bits[0] |= (((bits_to_clear&TMXR_MDM_DTR) ? TT$M_DS_DTR : 0) |
                ((bits_to_clear&TMXR_MDM_RTS) ? TT$M_DS_RTS : 0)) << 24;
if (bits_to_set || bits_to_clear) {
    status = sys$qiow (0, port->port, IO$_SETMODE|IO$M_SET_MODEM|IO$M_MAINT, &iosb, 0, 0,
                       bits, 0, 0, 0, 0, 0);
    if (status == SS$_NORMAL)
        status = iosb.status;
    if (status != SS$_NORMAL) {
        sim_error_serial ("control-SETMODE", status);      /* report unexpected error */
        return SCPE_IOERR;
        }
    }
if (incoming_bits) {
    uint32 modem;

    status = sys$qiow (0, port->port, IO$_SENSEMODE|IO$M_RD_MODEM, &iosb, 0, 0,
                       bits, 0, 0, 0, 0, 0);
    if (status == SS$_NORMAL)
        status = iosb.status;
    if (status != SS$_NORMAL) {
        sim_error_serial ("control-SENSEMODE", status);      /* report unexpected error */
        return SCPE_IOERR;
        }
    modem = bits[0] >> 16;
    *incoming_bits = ((modem&TT$M_DS_CTS)     ? TMXR_MDM_CTS : 0) |
                     ((modem&TT$M_DS_DSR)     ? TMXR_MDM_DSR : 0) |
                     ((modem&TT$M_DS_RING)    ? TMXR_MDM_RNG : 0) |
                     ((modem&TT$M_DS_CARRIER) ? TMXR_MDM_DCD : 0);
    }

return SCPE_OK;
}


/* Read from a serial port.

   The port is checked for available characters.  If any are present, they are
   copied to the passed buffer, and the count of characters is returned.  If no
   characters are available, 0 is returned.  If an error occurs, -1 is returned.
   If a BREAK is detected on the communications line, the corresponding flag in
   the "brk" array is set.

   Implementation notes:

    1. A character with a framing or parity error is indicated in the input
       stream by the three-character sequence \377 \000 \ccc, where "ccc" is the
       bad character.  A communications line BREAK is indicated by the sequence
       \377 \000 \000.  A received \377 character is indicated by the
       two-character sequence \377 \377.  If we find any of these sequences,
       they are replaced by the single intended character by sliding the
       succeeding characters backward by one or two positions.  If a BREAK
       sequence was encountered, the corresponding location in the "brk" array
       is determined, and the flag is set.  Note that there may be multiple
       sequences in the buffer.
*/

int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
{
int read_count = 0;
uint32 status;
static uint32 term[2] = {0, 0};
unsigned char buf[4];
IOSB iosb;
SENSE_BUF sense;

status = sys$qiow (0, port->port, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb,
    0, 0, &sense, 8, 0, term, 0, 0);
if (status == SS$_NORMAL)
    status = iosb.status;
if (status != SS$_NORMAL) {
    sim_error_serial ("read", status);                      /* report unexpected error */
    return -1;
    }
if (sense.sense_count == 0)                                 /* no characters available? */
    return 0;                                               /* return 0 to indicate */
status = sys$qiow (0, port->port, IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, 
                   &iosb, 0, 0, buffer, (count < sense.sense_count) ? count : sense.sense_count, 0, term, 0, 0);
if (status == SS$_NORMAL)
    status = iosb.status;
if (status != SS$_NORMAL) {
    sim_error_serial ("read", status);                      /* report unexpected error */
    return -1;
    }
return (int32)iosb.count;                                   /* return the number of characters read */
}


/* Write to a serial port.

   "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)
{
uint32 status;

if (port->write_iosb.status == 0)           /* Prior write not done yet? */
    return 0;
status = sys$qio (0, port->port, IO$_WRITELBLK | IO$M_NOFORMAT,
                  &port->write_iosb, 0, 0, buffer, count, 0, 0, 0, 0);
if (status != SS$_NORMAL) {
    sim_error_serial ("write", status);                 /* report unexpected error */
    return -1;
    }
return (int32)count;                                    /* return number of characters written */
}


/* Close a serial port.

   The serial port is closed.  Errors are ignored.
*/

static void sim_close_os_serial (SERHANDLE port)
{
sys$dassgn (port->port);                                /* close the port */
free (port);
}

#else

/* Non-implemented stubs */

/* Enumerate the available serial ports. */

static int sim_serial_os_devices (int max, SERIAL_LIST* list)
{
return 0;
}

/* Open a serial port */

static SERHANDLE sim_open_os_serial (char *name)
{
return INVALID_HANDLE;
}


/* Configure a serial port */

static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config)
{
return SCPE_IERR;
}


/* Control a serial port */

t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
{
return SCPE_NOFNC;
}


/* Read from a serial port */

int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
{
return -1;
}


/* Write to a serial port */

int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
{
return -1;
}


/* Close a serial port */

static void sim_close_os_serial (SERHANDLE port)
{
}



#endif                                                  /* end else !implemented */
Added src/sim_serial.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_serial.h: OS-dependent serial port routines header file

   Copyright (c) 2008, J. David Bryan

   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 AUTHOR 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 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
*/


#ifndef SIM_SERIAL_H_
#define SIM_SERIAL_H_    0

#ifdef  __cplusplus
extern "C" {
#endif

#ifndef SIMH_SERHANDLE_DEFINED
#define SIMH_SERHANDLE_DEFINED 0
typedef struct SERPORT *SERHANDLE;
#endif /* SERHANDLE_DEFINED */

#if defined (_WIN32)                        /* Windows definitions */

/* We need the basic Win32 definitions, but including "windows.h" also includes
   "winsock.h" as well.  However, "sim_sock.h" explicitly includes "winsock2.h,"
   and this file cannot coexist with "winsock.h".  So we set a guard definition
   that prevents "winsock.h" from being included.
*/

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#if !defined(INVALID_HANDLE)
#define INVALID_HANDLE  (SERHANDLE)INVALID_HANDLE_VALUE
#endif /* !defined(INVALID_HANDLE) */

#elif defined (__unix__) || defined (__APPLE__) || defined (__hpux) /* UNIX definitions */

#include <fcntl.h>
#ifdef __hpux
#include <sys/modem.h>
#endif
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>

#if !defined(INVALID_HANDLE)
#define INVALID_HANDLE  ((SERHANDLE)(void *)-1)
#endif /* !defined(INVALID_HANDLE) */

#elif defined (VMS)                             /* VMS definitions */
#if !defined(INVALID_HANDLE)
#define INVALID_HANDLE  ((SERHANDLE)(void *)-1)
#endif /* !defined(INVALID_HANDLE) */

#else                                           /* Non-implemented definitions */

#if !defined(INVALID_HANDLE)
#define INVALID_HANDLE  ((SERHANDLE)(void *)-1)
#endif /* !defined(INVALID_HANDLE) */

#endif  /* OS variants */


/* Common definitions */

/* Global routines */
#include "sim_tmxr.h"                           /* need TMLN definition and modem definitions */

extern SERHANDLE sim_open_serial    (char *name, TMLN *lp, t_stat *status);
extern t_stat    sim_config_serial  (SERHANDLE port, CONST char *config);
extern t_stat    sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits);
extern int32     sim_read_serial    (SERHANDLE port, char *buffer, int32 count, char *brk);
extern int32     sim_write_serial   (SERHANDLE port, char *buffer, int32 count);
extern void      sim_close_serial   (SERHANDLE port);
extern t_stat    sim_show_serial    (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc);

#ifdef  __cplusplus
}
#endif

#endif
Added src/sim_sock.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_sock.c: OS-dependent socket routines

   Copyright (c) 2001-2010, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   15-Oct-12    MP      Added definitions needed to detect possible tcp 
                        connect failures
   25-Sep-12    MP      Reworked for RFC3493 interfaces supporting IPv6 and IPv4
   22-Jun-10    RMS     Fixed types in sim_accept_conn (from Mark Pizzolato)
   19-Nov-05    RMS     Added conditional for OpenBSD (from Federico G. Schwindt)
   16-Aug-05    RMS     Fixed spurious SIGPIPE signal error in Unix
   14-Apr-05    RMS     Added WSAEINPROGRESS test (from Tim Riker)
   09-Jan-04    RMS     Fixed typing problem in Alpha Unix (found by Tim Chapman)
   17-Apr-03    RMS     Fixed non-implemented version of sim_close_sock
                        (found by Mark Pizzolato)
   17-Dec-02    RMS     Added sim_connect_socket, sim_create_socket
   08-Oct-02    RMS     Revised for .NET compatibility
   22-Aug-02    RMS     Changed calling sequence for sim_accept_conn
   22-May-02    RMS     Added OS2 EMX support from Holger Veit
   06-Feb-02    RMS     Added VMS support from Robert Alan Byer
   16-Sep-01    RMS     Added Macintosh support from Peter Schorn
   02-Sep-01    RMS     Fixed UNIX bugs found by Mirian Lennox and Tom Markson
*/

#ifdef  __cplusplus
extern "C" {
#endif

#include "sim_sock.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

#if defined(AF_INET6) && defined(_WIN32)
#include <ws2tcpip.h>
#endif

#ifdef HAVE_DLOPEN
#include <dlfcn.h>
#endif

#ifndef WSAAPI
#define WSAAPI
#endif

#if defined(SHUT_RDWR) && !defined(SD_BOTH)
#define SD_BOTH SHUT_RDWR
#endif

#ifndef   NI_MAXHOST
#define   NI_MAXHOST 1025
#endif

/* OS dependent routines

   sim_master_sock      create master socket
   sim_connect_sock     connect a socket to a remote destination
   sim_connect_sock_ex  connect a socket to a remote destination
   sim_accept_conn      accept connection
   sim_read_sock        read from socket
   sim_write_sock       write from socket
   sim_close_sock       close socket
   sim_setnonblock      set socket non-blocking
*/

/* First, all the non-implemented versions */

#if defined (__OS2__) && !defined (__EMX__)

void sim_init_sock (void)
{
}

void sim_cleanup_sock (void)
{
}

SOCKET sim_master_sock_ex (const char *hostport, int *parse_status, int opt_flags)
{
return INVALID_SOCKET;
}

SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, int opt_flags)
{
return INVALID_SOCKET;
}

SOCKET sim_accept_conn (SOCKET master, char **connectaddr);
{
return INVALID_SOCKET;
}

int sim_read_sock (SOCKET sock, char *buf, int nbytes)
{
return -1;
}

int sim_write_sock (SOCKET sock, char *msg, int nbytes)
{
return 0;
}

void sim_close_sock (SOCKET sock)
{
return;
}

#else                                                   /* endif unimpl */

/* UNIX, Win32, Macintosh, VMS, OS2 (Berkeley socket) routines */

static struct sock_errors {
    int value;
    const char *text;
    } sock_errors[] = {
        {WSAEWOULDBLOCK,  "Operation would block"},
        {WSAENAMETOOLONG, "File name too long"},
        {WSAEINPROGRESS,  "Operation now in progress "},
        {WSAETIMEDOUT,    "Connection timed out"},
        {WSAEISCONN,      "Transport endpoint is already connected"},
        {WSAECONNRESET,   "Connection reset by peer"},
        {WSAECONNREFUSED, "Connection refused"},
        {WSAECONNABORTED, "Connection aborted"},
        {WSAEHOSTUNREACH, "No route to host"},
        {WSAEADDRINUSE,   "Address already in use"},
#if defined (WSAEAFNOSUPPORT)
        {WSAEAFNOSUPPORT, "Address family not supported by protocol"},
#endif
        {WSAEACCES,       "Permission denied"},
        {0, NULL}
    };


const char *sim_get_err_sock (const char *emsg)
{
int err = WSAGetLastError ();
int i;
static char err_buf[512];

for (i=0; (sock_errors[i].text) && (sock_errors[i].value != err); i++)
    ;
if (sock_errors[i].value == err)
    sprintf (err_buf, "Sockets: %s error %d - %s\n", emsg, err, sock_errors[i].text);
else
#if defined(_WIN32)
    sprintf (err_buf, "Sockets: %s error %d\n", emsg, err);
#else
    sprintf (err_buf, "Sockets: %s error %d - %s\n", emsg, err, strerror(err));
#endif
return err_buf;
}

SOCKET sim_err_sock (SOCKET s, const char *emsg)
{
sim_printf ("%s", sim_get_err_sock (emsg));
if (s != INVALID_SOCKET) {
    int err = WSAGetLastError ();
    sim_close_sock (s);
    WSASetLastError (err);      /* Retain Original socket error value */
    }
return INVALID_SOCKET;
}

typedef void    (WSAAPI *freeaddrinfo_func) (struct addrinfo *ai);
static freeaddrinfo_func p_freeaddrinfo;

typedef int     (WSAAPI *getaddrinfo_func) (const char *hostname,
                                 const char *service,
                                 const struct addrinfo *hints,
                                 struct addrinfo **res);
static getaddrinfo_func p_getaddrinfo;

#if defined(VMS)
typedef size_t socklen_t;
#if !defined(EAI_OVERFLOW)
#define EAI_OVERFLOW EAI_FAIL
#endif
#endif

#if defined(__hpux)
#if !defined(EAI_OVERFLOW)
#define EAI_OVERFLOW EAI_FAIL
#endif
#endif

typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags);
static getnameinfo_func p_getnameinfo;

static void    WSAAPI s_freeaddrinfo (struct addrinfo *ai)
{
struct addrinfo *a, *an;

for (a=ai; a != NULL; a=an) {
    an = a->ai_next;
    free (a->ai_canonname);
    free (a->ai_addr);
    free (a);
    }
}

static int     WSAAPI s_getaddrinfo (const char *hostname,
                                     const char *service,
                                     const struct addrinfo *hints,
                                     struct addrinfo **res)
{
struct hostent *he;
struct servent *se = NULL;
struct sockaddr_in *sin;
struct addrinfo *result = NULL;
struct addrinfo *ai, *lai = NULL;
struct addrinfo dhints;
struct in_addr ipaddr;
struct in_addr *fixed[2];
struct in_addr **ips = NULL;
struct in_addr **ip;
const char *cname = NULL;
int port = 0;

// Validate parameters
if ((hostname == NULL) && (service == NULL))
    return EAI_NONAME;

if (hints) {
    if ((hints->ai_family != PF_INET) && (hints->ai_family != PF_UNSPEC))
        return EAI_FAMILY;
    switch (hints->ai_socktype)
        {
        default:
            return EAI_SOCKTYPE;
        case SOCK_DGRAM:
        case SOCK_STREAM:
        case 0:
            break;
        }
    }
else {
    hints = &dhints;
    memset(&dhints, 0, sizeof(dhints));
    dhints.ai_family = PF_UNSPEC;
    }
if (service) {
    char *c;

    port = strtoul(service, &c, 10);
    if ((port == 0) || (*c != '\0')) {
        switch (hints->ai_socktype)
            {
            case SOCK_DGRAM:
                se = getservbyname(service, "udp");
                break;
            case SOCK_STREAM:
            case 0:
                se = getservbyname(service, "tcp");
                break;
            }
        if (NULL == se)
            return EAI_SERVICE;
        port = se->s_port;
        }
    }

if (hostname) {
    if ((0xffffffff != (ipaddr.s_addr = inet_addr(hostname))) || 
        (0 == strcmp("255.255.255.255", hostname))) {
        fixed[0] = &ipaddr;
        fixed[1] = NULL;
        }
    else {
        if ((0xffffffff != (ipaddr.s_addr = inet_addr(hostname))) || 
            (0 == strcmp("255.255.255.255", hostname))) {
            fixed[0] = &ipaddr;
            fixed[1] = NULL;
            if ((hints->ai_flags & AI_CANONNAME) && !(hints->ai_flags & AI_NUMERICHOST)) {
                he = gethostbyaddr((char *)&ipaddr, 4, AF_INET);
                if (NULL != he)
                    cname = he->h_name;
                else
                    cname = hostname;
                }
            ips = fixed;
            }
        else {
            if (hints->ai_flags & AI_NUMERICHOST)
                return EAI_NONAME;
            he = gethostbyname(hostname);
            if (he) {
                ips = (struct in_addr **)he->h_addr_list;
                if (hints->ai_flags & AI_CANONNAME)
                    cname = he->h_name;
                }
            else {
                switch (h_errno)
                    {
                    case HOST_NOT_FOUND:
                    case NO_DATA:
                        return EAI_NONAME;
                    case TRY_AGAIN:
                        return EAI_AGAIN;
                    default:
                        return EAI_FAIL;
                    }
                }
            }
        }
    }
else {
    if (hints->ai_flags & AI_PASSIVE)
        ipaddr.s_addr = htonl(INADDR_ANY);
    else
        ipaddr.s_addr = htonl(INADDR_LOOPBACK);
    fixed[0] = &ipaddr;
    fixed[1] = NULL;
    ips = fixed;
    }
for (ip=ips; (ip != NULL) && (*ip != NULL); ++ip) {
    ai = (struct addrinfo *)calloc(1, sizeof(*ai));
    if (NULL == ai) {
        s_freeaddrinfo(result);
        return EAI_MEMORY;
        }
    ai->ai_family = PF_INET;
    ai->ai_socktype = hints->ai_socktype;
    ai->ai_protocol = hints->ai_protocol;
    ai->ai_addr = NULL;
    ai->ai_addrlen = sizeof(struct sockaddr_in);
    ai->ai_canonname = NULL;
    ai->ai_next = NULL;
    ai->ai_addr = (struct sockaddr *)calloc(1, sizeof(struct sockaddr_in));
    if (NULL == ai->ai_addr) {
        free(ai);
        s_freeaddrinfo(result);
        return EAI_MEMORY;
        }
    sin = (struct sockaddr_in *)ai->ai_addr;
    sin->sin_family = PF_INET;
    sin->sin_port = (unsigned short)port;
    memcpy(&sin->sin_addr, *ip, sizeof(sin->sin_addr));
    if (NULL == result)
        result = ai;
    else
        lai->ai_next = ai;
    lai = ai;
    }
if (cname) {
    result->ai_canonname = (char *)calloc(1, strlen(cname)+1);
    if (NULL == result->ai_canonname) {
        s_freeaddrinfo(result);
        return EAI_MEMORY;
        }
    strcpy(result->ai_canonname, cname);
    }
*res = result;
return 0;
}

#ifndef EAI_OVERFLOW
#define EAI_OVERFLOW WSAENAMETOOLONG
#endif

static int     WSAAPI s_getnameinfo (const struct sockaddr *sa, socklen_t salen,
                                     char *host, size_t hostlen,
                                     char *serv, size_t servlen,
                                     int flags)
{
struct hostent *he;
struct servent *se = NULL;
const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;

if (sin->sin_family != PF_INET)
    return EAI_FAMILY;
if ((NULL == host) && (NULL == serv))
    return EAI_NONAME;
if ((serv) && (servlen > 0)) {
    if (flags & NI_NUMERICSERV)
        se = NULL;
    else
        if (flags & NI_DGRAM)
            se = getservbyport(sin->sin_port, "udp");
        else
            se = getservbyport(sin->sin_port, "tcp");
    if (se) {
        if (servlen <= strlen(se->s_name))
            return EAI_OVERFLOW;
        strcpy(serv, se->s_name);
        }
    else {
        char buf[16];

        sprintf(buf, "%d", ntohs(sin->sin_port));
        if (servlen <= strlen(buf))
            return EAI_OVERFLOW;
        strcpy(serv, buf);
        }
    }
if ((host) && (hostlen > 0)) {
    if (flags & NI_NUMERICHOST)
        he = NULL;
    else
        he = gethostbyaddr((const char *)&sin->sin_addr, 4, AF_INET);
    if (he) {
        if (hostlen < strlen(he->h_name)+1)
            return EAI_OVERFLOW;
        strcpy(host, he->h_name);
        }
    else {
        if (flags & NI_NAMEREQD)
            return EAI_NONAME;
        if (hostlen < strlen(inet_ntoa(sin->sin_addr))+1)
            return EAI_OVERFLOW;
        strcpy(host, inet_ntoa(sin->sin_addr));
        }
    }
return 0;
}

#if defined(_WIN32) || defined(__CYGWIN__)

#if !defined(IPV6_V6ONLY)           /* Older XP environments may not define IPV6_V6ONLY */
#define IPV6_V6ONLY           27    /* Treat wildcard bind as AF_INET6-only. */
#endif
/* Dynamic DLL load variables */
#ifdef _WIN32
static HINSTANCE hLib = 0;                      /* handle to DLL */
#else
static void *hLib = NULL;                       /* handle to Library */
#endif
static int lib_loaded = 0;                      /* 0=not loaded, 1=loaded, 2=library load failed, 3=Func load failed */
static const char* lib_name = "Ws2_32.dll";

/* load function pointer from DLL */
typedef int (*_func)();

static void load_function(const char* function, _func* func_ptr) {
#ifdef _WIN32
    *func_ptr = (_func)GetProcAddress(hLib, function);
#else
    *func_ptr = (_func)dlsym(hLib, function);
#endif
    if (*func_ptr == 0) {
    sim_printf ("Sockets: Failed to find function '%s' in %s\r\n", function, lib_name);
    lib_loaded = 3;
  }
}

/* load Ws2_32.dll as required */
int load_ws2(void) {
  switch(lib_loaded) {
    case 0:                  /* not loaded */
            /* attempt to load DLL */
#ifdef _WIN32
      hLib = LoadLibraryA(lib_name);
#else
      hLib = dlopen(lib_name, RTLD_NOW);
#endif
      if (hLib == 0) {
        /* failed to load DLL */
        sim_printf ("Sockets: Failed to load %s\r\n", lib_name);
        lib_loaded = 2;
        break;
      } else {
        /* library loaded OK */
        lib_loaded = 1;
      }

      /* load required functions; sets dll_load=3 on error */
      load_function("getaddrinfo",       (_func *) &p_getaddrinfo);
      load_function("getnameinfo",       (_func *) &p_getnameinfo);
      load_function("freeaddrinfo",      (_func *) &p_freeaddrinfo);

      if (lib_loaded != 1) {
        /* unsuccessful load, connect stubs */
        p_getaddrinfo = (getaddrinfo_func)s_getaddrinfo;
        p_getnameinfo = (getnameinfo_func)s_getnameinfo;
        p_freeaddrinfo = (freeaddrinfo_func)s_freeaddrinfo;
      }
      break;
    default:                /* loaded or failed */
      break;
  }
  return (lib_loaded == 1) ? 1 : 0;
}
#endif

/* OS independent routines

   sim_parse_addr       parse a hostname/ipaddress from port and apply defaults and 
                        optionally validate an address match
*/

/* sim_parse_addr       host:port

   Presumption is that the input, if it doesn't contain a ':' character is a port specifier.
   If the host field contains one or more colon characters (i.e. it is an IPv6 address), 
   the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format)

   Inputs:
        cptr    =       pointer to input string
        default_host
                =       optional pointer to default host if none specified
        host_len =      length of host buffer
        default_port
                =       optional pointer to default port if none specified
        port_len =      length of port buffer
        validate_addr = optional name/addr which is checked to be equivalent
                        to the host result of parsing the other input.  This
                        address would usually be returned by sim_accept_conn.
   Outputs:
        host    =       pointer to buffer for IP address (may be NULL), 0 = none
        port    =       pointer to buffer for IP port (may be NULL), 0 = none
        result  =       status (0 on complete success or -1 if 
                        parsing can't happen due to bad syntax, a value is 
                        out of range, a result can't fit into a result buffer, 
                        a service name doesn't exist, or a validation name 
                        doesn't match the parsed host)
*/

int sim_parse_addr (const char *cptr, char *host, size_t host_len, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr)
{
char gbuf[CBUFSIZE], default_pbuf[CBUFSIZE];
const char *hostp;
char *portp;
char *endc;
unsigned long portval;

if ((host != NULL) && (host_len != 0))
    memset (host, 0, host_len);
if ((port != NULL) && (port_len != 0))
    memset (port, 0, port_len);
if ((cptr == NULL) || (*cptr == 0)) {
    if (((default_host == NULL) || (*default_host == 0)) || ((default_port == NULL) || (*default_port == 0)))
        return -1;
    if ((host == NULL) || (port == NULL))
        return -1;                                  /* no place */
    if ((strlen(default_host) >= host_len) || (strlen(default_port) >= port_len))
        return -1;                                  /* no room */
    strcpy (host, default_host);
    strcpy (port, default_port);
    return 0;
    }
memset (default_pbuf, 0, sizeof(default_pbuf));
if (default_port)
    strncpy (default_pbuf, default_port, sizeof(default_pbuf)-1);
gbuf[sizeof(gbuf)-1] = '\0';
strncpy (gbuf, cptr, sizeof(gbuf)-1);
hostp = gbuf;                                           /* default addr */
portp = NULL;
if ((portp = strrchr (gbuf, ':')) &&                    /* x:y? split */
    (NULL == strchr (portp, ']'))) {
    *portp++ = 0;
    if (*portp == '\0')
        portp = default_pbuf;
    }
else {                                                  /* No colon in input */
    portp = gbuf;                                       /* Input is the port specifier */
    hostp = (const char *)default_host;                 /* host is defaulted if provided */
    }
if (portp != NULL) {
    portval = strtoul(portp, &endc, 10);
    if ((*endc == '\0') && ((portval == 0) || (portval > 65535)))
        return -1;                                      /* numeric value too big */
    if (*endc != '\0') {
        struct servent *se = getservbyname(portp, "tcp");

        if (se == NULL)
            return -1;                                  /* invalid service name */
        }
    }
if (port)                                               /* port wanted? */
    if (portp != NULL) {
        if (strlen(portp) >= port_len)
            return -1;                                  /* no room */
        else
            strcpy (port, portp);
        }
if (hostp != NULL) {
    if (']' == hostp[strlen(hostp)-1]) {
        if ('[' != hostp[0])
            return -1;                                  /* invalid domain literal */
        /* host may be the const default_host so move to temp buffer before modifying */
        strncpy(gbuf, hostp+1, sizeof(gbuf)-1);         /* remove brackets from domain literal host */
        gbuf[strlen(gbuf)-1] = '\0';
        hostp = gbuf;
        }
    }
if (host) {                                             /* host wanted? */
    if (hostp != NULL) {
        if (strlen(hostp) >= host_len)
            return -1;                                  /* no room */
        else
            if (('\0' != hostp[0]) || (default_host == NULL))
                strcpy (host, hostp);
            else
                if (strlen(default_host) >= host_len)
                    return -1;                          /* no room */
                else
                    strcpy (host, default_host);
        }
    else {
        if (default_host) {
            if (strlen(default_host) >= host_len)
                return -1;                              /* no room */
            else
                strcpy (host, default_host);
            }
        }
    }
if (validate_addr) {
    struct addrinfo *ai_host, *ai_validate, *ai, *aiv;
    int status;

    if (hostp == NULL)
        return -1;
    if (p_getaddrinfo(hostp, NULL, NULL, &ai_host))
        return -1;
    if (p_getaddrinfo(validate_addr, NULL, NULL, &ai_validate)) {
        p_freeaddrinfo (ai_host);
        return -1;
        }
    status = -1;
    for (ai = ai_host; ai != NULL; ai = ai->ai_next) {
        for (aiv = ai_validate; aiv != NULL; aiv = aiv->ai_next) {
            if ((ai->ai_addrlen == aiv->ai_addrlen) &&
                (ai->ai_family == aiv->ai_family) &&
                (0 == memcmp (ai->ai_addr, aiv->ai_addr, ai->ai_addrlen))) {
                status = 0;
                break;
                }
            }
        }
    if (status != 0) {
        /* be generous and allow successful validations against variations of localhost addresses */
        if (((0 == strcmp("127.0.0.1", hostp)) && 
             (0 == strcmp("::1", validate_addr))) ||
            ((0 == strcmp("127.0.0.1", validate_addr)) && 
             (0 == strcmp("::1", hostp))))
            status = 0;
        }
    p_freeaddrinfo (ai_host);
    p_freeaddrinfo (ai_validate);
    return status;
    }
return 0;
}

/* sim_parse_addr_ex    localport:host:port

   Presumption is that the input, if it doesn't contain a ':' character is a port specifier.
   If the host field contains one or more colon characters (i.e. it is an IPv6 address), 
   the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format)

        llll:w.x.y.z:rrrr
        llll:name.domain.com:rrrr
        llll::rrrr
        rrrr
        w.x.y.z:rrrr
        [w.x.y.z]:rrrr
        name.domain.com:rrrr

   Inputs:
        cptr    =       pointer to input string
        default_host
                =       optional pointer to default host if none specified
        host_len =      length of host buffer
        default_port
                =       optional pointer to default port if none specified
        port_len =      length of port buffer

   Outputs:
        host    =       pointer to buffer for IP address (may be NULL), 0 = none
        port    =       pointer to buffer for IP port (may be NULL), 0 = none
        localport
                =       pointer to buffer for local IP port (may be NULL), 0 = none
        result  =       status (SCPE_OK on complete success or SCPE_ARG if 
                        parsing can't happen due to bad syntax, a value is 
                        out of range, a result can't fit into a result buffer, 
                        a service name doesn't exist, or a validation name 
                        doesn't match the parsed host)
*/
int sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, char *localport, size_t localport_len, const char *default_port)
{
const char *hostp;

if ((localport != NULL) && (localport_len != 0))
    memset (localport, 0, localport_len);
hostp = strchr (cptr, ':');
if ((hostp != NULL) && ((hostp[1] == '[') || (NULL != strchr (hostp+1, ':')))) {
    if ((localport != NULL) && (localport_len != 0)) {
        localport_len -= 1;
        if (localport_len > (size_t)(hostp-cptr))
            localport_len = (size_t)(hostp-cptr);
        memcpy (localport, cptr, localport_len);
        }
    return sim_parse_addr (hostp+1, host, hostlen, default_host, port, port_len, default_port, NULL);
    }
return sim_parse_addr (cptr, host, hostlen, default_host, port, port_len, default_port, NULL);
}


void sim_init_sock (void)
{
#if defined (_WIN32)
int err;
WORD wVersionRequested; 
WSADATA wsaData; 
wVersionRequested = MAKEWORD (2, 2);

err = WSAStartup (wVersionRequested, &wsaData);         /* start Winsock */ 
if (err != 0)
    sim_printf ("Winsock: startup error %d\n", err);
#if defined(AF_INET6)
load_ws2 ();
#endif                                                  /* endif AF_INET6 */
#else                                                   /* Use native addrinfo APIs */
#if defined(AF_INET6)
    p_getaddrinfo = (getaddrinfo_func)getaddrinfo;
    p_getnameinfo = (getnameinfo_func)getnameinfo;
    p_freeaddrinfo = (freeaddrinfo_func)freeaddrinfo;
#else
    /* Native APIs not available, connect stubs */
    p_getaddrinfo = (getaddrinfo_func)s_getaddrinfo;
    p_getnameinfo = (getnameinfo_func)s_getnameinfo;
    p_freeaddrinfo = (freeaddrinfo_func)s_freeaddrinfo;
#endif                                                  /* endif AF_INET6 */
#endif                                                  /* endif _WIN32 */
#if defined (SIGPIPE)
signal (SIGPIPE, SIG_IGN);                              /* no pipe signals */
#endif
}

void sim_cleanup_sock (void)
{
#if defined (_WIN32)
WSACleanup ();
#endif
}

#if defined (_WIN32)                                    /* Windows */
static int sim_setnonblock (SOCKET sock)
{
unsigned long non_block = 1;

return ioctlsocket (sock, FIONBIO, &non_block);         /* set nonblocking */
}

#elif defined (VMS)                                     /* VMS */
static int sim_setnonblock (SOCKET sock)
{
int non_block = 1;

return ioctl (sock, FIONBIO, &non_block);               /* set nonblocking */
}

#else                                                   /* Mac, Unix, OS/2 */
static int sim_setnonblock (SOCKET sock)
{
int fl, sta;

fl = fcntl (sock, F_GETFL,0);                           /* get flags */
if (fl == -1)
    return SOCKET_ERROR;
sta = fcntl (sock, F_SETFL, fl | O_NONBLOCK);           /* set nonblock */
if (sta == -1)
    return SOCKET_ERROR;
#if !defined (macintosh) && !defined (__EMX__) && \
    !defined (__HAIKU__)                                /* Unix only */
sta = fcntl (sock, F_SETOWN, getpid());                 /* set ownership */
if (sta == -1)
    return SOCKET_ERROR;
#endif
return 0;
}

#endif                                                  /* endif !Win32 && !VMS */

static int sim_setnodelay (SOCKET sock)
{
int nodelay = 1;
int sta;

/* disable Nagle algorithm */
sta = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay));
if (sta == -1)
    return SOCKET_ERROR;

#if defined(TCP_NODELAYACK)
/* disable delayed ack algorithm */
sta = setsockopt (sock, IPPROTO_TCP, TCP_NODELAYACK, (char *)&nodelay, sizeof(nodelay));
if (sta == -1)
    return SOCKET_ERROR;
#endif

#if defined(TCP_QUICKACK)
/* disable delayed ack algorithm */
sta = setsockopt (sock, IPPROTO_TCP, TCP_QUICKACK, (char *)&nodelay, sizeof(nodelay));
if (sta == -1)
    return SOCKET_ERROR;
#endif

return sta;
}

static SOCKET sim_create_sock (int af, int opt_flags)
{
SOCKET newsock;
int err;

newsock = socket (af, ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? SOCK_DGRAM : SOCK_STREAM), 0);/* create socket */
if (newsock == INVALID_SOCKET) {                        /* socket error? */
    err = WSAGetLastError ();
#if defined(WSAEAFNOSUPPORT)
    if (err == WSAEAFNOSUPPORT)                         /* expected error, just return */
        return newsock;
#endif
    return sim_err_sock (newsock, "socket");            /* report error and return */
    }
return newsock;
}

/*
   Some platforms and/or network stacks have varying support for listening on 
   an IPv6 socket and receiving connections from both IPv4 and IPv6 client 
   connections.  This is known as IPv4-Mapped.  Some platforms claim such 
   support (i.e. some Windows versions), but it doesn't work in all cases.
*/

SOCKET sim_master_sock_ex (const char *hostport, int *parse_status, int opt_flags)
{
SOCKET newsock = INVALID_SOCKET;
int sta;
char host[CBUFSIZE], port[CBUFSIZE];
int r;
struct addrinfo hints;
struct addrinfo *result = NULL, *preferred;

r = sim_parse_addr (hostport, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL);
if (parse_status)
    *parse_status = r;
if (r)
    return newsock;

memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &result)) {
    if (parse_status)
        *parse_status = -1;
    return newsock;
    }
preferred = result;
#ifdef IPV6_V6ONLY
/*
    When we can create a dual stack socket, be sure to find the IPv6 addrinfo 
    to bind to.
*/
for (; preferred != NULL; preferred = preferred->ai_next) {
    if (preferred->ai_family == AF_INET6)
        break;
    }
if (preferred == NULL)
    preferred = result;
#endif
retry:
newsock = sim_create_sock (preferred->ai_family, 0);    /* create socket */
if (newsock == INVALID_SOCKET) {                        /* socket error? */
#ifndef IPV6_V6ONLY
    if (preferred->ai_next) {
        preferred = preferred->ai_next;
        goto retry;
        }
#else
    if ((preferred->ai_family == AF_INET6) &&
        (preferred != result)) {
        preferred = result;
        goto retry;
        }
#endif
    p_freeaddrinfo(result);
    return newsock;
    }
#ifdef IPV6_V6ONLY
if (preferred->ai_family == AF_INET6) {
    int off = 0;
    sta = setsockopt (newsock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&off, sizeof(off));
    }
#endif
if (opt_flags & SIM_SOCK_OPT_REUSEADDR) {
    int on = 1;

    sta = setsockopt (newsock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
    }
#if defined (SO_EXCLUSIVEADDRUSE)
else {
    int on = 1;

    sta = setsockopt (newsock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&on, sizeof(on));
    }
#endif
sta = bind (newsock, preferred->ai_addr, preferred->ai_addrlen);
p_freeaddrinfo(result);
if (sta == SOCKET_ERROR)                                /* bind error? */
    return sim_err_sock (newsock, "bind");
if (!(opt_flags & SIM_SOCK_OPT_BLOCKING)) {
    sta = sim_setnonblock (newsock);                    /* set nonblocking */
    if (sta == SOCKET_ERROR)                            /* fcntl error? */
        return sim_err_sock (newsock, "fcntl");
    }
sta = listen (newsock, 1);                              /* listen on socket */
if (sta == SOCKET_ERROR)                                /* listen error? */
    return sim_err_sock (newsock, "listen");
return newsock;                                         /* got it! */
}

SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, int opt_flags)
{
SOCKET newsock = INVALID_SOCKET;
int sta;
char host[CBUFSIZE], port[CBUFSIZE];
struct addrinfo hints;
struct addrinfo *result = NULL, *source = NULL;

if (sim_parse_addr (hostport, host, sizeof(host), default_host, port, sizeof(port), default_port, NULL))
    return INVALID_SOCKET;

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_protocol = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? IPPROTO_UDP : IPPROTO_TCP);
hints.ai_socktype = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? SOCK_DGRAM : SOCK_STREAM);
if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &result))
    return INVALID_SOCKET;

if (sourcehostport) {

    /* Validate the local/source side address which we'll bind to */
    if (sim_parse_addr (sourcehostport, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL)) {
        p_freeaddrinfo (result);
        return INVALID_SOCKET;
        }

    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = result->ai_family;                /* Same family as connect destination */
    hints.ai_protocol = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? IPPROTO_UDP : IPPROTO_TCP);
    hints.ai_socktype = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? SOCK_DGRAM : SOCK_STREAM);
    if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &source)) {
        p_freeaddrinfo (result);
        return INVALID_SOCKET;
        }

    newsock = sim_create_sock (result->ai_family, opt_flags & SIM_SOCK_OPT_DATAGRAM);/* create socket */
    if (newsock == INVALID_SOCKET) {                    /* socket error? */
        p_freeaddrinfo (result);
        p_freeaddrinfo (source);
        return newsock;
        }

    sta = bind (newsock, source->ai_addr, source->ai_addrlen);
    p_freeaddrinfo(source);
    source = NULL;
    if (sta == SOCKET_ERROR) {                          /* bind error? */
        p_freeaddrinfo (result);
        return sim_err_sock (newsock, "bind");
        }
    }

if (newsock == INVALID_SOCKET) {                        /* socket error? */
    newsock = sim_create_sock (result->ai_family, opt_flags & SIM_SOCK_OPT_DATAGRAM);/* create socket */
    if (newsock == INVALID_SOCKET) {                    /* socket error? */
        p_freeaddrinfo (result);
        return newsock;
        }
    }

if (!(opt_flags & SIM_SOCK_OPT_BLOCKING)) {
    sta = sim_setnonblock (newsock);                    /* set nonblocking */
    if (sta == SOCKET_ERROR) {                          /* fcntl error? */
        p_freeaddrinfo (result);
        return sim_err_sock (newsock, "fcntl");
        }
    }
if ((!(opt_flags & SIM_SOCK_OPT_DATAGRAM)) && (opt_flags & SIM_SOCK_OPT_NODELAY)) {
    sta = sim_setnodelay (newsock);                     /* set nodelay */
    if (sta == SOCKET_ERROR) {                          /* setsock error? */
        p_freeaddrinfo (result);
        return sim_err_sock (newsock, "setnodelay");
        }
    }
if (!(opt_flags & SIM_SOCK_OPT_DATAGRAM)) {
    int keepalive = 1;

    /* enable TCP Keep Alives */
    sta = setsockopt (newsock, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(keepalive));
    if (sta == -1) 
        return sim_err_sock (newsock, "setsockopt KEEPALIVE");
    }
sta = connect (newsock, result->ai_addr, result->ai_addrlen);
p_freeaddrinfo (result);
if (sta == SOCKET_ERROR) {
    if (opt_flags & SIM_SOCK_OPT_BLOCKING) {
        if ((WSAGetLastError () == WSAETIMEDOUT)    ||                        /* expected errors after a connect failure */
            (WSAGetLastError () == WSAEHOSTUNREACH) ||
            (WSAGetLastError () == WSAECONNREFUSED) ||
            (WSAGetLastError () == WSAECONNABORTED) ||
            (WSAGetLastError () == WSAECONNRESET)) {
            sim_close_sock (newsock);
            newsock = INVALID_SOCKET;
            }
        else
            return sim_err_sock (newsock, "connect");
        }
    else    /* Non Blocking case won't return errors until some future read */
        if ((WSAGetLastError () != WSAEWOULDBLOCK) &&
            (WSAGetLastError () != WSAEINPROGRESS))
            return sim_err_sock (newsock, "connect");
    }
return newsock;                                         /* got it! */
}

SOCKET sim_accept_conn_ex (SOCKET master, char **connectaddr, int opt_flags)
{
int sta = 0, err;
int keepalive = 1;
#if defined (macintosh) || defined (__linux) || defined (__linux__) || \
    defined (__APPLE__) || defined (__OpenBSD__) || \
    defined(__NetBSD__) || defined(__FreeBSD__) || \
    (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \
    defined (__HAIKU__)
socklen_t size;
#elif defined (_WIN32) || defined (__EMX__) || \
     (defined (__ALPHA) && defined (__unix__)) || \
     defined (__hpux)
int size;
#else 
size_t size; 
#endif
SOCKET newsock;
struct sockaddr_storage clientname;

if (master == 0)                                        /* not attached? */
    return INVALID_SOCKET;
size = sizeof (clientname);
memset (&clientname, 0, sizeof(clientname));
newsock = accept (master, (struct sockaddr *) &clientname, &size);
if (newsock == INVALID_SOCKET) {                        /* error? */
    err = WSAGetLastError ();
    if (err != WSAEWOULDBLOCK)
        sim_err_sock(newsock, "accept");
    return INVALID_SOCKET;
    }
if (connectaddr != NULL) {
    *connectaddr = (char *)calloc(1, NI_MAXHOST+1);
#ifdef AF_INET6
    p_getnameinfo((struct sockaddr *)&clientname, size, *connectaddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
    if (0 == memcmp("::ffff:", *connectaddr, 7))        /* is this a IPv4-mapped IPv6 address? */
        memmove(*connectaddr, 7+*connectaddr,           /* prefer bare IPv4 address */
                strlen(*connectaddr) - 7 + 1);          /* length to include terminating \0 */
#else
    strcpy(*connectaddr, inet_ntoa(((struct sockaddr_in *)&connectaddr)->s_addr));
#endif
    }

if (!(opt_flags & SIM_SOCK_OPT_BLOCKING)) {
    sta = sim_setnonblock (newsock);                    /* set nonblocking */
    if (sta == SOCKET_ERROR)                            /* fcntl error? */
        return sim_err_sock (newsock, "fcntl");
    }

if ((opt_flags & SIM_SOCK_OPT_NODELAY)) {
    sta = sim_setnodelay (newsock);                     /* set nonblocking */
    if (sta == SOCKET_ERROR)                            /* setsockopt error? */
        return sim_err_sock (newsock, "setnodelay");
    }

/* enable TCP Keep Alives */
sta = setsockopt (newsock, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(keepalive));
if (sta == -1) 
    return sim_err_sock (newsock, "setsockopt KEEPALIVE");

return newsock;
}

int sim_check_conn (SOCKET sock, int rd)
{
fd_set rw_set, er_set;
fd_set *rw_p = &rw_set;
fd_set *er_p = &er_set;
struct timeval zero;
struct sockaddr_storage peername;
#if defined (macintosh) || defined (__linux) || defined (__linux__) || \
    defined (__APPLE__) || defined (__OpenBSD__) || \
    defined(__NetBSD__) || defined(__FreeBSD__) || \
    (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \
    defined (__HAIKU__)
socklen_t peernamesize = (socklen_t)sizeof(peername);
#elif defined (_WIN32) || defined (__EMX__) || \
     (defined (__ALPHA) && defined (__unix__)) || \
     defined (__hpux)
int peernamesize = (int)sizeof(peername);
#else 
size_t peernamesize = sizeof(peername); 
#endif

memset (&zero, 0, sizeof(zero));
FD_ZERO (rw_p);
FD_ZERO (er_p);
FD_SET (sock, rw_p);
FD_SET (sock, er_p);
if (rd)
    select ((int) sock + 1, rw_p, NULL, er_p, &zero);
else
    select ((int) sock + 1, NULL, rw_p, er_p, &zero);
if (FD_ISSET (sock, er_p))
    return -1;
if (FD_ISSET (sock, rw_p)) {
    if (0 == getpeername (sock, (struct sockaddr *)&peername, &peernamesize))
        return 1;
    else
        return -1;
    }
return 0;
}

static int _sim_getaddrname (struct sockaddr *addr, size_t addrsize, char *hostnamebuf, char *portnamebuf)
{
#if defined (macintosh) || defined (__linux) || defined (__linux__) || \
    defined (__APPLE__) || defined (__OpenBSD__) || \
    defined(__NetBSD__) || defined(__FreeBSD__) || \
    (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \
    defined (__HAIKU__)
socklen_t size = (socklen_t)addrsize;
#elif defined (_WIN32) || defined (__EMX__) || \
     (defined (__ALPHA) && defined (__unix__)) || \
     defined (__hpux)
int size = (int)addrsize;
#else 
size_t size = addrsize; 
#endif
int ret = 0;

#ifdef AF_INET6
*hostnamebuf = '\0';
*portnamebuf = '\0';
ret = p_getnameinfo(addr, size, hostnamebuf, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (0 == memcmp("::ffff:", hostnamebuf, 7))        /* is this a IPv4-mapped IPv6 address? */
    memmove(hostnamebuf, 7+hostnamebuf,            /* prefer bare IPv4 address */
            strlen(hostnamebuf) + 7 - 1);          /* length to include terminating \0 */
if (!ret)
    ret = p_getnameinfo(addr, size, NULL, 0, portnamebuf, NI_MAXSERV, NI_NUMERICSERV);
#else
strcpy(hostnamebuf, inet_ntoa(((struct sockaddr_in *)addr)->s_addr));
sprintf(portnamebuf, "%d", (int)ntohs(((struct sockaddr_in *)addr)->s_port)));
#endif
return ret;
}

int sim_getnames_sock (SOCKET sock, char **socknamebuf, char **peernamebuf)
{
struct sockaddr_storage sockname, peername;
#if defined (macintosh) || defined (__linux) || defined (__linux__) || \
    defined (__APPLE__) || defined (__OpenBSD__) || \
    defined(__NetBSD__) || defined(__FreeBSD__) || \
    (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \
    defined (__HAIKU__)
socklen_t socknamesize = (socklen_t)sizeof(sockname);
socklen_t peernamesize = (socklen_t)sizeof(peername);
#elif defined (_WIN32) || defined (__EMX__) || \
     (defined (__ALPHA) && defined (__unix__)) || \
     defined (__hpux)
int socknamesize = (int)sizeof(sockname);
int peernamesize = (int)sizeof(peername);
#else 
size_t socknamesize = sizeof(sockname); 
size_t peernamesize = sizeof(peername); 
#endif
char hostbuf[NI_MAXHOST+1];
char portbuf[NI_MAXSERV+1];

if (socknamebuf)
    *socknamebuf = (char *)calloc(1, NI_MAXHOST+NI_MAXSERV+4);
if (peernamebuf)
    *peernamebuf = (char *)calloc(1, NI_MAXHOST+NI_MAXSERV+4);
getsockname (sock, (struct sockaddr *)&sockname, &socknamesize);
getpeername (sock, (struct sockaddr *)&peername, &peernamesize);
if (socknamebuf != NULL) {
    _sim_getaddrname ((struct sockaddr *)&sockname, (size_t)socknamesize, hostbuf, portbuf);
    sprintf(*socknamebuf, "[%s]:%s", hostbuf, portbuf);
    }
if (peernamebuf != NULL) {
    _sim_getaddrname ((struct sockaddr *)&peername, (size_t)peernamesize, hostbuf, portbuf);
    sprintf(*peernamebuf, "[%s]:%s", hostbuf, portbuf);
    }
return 0;
}


int sim_read_sock (SOCKET sock, char *buf, int nbytes)
{
int rbytes, err;

rbytes = recv (sock, buf, nbytes, 0);
if (rbytes == 0)                                        /* disconnect */
    return -1;
if (rbytes == SOCKET_ERROR) {
    err = WSAGetLastError ();
    if (err == WSAEWOULDBLOCK)                          /* no data */
        return 0;
#if defined(EAGAIN)
    if (err == EAGAIN)                                  /* no data */
        return 0;
#endif
    if ((err != WSAETIMEDOUT) &&                        /* expected errors after a connect failure */
        (err != WSAEHOSTUNREACH) &&
        (err != WSAECONNREFUSED) &&
        (err != WSAECONNABORTED) &&
        (err != WSAECONNRESET) &&
        (err != WSAEINTR))                              /* or a close of a blocking read */
        sim_err_sock (INVALID_SOCKET, "read");
    return -1;
    }
return rbytes;
}

int sim_write_sock (SOCKET sock, const char *msg, int nbytes)
{
int err, sbytes = send (sock, msg, nbytes, 0);

if (sbytes == SOCKET_ERROR) {
    err = WSAGetLastError ();
    if (err == WSAEWOULDBLOCK)                          /* no data */
        return 0;
#if defined(EAGAIN)
    if (err == EAGAIN)                                  /* no data */
        return 0;
#endif
    }
return sbytes;
}

void sim_close_sock (SOCKET sock)
{
shutdown(sock, SD_BOTH);
closesocket (sock);
}

#endif                                                  /* end else !implemented */

#ifdef  __cplusplus
}
#endif
Added src/sim_sock.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_sock.h: OS-dependent socket routines header file

   Copyright (c) 2001-2008, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   15-Oct-12    MP      Added definitions needed to detect possible tcp 
                        connect failures
   25-Sep-12    MP      Reworked for RFC3493 interfaces supporting IPv6 and IPv4
   04-Jun-08    RMS     Addes sim_create_sock, for IBM 1130
   14-Apr-05    RMS     Added WSAEINPROGRESS (from Tim Riker)
   20-Aug-04    HV      Added missing definition for OS/2 (from Holger Veit)
   22-Oct-03    MP      Changed WIN32 winsock include to use winsock2.h to
                        avoid a conflict if sim_sock.h and sim_ether.h get
                        included by the same module.
   20-Mar-03    RMS     Added missing timerclear definition for VMS (from
                        Robert Alan Byer)
   15-Feb-03    RMS     Added time.h for EMX (from Holger Veit)
   17-Dec-02    RMS     Added sim_connect_sock
   08-Oct-02    RMS     Revised for .NET compatibility
   20-Aug-02    RMS     Changed calling sequence for sim_accept_conn
   30-Apr-02    RMS     Changed VMS stropts include to ioctl
   06-Feb-02    RMS     Added VMS support from Robert Alan Byer
   16-Sep-01    RMS     Added Macintosh support from Peter Schorn
*/

#ifndef SIM_SOCK_H_
#define SIM_SOCK_H_    0

#ifdef  __cplusplus
extern "C" {
#endif

#if defined (_WIN32)                                    /* Windows */
#include <winsock2.h>

#elif !defined (__OS2__) || defined (__EMX__)           /* VMS, Mac, Unix, OS/2 EMX */
#include <sys/types.h>                                  /* for fcntl, getpid */
#include <sys/socket.h>                                 /* for sockets */
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>                                 /* for sockaddr_in */
#include <netinet/tcp.h>                                /* for TCP_NODELAY */
#include <arpa/inet.h>                                  /* for inet_addr and inet_ntoa */
#include <netdb.h>
#include <sys/time.h>                                   /* for EMX */

#define WSAGetLastError()       errno                   /* Windows macros */
#define WSASetLastError(err) errno = err
#define closesocket     close 
#define SOCKET          int
#if defined(__hpux)
#define WSAEWOULDBLOCK  EAGAIN
#else
#define WSAEWOULDBLOCK  EWOULDBLOCK
#endif
#define WSAENAMETOOLONG ENAMETOOLONG
#define WSAEINPROGRESS  EINPROGRESS
#define WSAETIMEDOUT    ETIMEDOUT
#define WSAEISCONN      EISCONN
#define WSAECONNRESET   ECONNRESET
#define WSAECONNREFUSED ECONNREFUSED
#define WSAECONNABORTED ECONNABORTED
#define WSAEHOSTUNREACH EHOSTUNREACH
#define WSAEADDRINUSE   EADDRINUSE
#if defined(EAFNOSUPPORT)
#define WSAEAFNOSUPPORT EAFNOSUPPORT
#endif
#define WSAEACCES       EACCES
#define WSAEINTR        EINTR
#define INVALID_SOCKET  ((SOCKET)-1) 
#define SOCKET_ERROR    -1
#endif

#if defined (VMS)                                       /* VMS unique */
#include <ioctl.h>                                      /* for ioctl */
#if !defined (AI_NUMERICHOST)
#define AI_NUMERICHOST 0
#endif
#if defined (__VAX)
#define sockaddr_storage sockaddr
#endif
#endif

#if !defined(CBUFSIZE)
#define CBUFSIZE 1024
#define sim_printf printf
#endif

int sim_parse_addr (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr);
int sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, char *localport, size_t local_port_len, const char *default_port);
#define SIM_SOCK_OPT_REUSEADDR      0x0001
#define SIM_SOCK_OPT_DATAGRAM       0x0002
#define SIM_SOCK_OPT_NODELAY        0x0004
#define SIM_SOCK_OPT_BLOCKING       0x0008
SOCKET sim_master_sock_ex (const char *hostport, int *parse_status, int opt_flags);
#define sim_master_sock(hostport, parse_status) sim_master_sock_ex(hostport, parse_status, ((sim_switches & SWMASK ('U')) ? SIM_SOCK_OPT_REUSEADDR : 0))
SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, int opt_flags);
#define sim_connect_sock(hostport, default_host, default_port) sim_connect_sock_ex(NULL, hostport, default_host, default_port, 0)
SOCKET sim_accept_conn_ex (SOCKET master, char **connectaddr, int opt_flags);
#define sim_accept_conn(master, connectaddr) sim_accept_conn_ex(master, connectaddr, 0)
int sim_check_conn (SOCKET sock, int rd);
int sim_read_sock (SOCKET sock, char *buf, int nbytes);
int sim_write_sock (SOCKET sock, const char *msg, int nbytes);
void sim_close_sock (SOCKET sock);
const char *sim_get_err_sock (const char *emsg);
SOCKET sim_err_sock (SOCKET sock, const char *emsg);
int sim_getnames_sock (SOCKET sock, char **socknamebuf, char **peernamebuf);
void sim_init_sock (void);
void sim_cleanup_sock (void);

#ifdef  __cplusplus
}
#endif

#endif
Added src/sim_tape.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_tape.c: simulator tape support library

   Copyright (c) 1993-2008, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   Ultimately, this will be a place to hide processing of various tape formats,
   as well as OS-specific direct hardware access.

   23-Jan-12    MP      Added support for Logical EOT detection while positioning
   05-Feb-11    MP      Refactored to prepare for SIM_ASYNC_IO support
                        Added higher level routines:
                            sim_tape_wreomrw    - erase remainder of tape & rewind
                            sim_tape_sprecsf    - skip records
                            sim_tape_spfilef    - skip files
                            sim_tape_sprecsr    - skip records rev
                            sim_tape_spfiler    - skip files rev
                            sim_tape_position   - general purpose position
                        These routines correspond to natural tape operations 
                        and will align better when physical tape support is 
                        included here.
   08-Jun-08    JDB     Fixed signed/unsigned warning in sim_tape_set_fmt
   23-Jan-07    JDB     Fixed backspace over gap at BOT
   22-Jan-07    RMS     Fixed bug in P7B format read reclnt rev (found by Rich Cornwell)
   15-Dec-06    RMS     Added support for small capacity tapes
   30-Aug-06    JDB     Added erase gap support
   14-Feb-06    RMS     Added variable tape capacity
   23-Jan-06    JDB     Fixed odd-byte-write problem in sim_tape_wrrecf
   17-Dec-05    RMS     Added write support for Paul Pierce 7b format
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   02-May-05    RMS     Added support for Pierce 7b format
   28-Jul-04    RMS     Fixed bug in writing error records (found by Dave Bryan)
                RMS     Fixed incorrect error codes (found by Dave Bryan)
   05-Jan-04    RMS     Revised for file I/O library
   25-Apr-03    RMS     Added extended file support
   28-Mar-03    RMS     Added E11 and TPC format support

   Public routines:

   sim_tape_attach      attach tape unit
   sim_tape_detach      detach tape unit
   sim_tape_attach_help help routine for attaching tapes
   sim_tape_rdrecf      read tape record forward
   sim_tape_rdrecr      read tape record reverse
   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_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
   sim_tape_bot         TRUE if at beginning of tape
   sim_tape_eot         TRUE if at or beyond end of tape
   sim_tape_wrp         TRUE if write protected
   sim_tape_set_fmt     set tape format
   sim_tape_show_fmt    show tape format
   sim_tape_set_capac   set tape capacity
   sim_tape_show_capac  show tape capacity
   sim_tape_set_dens    set tape density
   sim_tape_show_dens   show tape density
   sim_tape_set_async   enable asynchronous operation
   sim_tape_clr_async   disable asynchronous operation
*/

#include "sim_defs.h"
#include "sim_tape.h"
#include <ctype.h>

#if defined SIM_ASYNCH_IO
#include <pthread.h>
#endif

struct sim_tape_fmt {
    const char          *name;                          /* name */
    int32               uflags;                         /* unit flags */
    t_addr              bot;                            /* bot test */
    };

static struct sim_tape_fmt fmts[MTUF_N_FMT] = {
    { "SIMH", 0,       sizeof (t_mtrlnt) - 1 },
    { "E11",  0,       sizeof (t_mtrlnt) - 1 },
    { "TPC",  UNIT_RO, sizeof (t_tpclnt) - 1 },
    { "P7B",  0,       0 },
/*  { "TPF",  UNIT_RO, 0 }, */
    { NULL,   0,       0 }
    };

static const uint32 bpi [] = {                          /* tape density table, indexed by MT_DENS constants */
    0,                                                  /*   0 = MT_DENS_NONE -- density not set */
    200,                                                /*   1 = MT_DENS_200  -- 200 bpi NRZI */
    556,                                                /*   2 = MT_DENS_556  -- 556 bpi NRZI */
    800,                                                /*   3 = MT_DENS_800  -- 800 bpi NRZI */
    1600,                                               /*   4 = MT_DENS_1600 -- 1600 bpi PE */
    6250                                                /*   5 = MT_DENS_6250 -- 6250 bpi GCR */
    };

#define BPI_COUNT       (sizeof (bpi) / sizeof (bpi [0]))   /* count of density table entries */

static t_stat sim_tape_ioerr (UNIT *uptr);
static t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat);
static uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map, uint32 mapsize);
static t_stat sim_tape_simh_check (UNIT *uptr);
static t_stat sim_tape_e11_check (UNIT *uptr);
static t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map);
static void sim_tape_data_trace (UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason);


struct tape_context {
    DEVICE              *dptr;              /* Device for unit (access to debug flags) */
    uint32              dbit;               /* debugging bit for trace */
    uint32              auto_format;        /* Format determined dynamically */
#if defined SIM_ASYNCH_IO
    int                 asynch_io;          /* Asynchronous Interrupt scheduling enabled */
    int                 asynch_io_latency;  /* instructions to delay pending interrupt */
    pthread_mutex_t     lock;
    pthread_t           io_thread;          /* I/O Thread Id */
    pthread_mutex_t     io_lock;
    pthread_cond_t      io_cond;
    pthread_cond_t      io_done;
    pthread_cond_t      startup_cond;
    int                 io_top;
    uint8               *buf;
    uint32              *bc;
    uint32              *fc;
    uint32              vbc;
    uint32              max;
    uint32              gaplen;
    uint32              bpi;
    uint32              *objupdate;
    TAPE_PCALLBACK      callback;
    t_stat              io_status;
#endif
    };
#define tape_ctx up8                        /* Field in Unit structure which points to the tape_context */

#if defined SIM_ASYNCH_IO
#define AIO_CALLSETUP                                                   \
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;       \
                                                                        \
if (ctx == NULL)                                                        \
    return sim_messagef (SCPE_IERR, "Bad Attach\n");                    \
if ((!callback) || !ctx->asynch_io)

#define AIO_CALL(op, _buf, _bc, _fc, _max, _vbc, _gaplen, _bpi, _obj, _callback)\
    if (ctx->asynch_io) {                                               \
        struct tape_context *ctx =                                      \
                      (struct tape_context *)uptr->tape_ctx;            \
                                                                        \
        pthread_mutex_lock (&ctx->io_lock);                             \
                                                                        \
        sim_debug (ctx->dbit, ctx->dptr,                                \
      "sim_tape AIO_CALL(op=%d, unit=%d)\n", op, (int)(uptr-ctx->dptr->units));\
                                                                        \
        if (ctx->callback)                                              \
            abort(); /* horrible mistake, stop */                       \
        ctx->io_top = op;                                               \
        ctx->buf = _buf;                                                \
        ctx->bc = _bc;                                                  \
        ctx->fc = _fc;                                                  \
        ctx->max = _max;                                                \
        ctx->vbc = _vbc;                                                \
        ctx->gaplen = _gaplen;                                          \
        ctx->bpi = _bpi;                                                \
        ctx->objupdate = _obj;                                          \
        ctx->callback = _callback;                                      \
        pthread_cond_signal (&ctx->io_cond);                            \
        pthread_mutex_unlock (&ctx->io_lock);                           \
        }                                                               \
    else                                                                \
        if (_callback)                                                  \
            (_callback) (uptr, r);
#define TOP_DONE  0             /* close */
#define TOP_RDRF  1             /* sim_tape_rdrecf_a */
#define TOP_RDRR  2             /* sim_tape_rdrecr_a */
#define TOP_WREC  3             /* sim_tape_wrrecf_a */
#define TOP_WTMK  4             /* sim_tape_wrtmk_a */
#define TOP_WEOM  5             /* sim_tape_wreom_a */
#define TOP_WEMR  6             /* sim_tape_wreomrw_a */
#define TOP_WGAP  7             /* sim_tape_wrgap_a */
#define TOP_SPRF  8             /* sim_tape_sprecf_a */
#define TOP_SRSF  9             /* sim_tape_sprecsf_a */
#define TOP_SPRR 10             /* sim_tape_sprecr_a */
#define TOP_SRSR 11             /* sim_tape_sprecsr_a */
#define TOP_SPFF 12             /* sim_tape_spfilef */
#define TOP_SFRF 13             /* sim_tape_spfilebyrecf */
#define TOP_SPFR 14             /* sim_tape_spfiler */
#define TOP_SFRR 15             /* sim_tape_spfilebyrecr */
#define TOP_RWND 16             /* sim_tape_rewind_a */
#define TOP_POSN 17             /* sim_tape_position_a */

static void *
_tape_io(void *arg)
{
UNIT* volatile uptr = (UNIT*)arg;
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

    /* Boost Priority for this I/O thread vs the CPU instruction execution 
       thread which in general won't be readily yielding the processor when 
       this thread needs to run */
    sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);

    sim_debug (ctx->dbit, ctx->dptr, "_tape_io(unit=%d) starting\n", (int)(uptr-ctx->dptr->units));

    pthread_mutex_lock (&ctx->io_lock);
    pthread_cond_signal (&ctx->startup_cond);   /* Signal we're ready to go */
    while (1) {
        pthread_cond_wait (&ctx->io_cond, &ctx->io_lock);
        if (ctx->io_top == TOP_DONE)
            break;
        pthread_mutex_unlock (&ctx->io_lock);
        switch (ctx->io_top) {
            case TOP_RDRF:
                ctx->io_status = sim_tape_rdrecf (uptr, ctx->buf, ctx->bc, ctx->max);
                break;
            case TOP_RDRR:
                ctx->io_status = sim_tape_rdrecr (uptr, ctx->buf, ctx->bc, ctx->max);
                break;
            case TOP_WREC:
                ctx->io_status = sim_tape_wrrecf (uptr, ctx->buf, ctx->vbc);
                break;
            case TOP_WTMK:
                ctx->io_status = sim_tape_wrtmk (uptr);
                break;
            case TOP_WEOM:
                ctx->io_status = sim_tape_wreom (uptr);
                break;
            case TOP_WEMR:
                ctx->io_status = sim_tape_wreomrw (uptr);
                break;
            case TOP_WGAP:
                ctx->io_status = sim_tape_wrgap (uptr, ctx->gaplen);
                break;
            case TOP_SPRF:
                ctx->io_status = sim_tape_sprecf (uptr, ctx->bc);
                break;
            case TOP_SRSF:
                ctx->io_status = sim_tape_sprecsf (uptr, ctx->vbc, ctx->bc);
                break;
            case TOP_SPRR:
                ctx->io_status = sim_tape_sprecr (uptr, ctx->bc);
                break;
            case TOP_SRSR:
                ctx->io_status = sim_tape_sprecsr (uptr, ctx->vbc, ctx->bc);
                break;
            case TOP_SPFF:
                ctx->io_status = sim_tape_spfilef (uptr, ctx->vbc, ctx->bc);
                break;
            case TOP_SFRF:
                ctx->io_status = sim_tape_spfilebyrecf (uptr, ctx->vbc, ctx->bc, ctx->fc, ctx->max);
                break;
            case TOP_SPFR:
                ctx->io_status = sim_tape_spfiler (uptr, ctx->vbc, ctx->bc);
                break;
            case TOP_SFRR:
                ctx->io_status = sim_tape_spfilebyrecr (uptr, ctx->vbc, ctx->bc, ctx->fc);
                break;
            case TOP_RWND:
                ctx->io_status = sim_tape_rewind (uptr);
                break;
            case TOP_POSN:
                ctx->io_status = sim_tape_position (uptr, ctx->vbc, ctx->gaplen, ctx->bc, ctx->bpi, ctx->fc, ctx->objupdate);
                break;
            }
        pthread_mutex_lock (&ctx->io_lock);
        ctx->io_top = TOP_DONE;
        pthread_cond_signal (&ctx->io_done);
        sim_activate (uptr, ctx->asynch_io_latency);
    }
    pthread_mutex_unlock (&ctx->io_lock);

    sim_debug (ctx->dbit, ctx->dptr, "_tape_io(unit=%d) exiting\n", (int)(uptr-ctx->dptr->units));

    return NULL;
}

/* This routine is called in the context of the main simulator thread before 
   processing events for any unit. It is only called when an asynchronous 
   thread has called sim_activate() to activate a unit.  The job of this 
   routine is to put the unit in proper condition to digest what may have
   occurred in the asynchronous thread.
   
   Since tape processing only handles a single I/O at a time to a 
   particular tape device, we have the opportunity to possibly detect 
   improper attempts to issue multiple concurrent I/O requests. */
static void _tape_completion_dispatch (UNIT *uptr)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
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->callback && ctx->io_top == TOP_DONE) {
    ctx->callback = NULL;
    callback (uptr, ctx->io_status);
    }
}

static t_bool _tape_is_active (UNIT *uptr)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

if (ctx) {
    sim_debug (ctx->dbit, ctx->dptr, "_tape_is_active(unit=%d, top=%d)\n", (int)(uptr-ctx->dptr->units), ctx->io_top);
    return (ctx->io_top != TOP_DONE);
    }
return FALSE;
}

static t_bool _tape_cancel (UNIT *uptr)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

if (ctx) {
    sim_debug (ctx->dbit, ctx->dptr, "_tape_cancel(unit=%d, top=%d)\n", (int)(uptr-ctx->dptr->units), ctx->io_top);
    if (ctx->asynch_io) {
        pthread_mutex_lock (&ctx->io_lock);
        while (ctx->io_top != TOP_DONE)
            pthread_cond_wait (&ctx->io_done, &ctx->io_lock);
        pthread_mutex_unlock (&ctx->io_lock);
        }
    }
return FALSE;
}
#else
#define AIO_CALLSETUP                                                       \
    if (uptr->tape_ctx == NULL)                                             \
        return sim_messagef (SCPE_IERR, "Bad Attach\n");
#define AIO_CALL(op, _buf, _fc, _bc, _max, _vbc, _gaplen, _bpi, _obj, _callback) \
    if (_callback)                                                    \
        (_callback) (uptr, r);
#endif


/* 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 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) {
    pthread_mutex_init (&ctx->io_lock, NULL);
    pthread_cond_init (&ctx->io_cond, NULL);
    pthread_cond_init (&ctx->io_done, NULL);
    pthread_cond_init (&ctx->startup_cond, NULL);
    pthread_attr_init(&attr);
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    pthread_mutex_lock (&ctx->io_lock);
    pthread_create (&ctx->io_thread, &attr, _tape_io, (void *)uptr);
    pthread_attr_destroy(&attr);
    pthread_cond_wait (&ctx->startup_cond, &ctx->io_lock); /* Wait for thread to stabilize */
    pthread_mutex_unlock (&ctx->io_lock);
    pthread_cond_destroy (&ctx->startup_cond);
    }
uptr->a_check_completion = _tape_completion_dispatch;
uptr->a_is_active = _tape_is_active;
uptr->cancel = _tape_cancel;
return SCPE_OK;
#endif
}

/* Disable asynchronous operation */

t_stat sim_tape_clr_async (UNIT *uptr)
{
#if !defined(SIM_ASYNCH_IO)
return SCPE_NOFNC;
#else
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

/* make sure device exists */
if (!ctx) return SCPE_UNATT;

if (ctx->asynch_io) {
    pthread_mutex_lock (&ctx->io_lock);
    ctx->asynch_io = 0;
    pthread_cond_signal (&ctx->io_cond);
    pthread_mutex_unlock (&ctx->io_lock);
    pthread_join (ctx->io_thread, NULL);
    pthread_mutex_destroy (&ctx->io_lock);
    pthread_cond_destroy (&ctx->io_cond);
    pthread_cond_destroy (&ctx->io_done);
    }
return SCPE_OK;
#endif
}

/* 
   This routine is called when the simulator stops and any time
   the asynch mode is changed (enabled or disabled)
*/
static void _sim_tape_io_flush (UNIT *uptr)
{
#if defined (SIM_ASYNCH_IO)
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

sim_tape_clr_async (uptr);
if (sim_asynch_enabled)
    sim_tape_set_async (uptr, ctx->asynch_io_latency);
#endif
fflush (uptr->fileref);
}

/* Attach tape unit */

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

t_stat sim_tape_attach_ex (UNIT *uptr, const char *cptr, uint32 dbit, int completion_delay)
{
struct tape_context *ctx;
uint32 objc;
DEVICE *dptr;
char gbuf[CBUFSIZE];
t_stat r;
t_bool auto_format = FALSE;

if ((dptr = find_dev_from_unit (uptr)) == NULL)
    return SCPE_NOATT;
if (sim_switches & SWMASK ('F')) {                      /* format spec? */
    cptr = get_glyph (cptr, gbuf, 0);                   /* get spec */
    if (*cptr == 0)                                     /* must be more */
        return SCPE_2FARG;
    if (sim_tape_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK)
        return sim_messagef (SCPE_ARG, "Invalid Tape Format: %s\n", gbuf);
    sim_switches = sim_switches & ~(SWMASK ('F'));      /* Record Format specifier already processed */
    auto_format = TRUE;
    }
if (MT_GET_FMT (uptr) == MTUF_F_TPC)
    sim_switches |= SWMASK ('R');                       /* Force ReadOnly attach for TPC tapes */
r = attach_unit (uptr, (CONST char *)cptr);             /* attach unit */
if (r != SCPE_OK)                                       /* error? */
    return sim_messagef (r, "Can't open tape image: %s\n", cptr);
switch (MT_GET_FMT (uptr)) {                            /* case on format */

    case MTUF_F_STD:                                    /* SIMH */
        if (SCPE_OK != sim_tape_simh_check (uptr)) {
            sim_tape_detach (uptr);
            return SCPE_FMT;                            /* yes, complain */
            }
        break;

    case MTUF_F_E11:                                    /* E11 */
        if (SCPE_OK != sim_tape_e11_check (uptr)) {
            sim_tape_detach (uptr);
            return SCPE_FMT;                            /* yes, complain */
            }
        break;

    case MTUF_F_TPC:                                    /* TPC */
        objc = sim_tape_tpc_map (uptr, NULL, 0);        /* get # objects */
        if (objc == 0) {                                /* tape empty? */
            sim_tape_detach (uptr);
            return SCPE_FMT;                            /* yes, complain */
            }
        uptr->filebuf = calloc (objc + 1, sizeof (t_addr));
        if (uptr->filebuf == NULL) {                    /* map allocated? */
            sim_tape_detach (uptr);
            return SCPE_MEM;                            /* no, complain */
            }
        uptr->hwmark = objc + 1;                        /* save map size */
        sim_tape_tpc_map (uptr, (t_addr *) uptr->filebuf, objc);/* fill map */
        break;

    default:
        break;
        }

uptr->tape_ctx = ctx = (struct tape_context *)calloc(1, sizeof(struct tape_context));
ctx->dptr = dptr;                                       /* save DEVICE pointer */
ctx->dbit = dbit;                                       /* save debug bit */
ctx->auto_format = auto_format;                         /* save that we auto selected format */

sim_tape_rewind (uptr);

#if defined (SIM_ASYNCH_IO)
sim_tape_set_async (uptr, completion_delay);
#endif
uptr->io_flush = _sim_tape_io_flush;

return SCPE_OK;
}

/* Detach tape unit */

t_stat sim_tape_detach (UNIT *uptr)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
uint32 f = MT_GET_FMT (uptr);
t_stat r;
t_bool auto_format = FALSE;

if ((ctx == NULL) || (uptr == NULL) || !(uptr->flags & UNIT_ATT))
    return SCPE_IERR;

if (uptr->io_flush)
    uptr->io_flush (uptr);                              /* flush buffered data */
if (ctx)
    auto_format = ctx->auto_format;

sim_tape_clr_async (uptr);

r = detach_unit (uptr);                                 /* detach unit */
if (r != SCPE_OK)
    return r;
switch (f) {                                            /* case on format */

    case MTUF_F_TPC:                                    /* TPC */
        if (uptr->filebuf)                              /* free map */
            free (uptr->filebuf);
        uptr->filebuf = NULL;
        uptr->hwmark = 0;
        break;

    default:
        break;
        }

sim_tape_rewind (uptr);
free (uptr->tape_ctx);
uptr->tape_ctx = NULL;
uptr->io_flush = NULL;
if (auto_format)    /* format was determined or specified at attach time? */
    sim_tape_set_fmt (uptr, 0, "SIMH", NULL);   /* restore default format */
return SCPE_OK;
}

t_stat sim_tape_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "%s Tape Attach Help\n\n", dptr->name);
if (0 == (uptr-dptr->units)) {
    if (dptr->numunits > 1) {
        uint32 i;

        for (i=0; i < dptr->numunits; ++i)
            if (dptr->units[i].flags & UNIT_ATTABLE)
                fprintf (st, "  sim> ATTACH {switches} %s%d tapefile\n\n", dptr->name, i);
        }
    else
        fprintf (st, "  sim> ATTACH {switches} %s tapefile\n\n", dptr->name);
    }
else
    fprintf (st, "  sim> ATTACH {switches} %s tapefile\n\n", dptr->name);
fprintf (st, "Attach command switches\n");
fprintf (st, "    -R          Attach Read Only.\n");
fprintf (st, "    -E          Must Exist (if not specified an attempt to create the indicated\n");
fprintf (st, "                virtual tape will be attempted).\n");
fprintf (st, "    -F          Open the indicated tape container in a specific format (default\n");
fprintf (st, "                is SIMH, alternatives are E11, TPC and P7B)\n");
return SCPE_OK;
}

static void sim_tape_data_trace(UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

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)

   Inputs:
        uptr    =       pointer to tape unit
        bc      =       pointer to returned record length
   Outputs:
        status  =       operation status

   exit condition       tape position
   ------------------   -----------------------------------------------------
   unit unattached      unchanged
   read error           unchanged, PNU set
   end of file/medium   updated if a gap precedes, else unchanged and PNU set
   tape mark            updated
   tape runaway         updated
   data record          updated, sim_fread will read record forward

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

   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.

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

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

    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 */
                    else                                /*   otherwise report the physical EOF */
                        r = 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 */
                    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 */
                        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 */
                        else                            /*       otherwise report the physical EOF */
                            r = 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 */
                else                                    /*   otherwise report the physical EOF */
                    r = 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 */
                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 */

                *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 */
                }
            }
        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 */

        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);
            }
        if (feof (uptr->fileref)) {                     /* eof? */
            MT_SET_PNU (uptr);                          /* pos not upd */
            r = MTSE_EOM;
            break;
            }
        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 */
        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);
                }
            if (feof (uptr->fileref)) {                 /* eof? */
                if (sbc == 0)                           /* no data? eom */
                    return MTSE_EOM;
                break;                                  /* treat like eor */
                }
            if ((sbc != 0) && (c & P7B_SOR))            /* next record? */
                break;
            if ((c & P7B_DPAR) != P7B_EOF)
                all_eof = 0;
            }
        *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;
        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;
}

/* Read record length reverse (internal routine)

   Inputs:
        uptr    =       pointer to tape unit
        bc      =       pointer to returned record length
   Outputs:
        status  =       operation status

   exit condition       tape position
   ------------------   -------------------------------------------
   unit unattached      unchanged
   beginning of tape    unchanged
   read error           unchanged
   end of file          unchanged
   end of medium        updated
   tape mark            updated
   tape runaway         updated
   data record          updated, sim_fread will read record forward

   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.

   See the notes at "sim_tape_rdlntf" and "sim_tape_wrgap" 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;
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;

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

switch (f) {                                            /* 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 */

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

        do {                                            /* loop until a record, gap, or error 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 */
                    break;
                    }

                else if (uptr->pos < sizeof (buffer))   /* if less than a full buffer remains */
                    bufcap = (uint32) uptr->pos         /*   then reduce the capacity accordingly */
                               / sizeof (t_mtrlnt);

                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 (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 */
                    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 */
                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);
                }
            }
        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 */

        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;
            }
        uptr->pos = ppos;                               /* spc over record */
        if (*bc == MTR_TMK) {                           /* tape mark? */
            r = MTSE_TMK;
            break;
            }
        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;
                break;
                }
            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;
        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;
}

/* Read record forward

   Inputs:
        uptr    =       pointer to tape unit
        buf     =       pointer to buffer
        bc      =       pointer to returned record length
        max     =       maximum record size
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   read error           unchanged, PNU set
   end of file/medium   unchanged, PNU set
   invalid record       unchanged, PNU set
   tape mark            updated
   data record          updated
   data record error    updated
*/

t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
uint32 f = MT_GET_FMT (uptr);
t_mtrlnt i, tbc, rbc;
t_addr opos;
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 */
    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 */
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;
if (f == MTUF_F_P7B)                                    /* p7b? strip SOR */
    buf[0] = buf[0] & P7B_DPAR;
sim_tape_data_trace(uptr, buf, rbc, "Record Read", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);
}

t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback)
{
t_stat r = SCPE_OK;
AIO_CALLSETUP
    r = sim_tape_rdrecf (uptr, buf, bc, max);
AIO_CALL(TOP_RDRF, buf, bc, NULL, max, 0, 0, 0, NULL, callback);
return r;
}


/* Read record reverse

   Inputs:
        uptr    =       pointer to tape unit
        buf     =       pointer to buffer
        bc      =       pointer to returned record length
        max     =       maximum record size
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   read error           unchanged
   end of file          unchanged
   end of medium        updated
   invalid record       unchanged
   tape mark            updated
   data record          updated
   data record error    updated
*/

t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
uint32 f = MT_GET_FMT (uptr);
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 */
    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 */
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);
return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);
}

t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback)
{
t_stat r = SCPE_OK;
AIO_CALLSETUP
    r = sim_tape_rdrecr (uptr, buf, bc, max);
AIO_CALL(TOP_RDRR, buf, bc, NULL, max, 0, 0, 0, NULL, callback);
return r;
}

/* Write record forward

   Inputs:
        uptr    =       pointer to tape unit
        buf     =       pointer to buffer
        bc      =       record length
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   write protect        unchanged
   write error          unchanged, PNU set
   data record          updated
*/

t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
uint32 f = MT_GET_FMT (uptr);
t_mtrlnt sbc;

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_wrrecf(unit=%d, buf=%p, bc=%d)\n", (int)(uptr-ctx->dptr->units), buf, bc);

sim_tape_data_trace(uptr, buf, bc, "Record Write", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
MT_CLR_PNU (uptr);
sbc = MTR_L (bc);
if ((uptr->flags & UNIT_ATT) == 0)                      /* not attached? */
    return MTSE_UNATT;
if (sim_tape_wrp (uptr))                                /* write prot? */
    return MTSE_WRP;
if (sbc == 0)                                           /* nothing to do? */
    return MTSE_OK;
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* set pos */
switch (f) {                                            /* case on format */

    case MTUF_F_STD:                                    /* standard */
        sbc = MTR_L ((bc + 1) & ~1);                    /* pad odd length */
    case MTUF_F_E11:                                    /* E11 */
        sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
        sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);
        sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
        if (ferror (uptr->fileref)) {                   /* error? */
            MT_SET_PNU (uptr);
            return sim_tape_ioerr (uptr);
            }
        uptr->pos = uptr->pos + sbc + (2 * sizeof (t_mtrlnt));  /* move tape */
        break;

    case MTUF_F_P7B:                                    /* Pierce 7B */
        buf[0] = buf[0] | P7B_SOR;                      /* mark start of rec */
        sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);
        sim_fwrite (buf, sizeof (uint8), 1, uptr->fileref); /* delimit rec */
        if (ferror (uptr->fileref)) {                   /* error? */
            MT_SET_PNU (uptr);
            return sim_tape_ioerr (uptr);
            }
        uptr->pos = uptr->pos + sbc;                    /* move tape */
        break;
        }
sim_tape_data_trace(uptr, buf, sbc, "Record Written", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR);
return MTSE_OK;
}

t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback)
{
t_stat r = SCPE_OK;
AIO_CALLSETUP
    r = sim_tape_wrrecf (uptr, buf, bc);
AIO_CALL(TOP_WREC, buf, 0, NULL, 0, bc, 0, 0, NULL, callback);
return r;
}

/* Write metadata forward (internal routine) */

static t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

MT_CLR_PNU (uptr);
if ((uptr->flags & UNIT_ATT) == 0)                      /* not attached? */
    return MTSE_UNATT;
if (ctx == NULL)                                        /* if not properly attached? */
    return sim_messagef (SCPE_IERR, "Bad Attach\n");    /*   that's a problem */
if (sim_tape_wrp (uptr))                                /* write prot? */
    return MTSE_WRP;
sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);         /* set pos */
sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref);
if (ferror (uptr->fileref)) {                           /* error? */
    MT_SET_PNU (uptr);
    return sim_tape_ioerr (uptr);
    }
sim_debug (MTSE_DBG_STR, ctx->dptr, "wr_lnt: lnt: %d, pos: %" T_ADDR_FMT "u\n", dat, uptr->pos);
uptr->pos = uptr->pos + sizeof (t_mtrlnt);              /* move tape */
return MTSE_OK;
}

/* Write tape mark */

t_stat sim_tape_wrtmk (UNIT *uptr)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

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_wrtmk(unit=%d)\n", (int)(uptr-ctx->dptr->units));
if (MT_GET_FMT (uptr) == MTUF_F_P7B) {                  /* P7B? */
    uint8 buf = P7B_EOF;                                /* eof mark */
    return sim_tape_wrrecf (uptr, &buf, 1);             /* write char */
    }
return sim_tape_wrdata (uptr, MTR_TMK);
}

t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_wrtmk (uptr);
AIO_CALL(TOP_WTMK, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback);
return r;
}

/* Write end of medium */

t_stat sim_tape_wreom (UNIT *uptr)
{
t_stat result;
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

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_wreom(unit=%d)\n", (int)(uptr-ctx->dptr->units));
if (MT_GET_FMT (uptr) == MTUF_F_P7B)                    /* cant do P7B */
    return MTSE_FMT;

result = sim_tape_wrdata (uptr, MTR_EOM);               /* write the EOM marker */

uptr->pos = uptr->pos - sizeof (t_mtrlnt);              /* restore original tape position */
MT_SET_PNU (uptr);                                      /* indicate that position was not updated */

return result;
}

t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_wreom (uptr);
AIO_CALL(TOP_WEOM, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback);
return r;
}

/* Write end of medium-rewind */

t_stat sim_tape_wreomrw (UNIT *uptr)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat r;

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_wreomrw(unit=%d)\n", (int)(uptr-ctx->dptr->units));
if (MT_GET_FMT (uptr) == MTUF_F_P7B)                    /* cant do P7B */
    return MTSE_FMT;
r = sim_tape_wrdata (uptr, MTR_EOM);
if (r == MTSE_OK)
    r = sim_tape_rewind (uptr);
return r;
}

t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback)
{
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

   Outputs:
        status  = operation status

   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


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

   The permissibility of data record lengths that are not multiples of the
   metadatum size presents a difficulty when reading.  If such an "odd length"
   record is written over a gap, half of a metadata marker will exist
   immediately after the trailing record length.

   This condition is detected when reading forward by the appearance of a
   "reversed" marker.  The value appears reversed because the value is made up
   of half of one marker and half of the next.  This is handled by seeking
   forward two bytes to resync (the stipulation above that the overwrite cannot
   leave only two bytes of gap means that at least one "whole" metadata marker
   will follow).  Reading in reverse presents a more complex problem, because
   half of the marker is from the preceding trailing record length marker and
   therefore could be any of a range of values.  However, that range is
   restricted by the SIMH tape specification requirement that record length
   metadata values must have bits 30:24 set to zero.  This allows unambiguous
   detection of the condition.

   The value chosen for gap metadata and the values reserved for "half-gap"
   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
   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.
*/

t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
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 */

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

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.

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

do {
    sim_fread (&meta, meta_size, 1, uptr->fileref);     /* read 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 */

    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 (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 */
        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 */
        }

/* 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 */
            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 */
            st = sim_tape_wrdata (uptr, new_len);           /* write new rec len */

            if (st != MTSE_OK) {                            /* write OK? */
                uptr->pos = gap_pos;                        /* restore orig pos */
                return st;                                  /* PNU was set by wrdata */
                }

            uptr->pos = uptr->pos + sbc - gap_needed;       /* position to end of data */
            st = sim_tape_wrdata (uptr, new_len);           /* write new rec len */

            if (st != MTSE_OK) {                            /* write OK? */
                uptr->pos = gap_pos;                        /* restore orig pos */
                return st;                                  /* PNU was set by wrdata */
                }

            gap_alloc = gap_alloc + gap_needed;             /* allocate remainder */
            gap_needed = 0;
            }
        }
    }
while (gap_needed > 0);

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

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

/* Space record forward

   Inputs:
        uptr    =       pointer to tape unit
        bc      =       pointer to size of record skipped
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   read error           unchanged, PNU set
   end of file/medium   unchanged, PNU set
   tape mark            updated
   data record          updated
   data record error    updated
*/

t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc)
{
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 */
*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;
AIO_CALLSETUP
    r = sim_tape_sprecf (uptr, bc);
AIO_CALL(TOP_SPRF, NULL, bc, NULL, 0, 0, 0, 0, NULL, callback);
return r;
}

/* Space records forward

   Inputs:
        uptr    =       pointer to tape unit
        count   =       count of records to skip
        skipped =       pointer to number of records actually skipped
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   read error           unchanged, PNU set
   end of file/medium   unchanged, PNU set
   tape mark            updated
   data record          updated
   data record error    updated
*/

t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat st;
t_mtrlnt tbc;

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_sprecsf(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);

*skipped = 0;
while (*skipped < count) {                              /* loopo */
    st = sim_tape_sprecf (uptr, &tbc);                  /* spc rec */
    if (st != MTSE_OK)
        return st;
    *skipped = *skipped + 1;                            /* # recs skipped */
    }
return MTSE_OK;
}

t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_sprecsf (uptr, count, skipped);
AIO_CALL(TOP_SRSF, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback);
return r;
}

/* Space record reverse

   Inputs:
        uptr    =       pointer to tape unit
        bc      =       pointer to size of records skipped
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   beginning of tape    unchanged
   read error           unchanged
   end of file          unchanged
   end of medium        updated
   tape mark            updated
   data record          updated
*/

t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc)
{
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_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 */
*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;
AIO_CALLSETUP
    r = sim_tape_sprecr (uptr, bc);
AIO_CALL(TOP_SPRR, NULL, bc, NULL, 0, 0, 0, 0, NULL, callback);
return r;
}

/* Space records reverse

   Inputs:
        uptr    =       pointer to tape unit
        count   =       count of records to skip
        skipped =       pointer to number of records actually skipped
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   beginning of tape    unchanged
   read error           unchanged
   end of file          unchanged
   end of medium        updated
   tape mark            updated
   data record          updated
*/

t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat st;
t_mtrlnt tbc;

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_sprecsr(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);

*skipped = 0;
while (*skipped < count) {                              /* loopo */
    st = sim_tape_sprecr (uptr, &tbc);                  /* spc rec rev */
    if (st != MTSE_OK)
        return st;
    *skipped = *skipped + 1;                            /* # recs skipped */
    }
return MTSE_OK;
}

t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_sprecsr (uptr, count, skipped);
AIO_CALL(TOP_SRSR, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback);
return r;
}

/* Space files forward by record

   Inputs:
        uptr    =       pointer to tape unit
        count   =       count of files to skip
        skipped =       pointer to number of files actually skipped
        recsskipped =   pointer to number of records skipped
        check_leot =    flag to detect and stop skip between two successive tape marks
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   read error           unchanged, PNU set
   end of file/medium   unchanged, PNU set
   tape mark            updated
   data record          updated
   data record error    updated
*/

t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat st;
t_bool last_tapemark = FALSE;
uint32 filerecsskipped;

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);
    last_tapemark = (MTSE_TMK == st);
    if ((st == MTSE_OK) || (st == MTSE_TMK))
        sim_tape_rdlntf (uptr, &rbc);
    }
*skipped = 0;
*recsskipped = 0;
while (*skipped < count) {                              /* loopo */
    while (1) {
        st = sim_tape_sprecsf (uptr, 0x1ffffff, &filerecsskipped);/* spc recs */
        *recsskipped += filerecsskipped;
        if (st != MTSE_OK)
            break;
        }
    if (st == MTSE_TMK) {
        *skipped = *skipped + 1;                        /* # files skipped */
        if (check_leot && (filerecsskipped == 0) && last_tapemark) {
            uint32 filefileskipped;
            sim_tape_spfilebyrecr (uptr, 1, &filefileskipped, &filerecsskipped);
            *skipped = *skipped - 1;                    /* adjust # files skipped */
            return MTSE_LEOT;
            }
        last_tapemark = TRUE;
        }
    else
        return st;
    }
return MTSE_OK;
}

t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_spfilebyrecf (uptr, count, skipped, recsskipped, check_leot);
AIO_CALL(TOP_SFRF, NULL, skipped, recsskipped, check_leot, count, 0, 0, NULL, callback);
return r;
}

/* Space files forward

   Inputs:
        uptr    =       pointer to tape unit
        count   =       count of files to skip
        skipped =       pointer to number of files actually skipped
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   read error           unchanged, PNU set
   end of file/medium   unchanged, PNU set
   tape mark            updated
   data record          updated
   data record error    updated
*/

t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
uint32 totalrecsskipped;

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_spfilef(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);

return sim_tape_spfilebyrecf (uptr, count, skipped, &totalrecsskipped, FALSE);
}

t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_spfilef (uptr, count, skipped);
AIO_CALL(TOP_SPFF, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback);
return r;
}

/* Space files reverse by record

   Inputs:
        uptr    =       pointer to tape unit
        count   =       count of files to skip
        skipped =       pointer to number of files actually skipped
        recsskipped =   pointer to number of records skipped
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   beginning of tape    unchanged
   read error           unchanged
   end of file          unchanged
   end of medium        updated
   tape mark            updated
   data record          updated
*/

t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat st;
uint32 filerecsskipped;

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_spfilebyrecr(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);

*skipped = 0;
*recsskipped = 0;
while (*skipped < count) {                              /* loopo */
    while (1) {
        st = sim_tape_sprecsr (uptr, 0x1ffffff, &filerecsskipped);/* spc recs rev */
        *recsskipped += filerecsskipped;
        if (st != MTSE_OK)
            break;
        }
    if (st == MTSE_TMK)
        *skipped = *skipped + 1;                        /* # files skipped */
    else
        return st;
    }
return MTSE_OK;
}

t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_spfilebyrecr (uptr, count, skipped, recsskipped);
AIO_CALL(TOP_SPFR, NULL, skipped, recsskipped, 0, count, 0, 0, NULL, callback);
return r;
}

/* Space files reverse

   Inputs:
        uptr    =       pointer to tape unit
        count   =       count of files to skip
        skipped =       pointer to number of files actually skipped
   Outputs:
        status  =       operation status

   exit condition       position

   unit unattached      unchanged
   beginning of tape    unchanged
   read error           unchanged
   end of file          unchanged
   end of medium        updated
   tape mark            updated
   data record          updated
*/

t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
uint32 totalrecsskipped;

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_spfiler(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count);

return sim_tape_spfilebyrecr (uptr, count, skipped, &totalrecsskipped);
}

t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_spfiler (uptr, count, skipped);
AIO_CALL(TOP_SPFR, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback);
return r;
}

/* Rewind tape */

t_stat sim_tape_rewind (UNIT *uptr)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

if (uptr->flags & UNIT_ATT) {
    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_rewind(unit=%d)\n", (int)(uptr-ctx->dptr->units));
    }
uptr->pos = 0;
MT_CLR_PNU (uptr);
return MTSE_OK;
}

t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_rewind (uptr);
AIO_CALL(TOP_RWND, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback);
return r;
}

/* Position Tape */

t_stat sim_tape_position (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
t_stat r = MTSE_OK;

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_position(unit=%d, flags=0x%X, recs=%d, files=%d)\n", (int)(uptr-ctx->dptr->units), flags, recs, files);

*recsskipped = *filesskipped = *objectsskipped = 0;
if (flags & MTPOS_M_REW)
    r = sim_tape_rewind (uptr);
if (r != MTSE_OK)
    return r;
if (flags & MTPOS_M_OBJ) {
    uint32 objs = recs;
    uint32 skipped;
    uint32 objsremaining = objs;

    while (*objectsskipped < objs) {                       /* loopo */
        if (flags & MTPOS_M_REV)                        /* reverse? */
            r = sim_tape_sprecsr (uptr, objsremaining, &skipped);
        else
            r = sim_tape_sprecsf (uptr, objsremaining, &skipped);
        objsremaining = objsremaining - (skipped + ((r == MTSE_TMK) ? 1 : 0));
        if ((r == MTSE_TMK) || (r == MTSE_OK))
            *objectsskipped = *objectsskipped + skipped + ((r == MTSE_TMK) ? 1 : 0);
        else
            return r;
        }
    r = MTSE_OK;
    }
else {
    uint32 fileskiprecs;

    if (flags & MTPOS_M_REV)                            /* reverse? */
        r = sim_tape_spfilebyrecr (uptr, files, filesskipped, &fileskiprecs);
    else
        r = sim_tape_spfilebyrecf (uptr, files, filesskipped, &fileskiprecs, (flags & MTPOS_M_DLE));
    if (r != MTSE_OK)
        return r;
    if (flags & MTPOS_M_REV)                            /* reverse? */
        r = sim_tape_sprecsr (uptr, recs, recsskipped);
    else
        r = sim_tape_sprecsf (uptr, recs, recsskipped);
    if (r == MTSE_TMK)
        *filesskipped = *filesskipped + 1;
    *objectsskipped = fileskiprecs + *filesskipped + *recsskipped;
    }
return r;
}

t_stat sim_tape_position_a (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback)
{
t_stat r = MTSE_OK;
AIO_CALLSETUP
    r = sim_tape_position (uptr, flags, recs, recsskipped, files, filesskipped, objectsskipped);
AIO_CALL(TOP_POSN, NULL, recsskipped, filesskipped, 0, flags, recs, files, objectsskipped, callback);
return r;
}

/* Reset tape */

t_stat sim_tape_reset (UNIT *uptr)
{
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;

MT_CLR_PNU (uptr);
if (!(uptr->flags & UNIT_ATT))                          /* attached? */
    return SCPE_OK;

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_reset(unit=%d)\n", (int)(uptr-ctx->dptr->units));

_sim_tape_io_flush(uptr);
AIO_VALIDATE;
AIO_UPDATE_QUEUE;
return SCPE_OK;
}

/* Test for BOT */

t_bool sim_tape_bot (UNIT *uptr)
{
uint32 f = MT_GET_FMT (uptr);

return (uptr->pos <= fmts[f].bot)? TRUE: FALSE;
}

/* Test for end of tape */

t_bool sim_tape_eot (UNIT *uptr)
{
return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE;
}

/* Test for write protect */

t_bool sim_tape_wrp (UNIT *uptr)
{
return ((uptr->flags & MTUF_WRP) || (MT_GET_FMT (uptr) == MTUF_F_TPC))? TRUE: FALSE;
}

/* Process I/O error */

static t_stat sim_tape_ioerr (UNIT *uptr)
{
sim_printf ("%s: Magtape library I/O error: %s\n", sim_uname (uptr), strerror (errno));
clearerr (uptr->fileref);
return MTSE_IOERR;
}

/* Set tape format */

t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
uint32 f;

if (uptr->flags & UNIT_ATT)
    return SCPE_ALATT;
if (uptr == NULL)
    return SCPE_IERR;
if (cptr == NULL)
    return SCPE_ARG;
for (f = 0; f < MTUF_N_FMT; f++) {
    if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) {
        uptr->flags = (uptr->flags & ~MTUF_FMT) |
            (f << MTUF_V_FMT) | fmts[f].uflags;
        return SCPE_OK;
        }
    }
return SCPE_ARG;
}

/* Show tape format */

t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 f = MT_GET_FMT (uptr);

if (fmts[f].name)
    fprintf (st, "%s format", fmts[f].name);
else fprintf (st, "invalid format");
return SCPE_OK;
}

/* Map a TPC format tape image */

static uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map, uint32 mapsize)
{
t_addr tpos, leot;
t_addr tape_size;
t_tpclnt bc, last_bc = 0xFFFF;
uint32 had_double_tape_mark = 0;
size_t i;
uint32 objc, sizec;
uint32 *countmap = NULL;
uint8 *recbuf = NULL;
DEVICE *dptr = find_dev_from_unit (uptr);

if ((uptr == NULL) || (uptr->fileref == NULL))
    return 0;
countmap = (uint32 *)calloc (65536, sizeof(*countmap));
recbuf = (uint8 *)malloc (65536);
tape_size = (t_addr)sim_fsize (uptr->fileref);
sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape_size: %" T_ADDR_FMT "u\n", tape_size);
for (objc = 0, sizec = 0, tpos = 0;; ) {
    sim_fseek (uptr->fileref, tpos, SEEK_SET);
    i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref);
    if (i == 0)     /* past or at eof? */
        break;
    if (countmap[bc] == 0)
        sizec++;
    ++countmap[bc];
    if (map && (objc < mapsize))
        map[objc] = tpos;
    if (bc) {
        sim_debug (MTSE_DBG_STR, dptr, "tpc_map: %d byte count at pos: %" T_ADDR_FMT "u\n", bc, tpos);
        if (sim_deb && (dptr->dctrl & MTSE_DBG_STR)) {
            sim_fread (recbuf, 1, bc, uptr->fileref);
            sim_data_trace(dptr, uptr, ((dptr->dctrl & MTSE_DBG_DAT) ? recbuf : NULL), "", bc, "Data Record", MTSE_DBG_STR);
            }
        }
    else
        sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape mark at pos: %" T_ADDR_FMT "u\n", tpos);
    objc++;
    tpos = tpos + ((bc + 1) & ~1) + sizeof (t_tpclnt);
    if ((bc == 0) && (last_bc == 0)) {  /* double tape mark? */
        had_double_tape_mark = objc;
        leot = tpos;
        }
    last_bc = bc;
    }
sim_debug (MTSE_DBG_STR, dptr, "tpc_map: objc: %u, different record sizes: %u\n", objc, sizec);
for (i=0; i<65535; i++) {
    if (countmap[i]) {
        if (i == 0)
            sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u tape marks\n", countmap[i]);
        else
            sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u %d byte record%s\n", countmap[i], (int)i, (countmap[i] > 1) ? "s" : "");
        }
    }
if (((last_bc != 0xffff) && 
     (tpos > tape_size) &&
     (!had_double_tape_mark))    ||
    (!had_double_tape_mark)      ||
    ((objc == countmap[0]) && 
     (countmap[0] != 2))) {     /* Unreasonable format? */
    if (last_bc != 0xffff)
        sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR unexpected EOT byte count: %d\n", last_bc);
    if (tpos > tape_size)
        sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR next record position %" T_ADDR_FMT "u beyond EOT: %" T_ADDR_FMT "u\n", tpos, tape_size);
    if (objc == countmap[0])
        sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR tape cnly contains tape marks\n");
    free (countmap);
    free (recbuf);
    return 0;
    }

if ((last_bc != 0xffff) && (tpos > tape_size)) {
    sim_debug (MTSE_DBG_STR, dptr, "tpc_map: WARNING unexpected EOT byte count: %d, double tape mark before %" T_ADDR_FMT "u provides logical EOT\n", last_bc, leot);
    objc = had_double_tape_mark;
    tpos = leot;
    }
if (map)
    map[objc] = tpos;
sim_debug (MTSE_DBG_STR, dptr, "tpc_map: OK objc: %d\n", objc);
free (countmap);
free (recbuf);
return objc;
}

/* Check the basic structure of a SIMH format tape image */

static t_stat sim_tape_simh_check (UNIT *uptr)
{
return SCPE_OK;
}

/* Check the basic structure of a E11 format tape image */

static t_stat sim_tape_e11_check (UNIT *uptr)
{
return SCPE_OK;
}

/* Find the preceding record in a TPC file */

static t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map)
{
uint32 lo, hi, p;


if (map == NULL)
    return 0;
lo = 0;
hi = uptr->hwmark - 1;
do {
    p = (lo + hi) >> 1;
    if (uptr->pos == map[p])
        return ((p == 0)? map[p]: map[p - 1]);
    else if (uptr->pos < map[p])
        hi = p - 1;
    else lo = p + 1;
    }
while (lo <= hi);
return ((p == 0)? map[p]: map[p - 1]);
}

/* Set tape capacity */

t_stat sim_tape_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
t_addr cap;
t_stat r;

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_ARG;
if (uptr->flags & UNIT_ATT)
    return SCPE_ALATT;
cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r);
if (r != SCPE_OK)
    return SCPE_ARG;
uptr->capac = cap * ((t_addr) 1000000);
return SCPE_OK;
}

/* Show tape capacity */

t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
if (uptr->capac) {
    if (uptr->capac >= (t_addr) 1000000)
        fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000)));
    else {
        if (uptr->capac >= (t_addr) 1000)
            fprintf (st, "capacity=%dKB", (uint32) (uptr->capac / ((t_addr) 1000)));
        else
            fprintf (st, "capacity=%dB", (uint32) uptr->capac);
        }
    }
else
    fprintf (st, "unlimited capacity");
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
   values.

   In either case, SCPE_ARG is returned if the density setting is not valid or
   allowed.  If the setting is OK, the new density is set into the unit
   structure, and SCPE_OK is returned.
*/

t_stat sim_tape_set_dens (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
uint32 density, new_bpi;
t_stat result = SCPE_OK;

if (uptr == NULL)                                               /* if the unit pointer is null */
    return SCPE_IERR;                                           /*   then the caller has screwed up */

else if (desc == NULL)                                          /* otherwise if a validation set was not supplied */
    if (val > 0 && val < (int32) BPI_COUNT)                     /*   then if a valid density code was supplied */
        uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK)     /*     then insert the code */
                           | (val << UNIT_V_DF_TAPE);           /*       in the unit flags */
    else                                                        /*   otherwise the code is invalid */
        return SCPE_ARG;                                        /*     so report a bad argument */

else {                                                          /* otherwise a validation set was supplied */
    if (cptr == NULL || *cptr == 0)                             /*   but if no value is present */
        return SCPE_MISVAL;                                     /*     then report a missing value */

    new_bpi = (uint32) get_uint (cptr, 10, UINT_MAX, &result);  /* convert the string value */

    if (result != SCPE_OK)                                      /* if the conversion failed */
        result = SCPE_ARG;                                      /*   then report a bad argument */

    else for (density = 0; density < BPI_COUNT; density++)      /* otherwise validate the density */
        if (new_bpi == bpi [density]                            /* if it matches a value in the list */
          && ((1 << density) & *(const int32 *) desc)) {        /*   and it's an allowed value */
            uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK) /*     then store the index of the value */
                               | density << UNIT_V_DF_TAPE;     /*       in the unit flags */
            return SCPE_OK;                                     /*         and return success */
            }

    result = SCPE_ARG;                                          /* if no match, then report a bad argument */
    }

return result;                                                  /* return the result of the operation */
}

/* Show the tape density */

t_stat sim_tape_show_dens (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
uint32 tape_density;

if (uptr == NULL)                                       /* if the unit pointer is null */
    return SCPE_IERR;                                   /*   then the caller has screwed up */

else {                                                  /* otherwise get the density */
    tape_density = bpi [MT_DENS (uptr->dynflags)];      /*   of the tape from the unit flags */

    if (tape_density)                                   /* if it's set */
        fprintf (st, "density=%d bpi", tape_density);   /*   then report it */
    else                                                /* otherwise */
        fprintf (st, "density not set");                /*   it was never set by the caller */
    }

return SCPE_OK;
}
Added src/sim_tape.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_tape.h: simulator tape support library definitions

   Copyright (c) 1993-2008, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   23-Jan-12    MP      Added support for Logical EOT detection while positioning
   05-Feb-11    MP      Add Asynch I/O support
   30-Aug-06    JDB     Added erase gap support
   14-Feb-06    RMS     Added variable tape capacity
   17-Dec-05    RMS     Added write support for Paul Pierce 7b format
   02-May-05    RMS     Added support for Paul Pierce 7b format
*/

#ifndef SIM_TAPE_H_
#define SIM_TAPE_H_    0

#ifdef  __cplusplus
extern "C" {
#endif

/* SIMH/E11 tape format */

typedef uint32          t_mtrlnt;                       /* magtape rec lnt */

#define MTR_TMK         0x00000000                      /* tape mark */
#define MTR_EOM         0xFFFFFFFF                      /* end of medium */
#define MTR_GAP         0xFFFFFFFE                      /* primary gap */
#define MTR_RRGAP       0xFFFFFFFF                      /* reverse read half gap */
#define MTR_FHGAP       0xFFFEFFFF                      /* fwd half gap (overwrite) */
#define MTR_RHGAP       0xFFFF0000                      /* rev half gap (overwrite) */
#define MTR_M_RHGAP     (~0x000080FF)                   /* range mask for rev gap */
#define MTR_MAXLEN      0x00FFFFFF                      /* max len is 24b */
#define MTR_ERF         0x80000000                      /* error flag */
#define MTR_F(x)        ((x) & MTR_ERF)                 /* record error flg */
#define MTR_L(x)        ((x) & ~MTR_ERF)                /* record length */

/* TPC tape format */

typedef uint16          t_tpclnt;                       /* magtape rec lnt */

/* P7B tape format */

#define P7B_SOR         0x80                            /* start of record */
#define P7B_PAR         0x40                            /* parity */
#define P7B_DATA        0x3F                            /* data */
#define P7B_DPAR        (P7B_PAR|P7B_DATA)              /* data and parity */
#define P7B_EOF         0x0F                            /* eof character */

#define TPC_TMK         0x0000                          /* tape mark */

/* Unit flags */

#define MTUF_V_PNU      (UNIT_V_UF + 0)                 /* position not upd */
#define MTUF_V_WLK      (UNIT_V_UF + 1)                 /* write locked */
#define MTUF_V_FMT      (UNIT_V_UF + 2)                 /* tape file format */
#define MTUF_W_FMT      3                               /* 3b of formats */
#define MTUF_N_FMT      (1u << MTUF_W_FMT)              /* number of formats */
#define MTUF_M_FMT      ((1u << MTUF_W_FMT) - 1)
#define MTUF_F_STD       0                              /* SIMH format */
#define MTUF_F_E11       1                              /* E11 format */
#define MTUF_F_TPC       2                              /* TPC format */
#define MTUF_F_P7B       3                              /* P7B format */
#define MUTF_F_TDF       4                              /* TDF format */
#define MTUF_V_UF       (MTUF_V_FMT + MTUF_W_FMT)
#define MTUF_PNU        (1u << MTUF_V_PNU)
#define MTUF_WLK        (1u << MTUF_V_WLK)
#define MTUF_FMT        (MTUF_M_FMT << MTUF_V_FMT)
#define MTUF_WRP        (MTUF_WLK | UNIT_RO)

#define MT_F_STD        (MTUF_F_STD << MTUF_V_FMT)
#define MT_F_E11        (MTUF_F_E11 << MTUF_V_FMT)
#define MT_F_TPC        (MTUF_F_TPC << MTUF_V_FMT)
#define MT_F_P7B        (MTUF_F_P7B << MTUF_V_FMT)
#define MT_F_TDF        (MTUF_F_TDF << MTUF_V_FMT)

#define MT_SET_PNU(u)   (u)->flags = (u)->flags | MTUF_PNU
#define MT_CLR_PNU(u)   (u)->flags = (u)->flags & ~MTUF_PNU
#define MT_TST_PNU(u)   ((u)->flags & MTUF_PNU)
#define MT_GET_FMT(u)   (((u)->flags >> MTUF_V_FMT) & MTUF_M_FMT)

/* sim_tape_position Position Flags */
#define MTPOS_V_REW     3
#define MTPOS_M_REW     (1u << MTPOS_V_REW)            /* Rewind First */
#define MTPOS_V_REV     2
#define MTPOS_M_REV     (1u << MTPOS_V_REV)            /* Reverse Direction */
#define MTPOS_V_OBJ     1
#define MTPOS_M_OBJ     (1u << MTPOS_V_OBJ)            /* Objects vs Records/Files */
#define MTPOS_V_DLE     4
#define MTPOS_M_DLE     (1u << MTPOS_V_DLE)            /* Detect LEOT */

/* Tape density values */

#define MT_DENS_NONE    0                               /* density not set */
#define MT_DENS_200     1                               /* 200 bpi NRZI */
#define MT_DENS_556     2                               /* 556 bpi NRZI */
#define MT_DENS_800     3                               /* 800 bpi NRZI */
#define MT_DENS_1600    4                               /* 1600 bpi PE */
#define MT_DENS_6250    5                               /* 6250 bpi GCR */

#define MTVF_DENS_MASK  (((1u << UNIT_S_DF_TAPE) - 1) << UNIT_V_DF_TAPE)
#define MT_DENS(f)      (((f) & MTVF_DENS_MASK) >> UNIT_V_DF_TAPE)

#define MT_NONE_VALID   (1u << MT_DENS_NONE)            /* density not set is valid */
#define MT_200_VALID    (1u << MT_DENS_200)             /* 200 bpi is valid */
#define MT_556_VALID    (1u << MT_DENS_556)             /* 556 bpi is valid */
#define MT_800_VALID    (1u << MT_DENS_800)             /* 800 bpi is valid */
#define MT_1600_VALID   (1u << MT_DENS_1600)            /* 1600 bpi is valid */
#define MT_6250_VALID   (1u << MT_DENS_6250)            /* 6250 bpi is valid */

/* Return status codes */

#define MTSE_OK         0                               /* no error */
#define MTSE_TMK        1                               /* tape mark */
#define MTSE_UNATT      2                               /* unattached */
#define MTSE_IOERR      3                               /* IO error */
#define MTSE_INVRL      4                               /* invalid rec lnt */
#define MTSE_FMT        5                               /* invalid format */
#define MTSE_BOT        6                               /* beginning of tape */
#define MTSE_EOM        7                               /* end of medium */
#define MTSE_RECE       8                               /* error in record */
#define MTSE_WRP        9                               /* write protected */
#define MTSE_LEOT       10                              /* Logical End Of Tape */
#define MTSE_RUNAWAY    11                              /* tape runaway */

typedef void (*TAPE_PCALLBACK)(UNIT *unit, t_stat status);

/* Tape Internal Debug flags */

#define MTSE_DBG_DAT   0x0400000                        /* Debug Data */
#define MTSE_DBG_POS   0x0800000                        /* Debug Positioning activities */
#define MTSE_DBG_STR   0x1000000                        /* Debug Tape Structure */

/* Prototypes */

t_stat sim_tape_attach_ex (UNIT *uptr, const char *cptr, uint32 dbit, int completion_delay);
t_stat sim_tape_attach (UNIT *uptr, CONST char *cptr);
t_stat sim_tape_detach (UNIT *uptr);
t_stat sim_tape_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max);
t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback);
t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max);
t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback);
t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc);
t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback);
t_stat sim_tape_wrtmk (UNIT *uptr);
t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback);
t_stat sim_tape_wreom (UNIT *uptr);
t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback);
t_stat sim_tape_wreomrw (UNIT *uptr);
t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback);
t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen);
t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback);
t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc);
t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback);
t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped);
t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback);
t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped);
t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback);
t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot);
t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot, TAPE_PCALLBACK callback);
t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc);
t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback);
t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped);
t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback);
t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped);
t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback);
t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped);
t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback);
t_stat sim_tape_rewind (UNIT *uptr);
t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback);
t_stat sim_tape_position (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recskipped, uint32 files, uint32 *fileskipped, uint32 *objectsskipped);
t_stat sim_tape_position_a (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback);
t_stat sim_tape_reset (UNIT *uptr);
t_bool sim_tape_bot (UNIT *uptr);
t_bool sim_tape_wrp (UNIT *uptr);
t_bool sim_tape_eot (UNIT *uptr);
t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat sim_tape_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat sim_tape_set_dens (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat sim_tape_show_dens (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat sim_tape_set_asynch (UNIT *uptr, int latency);
t_stat sim_tape_clr_asynch (UNIT *uptr);

#ifdef  __cplusplus
}
#endif

#endif
Added src/sim_timer.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_timer.c: simulator timer library

   Copyright (c) 1993-2010, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   21-Oct-11    MP      Fixed throttling in several ways:
                         - Sleep for the observed clock tick size while throttling
                         - Recompute the throttling wait once every 10 seconds
                           to account for varying instruction mixes during
                           different phases of a simulator execution or to 
                           accommodate the presence of other load on the host 
                           system.
                         - Each of the pre-existing throttling modes (Kcps, 
                           Mcps, and %) all compute the appropriate throttling 
                           interval dynamically.  These dynamic computations
                           assume that 100% of the host CPU is dedicated to 
                           the current simulator during this computation.
                           This assumption may not always be true and under 
                           certain conditions may never provide a way to 
                           correctly determine the appropriate throttling 
                           wait.  An additional throttling mode has been added
                           which allows the simulator operator to explicitly
                           state the desired throttling wait parameters.
                           These are specified by: 
                                  SET THROT insts/delay
                           where 'insts' is the number of instructions to 
                           execute before sleeping for 'delay' milliseconds.
   22-Apr-11    MP      Fixed Asynch I/O support to reasonably account cycles
                        when an idle wait is terminated by an external event
   05-Jan-11    MP      Added Asynch I/O support
   29-Dec-10    MP      Fixed clock resolution determination for Unix platforms
   22-Sep-08    RMS     Added "stability threshold" for idle routine
   27-May-08    RMS     Fixed bug in Linux idle routines (from Walter Mueller)
   18-Jun-07    RMS     Modified idle to exclude counted delays
   22-Mar-07    RMS     Added sim_rtcn_init_all
   17-Oct-06    RMS     Added idle support (based on work by Mark Pizzolato)
                        Added throttle support
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   02-Jan-04    RMS     Split out from SCP

   This library includes the following routines:

   sim_timer_init -         initialize timing system
   sim_rtc_init -           initialize calibration
   sim_rtc_calb -           calibrate clock
   sim_idle -               virtual machine idle
   sim_os_msec  -           return elapsed time in msec
   sim_os_sleep -           sleep specified number of seconds
   sim_os_ms_sleep -        sleep specified number of milliseconds
   sim_idle_ms_sleep -      sleep specified number of milliseconds
                            or until awakened by an asynchronous
                            event
   sim_timespec_diff        subtract two timespec values
   sim_timer_activate_after schedule unit for specific time
   sim_timer_activate_time  determine activation time
   sim_timer_activate_time_usecs determine activation time in usecs
   sim_rom_read_with_delay  delay for default or specified delay
   sim_get_rom_delay_factor get current or initialize 1usec delay factor
   sim_set_rom_delay_factor set specific delay factor


   The calibration, idle, and throttle routines are OS-independent; the _os_
   routines are not.
*/

#define NOT_MUX_USING_CODE /* sim_tmxr library provider or agnostic */

#include "sim_defs.h"
#include <ctype.h>
#include <math.h>

#define SIM_INTERNAL_CLK (SIM_NTIMERS+(1<<30))
#define SIM_INTERNAL_UNIT sim_internal_timer_unit
#ifndef MIN
#define MIN(a,b)  (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b)  (((a) > (b)) ? (a) : (b))
#endif

uint32 sim_idle_ms_sleep (unsigned int msec);

/* MS_MIN_GRANULARITY exists here so that timing behavior for hosts systems  */
/* with slow clock ticks can be assessed and tested without actually having  */
/* that slow a clock tick on the development platform                        */
//#define MS_MIN_GRANULARITY 20   /* Uncomment to simulate 20ms host tick size.*/
                                /* some Solaris and BSD hosts come this way  */

#if defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1)
uint32 real_sim_idle_ms_sleep (unsigned int msec);
uint32 real_sim_os_msec (void);
uint32 real_sim_os_ms_sleep (unsigned int msec);
static uint32 real_sim_os_sleep_min_ms = 0;
static uint32 real_sim_os_sleep_inc_ms = 0;

uint32 sim_idle_ms_sleep (unsigned int msec)
{
uint32 real_start = real_sim_os_msec ();
uint32 start = (real_start / MS_MIN_GRANULARITY) * MS_MIN_GRANULARITY;
uint32 tick_left;

if (msec == 0)
    return 0;
if (real_start == start)
    tick_left = 0;
else
    tick_left = MS_MIN_GRANULARITY - (real_start - start);
if (msec <= tick_left)
    real_sim_idle_ms_sleep (tick_left);
else
    real_sim_idle_ms_sleep (((msec + MS_MIN_GRANULARITY - 1) / MS_MIN_GRANULARITY) * MS_MIN_GRANULARITY);

return (sim_os_msec () - start);
}

uint32 sim_os_msec (void)
{
return (real_sim_os_msec ()/MS_MIN_GRANULARITY)*MS_MIN_GRANULARITY;
}

uint32 sim_os_ms_sleep (unsigned int msec)
{
msec = MS_MIN_GRANULARITY*((msec+MS_MIN_GRANULARITY-1)/MS_MIN_GRANULARITY);

return real_sim_os_ms_sleep (msec);
}

#endif /* defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) */

t_bool sim_idle_enab = FALSE;                       /* global flag */
volatile t_bool sim_idle_wait = FALSE;              /* global flag */

static int32 sim_calb_tmr = -1;                     /* the system calibrated timer */
static int32 sim_calb_tmr_last = -1;                /* shadow value when at sim> prompt */
static double sim_inst_per_sec_last = 0;            /* shadow value when at sim> prompt */

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 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;
static double sim_throt_inst_start;
static uint32 sim_throt_sleep_time = 0;
static int32 sim_throt_wait = 0;
static UNIT *sim_clock_unit[SIM_NTIMERS+1] = {NULL};
UNIT * volatile sim_clock_cosched_queue[SIM_NTIMERS+1] = {NULL};
static int32 sim_cosched_interval[SIM_NTIMERS+1];
static t_bool sim_catchup_ticks = TRUE;
#if defined (SIM_ASYNCH_CLOCKS) && !defined (SIM_ASYNCH_IO)
#undef SIM_ASYNCH_CLOCKS
#endif
t_bool sim_asynch_timer = FALSE;

#if defined (SIM_ASYNCH_CLOCKS)
UNIT * volatile sim_wallclock_queue = QUEUE_LIST_END;
UNIT * volatile sim_wallclock_entry = NULL;
#endif

#define sleep1Samples       100

static uint32 _compute_minimum_sleep (void)
{
uint32 i, tot, tim;

sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);
#if defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1)
real_sim_idle_ms_sleep (1);         /* Start sampling on a tick boundary */
for (i = 0, tot = 0; i < sleep1Samples; i++)
    tot += real_sim_idle_ms_sleep (1);
tim = tot / sleep1Samples;          /* Truncated average */
real_sim_os_sleep_min_ms = tim;
real_sim_idle_ms_sleep (1);         /* Start sampling on a tick boundary */
for (i = 0, tot = 0; i < sleep1Samples; i++)
    tot += real_sim_idle_ms_sleep (real_sim_os_sleep_min_ms + 1);
tim = tot / sleep1Samples;          /* Truncated average */
real_sim_os_sleep_inc_ms = tim - real_sim_os_sleep_min_ms;
#endif /* defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) */
sim_idle_ms_sleep (1);              /* Start sampling on a tick boundary */
for (i = 0, tot = 0; i < sleep1Samples; i++)
    tot += sim_idle_ms_sleep (1);
tim = tot / sleep1Samples;          /* Truncated average */
sim_os_sleep_min_ms = tim;
sim_idle_ms_sleep (1);              /* Start sampling on a tick boundary */
for (i = 0, tot = 0; i < sleep1Samples; i++)
    tot += sim_idle_ms_sleep (sim_os_sleep_min_ms + 1);
tim = tot / sleep1Samples;          /* Truncated average */
sim_os_sleep_inc_ms = tim - sim_os_sleep_min_ms;
sim_os_set_thread_priority (PRIORITY_NORMAL);
return sim_os_sleep_min_ms;
}

#if defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1)

#define sim_idle_ms_sleep   real_sim_idle_ms_sleep 
#define sim_os_msec         real_sim_os_msec 
#define sim_os_ms_sleep     real_sim_os_ms_sleep

#endif /* defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) */

#if defined(SIM_ASYNCH_IO)
uint32 sim_idle_ms_sleep (unsigned int msec)
{
uint32 start_time = sim_os_msec();
struct timespec done_time;
t_bool timedout = FALSE;

clock_gettime(CLOCK_REALTIME, &done_time);
done_time.tv_sec += (msec/1000);
done_time.tv_nsec += 1000000*(msec%1000);
if (done_time.tv_nsec > 1000000000) {
  done_time.tv_sec += done_time.tv_nsec/1000000000;
  done_time.tv_nsec = done_time.tv_nsec%1000000000;
  }
pthread_mutex_lock (&sim_asynch_lock);
sim_idle_wait = TRUE;
if (!pthread_cond_timedwait (&sim_asynch_wake, &sim_asynch_lock, &done_time))
  sim_asynch_check = 0;                 /* force check of asynch queue now */
else
  timedout = TRUE;
sim_idle_wait = FALSE;
pthread_mutex_unlock (&sim_asynch_lock);
if (!timedout) {
    AIO_UPDATE_QUEUE;
    }
return sim_os_msec() - start_time;
}
#else
uint32 sim_idle_ms_sleep (unsigned int msec)
{
return sim_os_ms_sleep (msec);
}
#endif

/* Mark the need for the sim_os_set_thread_priority routine, */
/* allowing the feature and/or platform dependent code to provide it */
#define NEED_THREAD_PRIORITY

/* If we've got pthreads support then use pthreads mechanisms */
#if defined(USE_READER_THREAD)

#undef NEED_THREAD_PRIORITY

#if defined(_WIN32)
/* On Windows there are several potentially disjoint threading APIs */
/* in use (base win32 pthreads, libSDL provided threading, and direct */
/* calls to beginthreadex), so go directly to the Win32 threading APIs */
/* to manage thread priority */
t_stat sim_os_set_thread_priority (int below_normal_above)
{
const static int val[3] = {THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL};

if ((below_normal_above < -1) || (below_normal_above > 1))
    return SCPE_ARG;
SetThreadPriority (GetCurrentThread(), val[1 + below_normal_above]);
return SCPE_OK;
}
#else
/* Native pthreads priority implementation */
t_stat sim_os_set_thread_priority (int below_normal_above)
{
int sched_policy, min_prio, max_prio;
struct sched_param sched_priority;

if ((below_normal_above < -1) || (below_normal_above > 1))
    return SCPE_ARG;

pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority);
min_prio = sched_get_priority_min(sched_policy);
max_prio = sched_get_priority_max(sched_policy);
switch (below_normal_above) {
    case PRIORITY_BELOW_NORMAL:
        sched_priority.sched_priority = min_prio;
        break;
    case PRIORITY_NORMAL:
        sched_priority.sched_priority = (max_prio + min_prio) / 2;
        break;
    case PRIORITY_ABOVE_NORMAL:
        sched_priority.sched_priority = max_prio;
        break;
    }
pthread_setschedparam (pthread_self(), sched_policy, &sched_priority);
return SCPE_OK;
}
#endif
#endif  /* defined(USE_READER_THREAD) */

/* OS-dependent timer and clock routines */

/* VMS */

#if defined (VMS)

#if defined (__VAX)
#define sys$gettim SYS$GETTIM
#define sys$setimr SYS$SETIMR
#define lib$emul LIB$EMUL
#define sys$waitfr SYS$WAITFR
#define lib$subx LIB$SUBX
#define lib$ediv LIB$EDIV
#endif

#include <starlet.h>
#include <lib$routines.h>
#include <unistd.h>

const t_bool rtc_avail = TRUE;

uint32 sim_os_msec (void)
{
uint32 quo, htod, tod[2];
int32 i;

sys$gettim (tod);                                       /* time 0.1usec */

/* To convert to msec, must divide a 64b quantity by 10000.  This is actually done
   by dividing the 96b quantity 0'time by 10000, producing 64b of quotient, the
   high 32b of which are discarded.  This can probably be done by a clever multiply...
*/

quo = htod = 0;
for (i = 0; i < 64; i++) {                              /* 64b quo */
    htod = (htod << 1) | ((tod[1] >> 31) & 1);          /* shift divd */
    tod[1] = (tod[1] << 1) | ((tod[0] >> 31) & 1);
    tod[0] = tod[0] << 1;
    quo = quo << 1;                                     /* shift quo */
    if (htod >= 10000) {                                /* divd work? */
        htod = htod - 10000;                            /* subtract */
        quo = quo | 1;                                  /* set quo bit */
        }
    }
return quo;
}

void sim_os_sleep (unsigned int sec)
{
sleep (sec);
return;
}

uint32 sim_os_ms_sleep_init (void)
{
return _compute_minimum_sleep ();
}

uint32 sim_os_ms_sleep (unsigned int msec)
{
uint32 stime = sim_os_msec ();
uint32 qtime[2];
int32 nsfactor = -10000;
static int32 zero = 0;

lib$emul (&msec, &nsfactor, &zero, qtime);
sys$setimr (2, qtime, 0, 0);
sys$waitfr (2);
return sim_os_msec () - stime;
}

#ifdef NEED_CLOCK_GETTIME
int clock_gettime(int clk_id, struct timespec *tp)
{
uint32 secs, ns, tod[2], unixbase[2] = {0xd53e8000, 0x019db1de};

if (clk_id != CLOCK_REALTIME)
  return -1;

sys$gettim (tod);                                       /* time 0.1usec */
lib$subx(tod, unixbase, tod);                           /* convert to unix base */
lib$ediv(&10000000, tod, &secs, &ns);                   /* isolate seconds & 100ns parts */
tp->tv_sec = secs;
tp->tv_nsec = ns*100;
return 0;
}
#endif /* CLOCK_REALTIME */

#elif defined (_WIN32)

/* Win32 routines */

const t_bool rtc_avail = TRUE;

uint32 sim_os_msec (void)
{
return timeGetTime ();
}

void sim_os_sleep (unsigned int sec)
{
Sleep (sec * 1000);
return;
}

void sim_timer_exit (void)
{
timeEndPeriod (sim_idle_rate_ms);
return;
}

uint32 sim_os_ms_sleep_init (void)
{
TIMECAPS timers;

if (timeGetDevCaps (&timers, sizeof (timers)) != TIMERR_NOERROR)
    return 0;
if (timers.wPeriodMin == 0)
    return 0;
if (timeBeginPeriod (timers.wPeriodMin) != TIMERR_NOERROR)
    return 0;
atexit (sim_timer_exit);
/* return measured actual minimum sleep time */
return _compute_minimum_sleep ();
}

uint32 sim_os_ms_sleep (unsigned int msec)
{
uint32 stime = sim_os_msec();

Sleep (msec);
return sim_os_msec () - stime;
}

#if defined(NEED_CLOCK_GETTIME)
int clock_gettime(int clk_id, struct timespec *tp)
{
t_uint64 now, unixbase;

if (clk_id != CLOCK_REALTIME)
    return -1;
unixbase = 116444736;
unixbase *= 1000000000;
GetSystemTimeAsFileTime((FILETIME*)&now);
now -= unixbase;
tp->tv_sec = (long)(now/10000000);
tp->tv_nsec = (now%10000000)*100;
return 0;
}
#endif

#elif defined (__OS2__)

/* OS/2 routines, from Bruce Ray */

const t_bool rtc_avail = FALSE;

uint32 sim_os_msec (void)
{
return 0;
}

void sim_os_sleep (unsigned int sec)
{
return;
}

uint32 sim_os_ms_sleep_init (void)
{
return 0;
}

uint32 sim_os_ms_sleep (unsigned int msec)
{
return 0;
}

/* Metrowerks CodeWarrior Macintosh routines, from Ben Supnik */

#elif defined (__MWERKS__) && defined (macintosh)

#include <Timer.h>
#include <Mactypes.h>
#include <sioux.h>
#include <unistd.h>
#include <siouxglobals.h>
#define NANOS_PER_MILLI     1000000
#define MILLIS_PER_SEC      1000

const t_bool rtc_avail = TRUE;

uint32 sim_os_msec (void)
{
unsigned long long micros;
UnsignedWide macMicros;
unsigned long millis;

Microseconds (&macMicros);
micros = *((unsigned long long *) &macMicros);
millis = micros / 1000LL;
return (uint32) millis;
}

void sim_os_sleep (unsigned int sec)
{
sleep (sec);
return;
}

uint32 sim_os_ms_sleep_init (void)
{
return _compute_minimum_sleep ();
}

uint32 sim_os_ms_sleep (unsigned int milliseconds)
{
uint32 stime = sim_os_msec ();
struct timespec treq;

treq.tv_sec = milliseconds / MILLIS_PER_SEC;
treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI;
(void) nanosleep (&treq, NULL);
return sim_os_msec () - stime;
}

#if defined(NEED_CLOCK_GETTIME)
int clock_gettime(int clk_id, struct timespec *tp)
{
struct timeval cur;

if (clk_id != CLOCK_REALTIME)
  return -1;
gettimeofday (&cur, NULL);
tp->tv_sec = cur.tv_sec;
tp->tv_nsec = cur.tv_usec*1000;
return 0;
}
#endif

#else

/* UNIX routines */

#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#define NANOS_PER_MILLI     1000000
#define MILLIS_PER_SEC      1000

const t_bool rtc_avail = TRUE;

uint32 sim_os_msec (void)
{
struct timeval cur;
struct timezone foo;
uint32 msec;

gettimeofday (&cur, &foo);
msec = (((uint32) cur.tv_sec) * 1000) + (((uint32) cur.tv_usec) / 1000);
return msec;
}

void sim_os_sleep (unsigned int sec)
{
sleep (sec);
return;
}

uint32 sim_os_ms_sleep_init (void)
{
return _compute_minimum_sleep ();
}

#if !defined(_POSIX_SOURCE)
#ifdef NEED_CLOCK_GETTIME
typedef int clockid_t;
int clock_gettime(clockid_t clk_id, struct timespec *tp)
{
struct timeval cur;
struct timezone foo;

if (clk_id != CLOCK_REALTIME)
  return -1;
gettimeofday (&cur, &foo);
tp->tv_sec = cur.tv_sec;
tp->tv_nsec = cur.tv_usec*1000;
return 0;
}
#endif /* CLOCK_REALTIME */
#endif /* !defined(_POSIX_SOURCE) && defined(SIM_ASYNCH_IO) */

uint32 sim_os_ms_sleep (unsigned int milliseconds)
{
uint32 stime = sim_os_msec ();
struct timespec treq;

treq.tv_sec = milliseconds / MILLIS_PER_SEC;
treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI;
(void) nanosleep (&treq, NULL);
return sim_os_msec () - stime;
}

#if defined(NEED_THREAD_PRIORITY)
#undef NEED_THREAD_PRIORITY
#include <sys/time.h>
#include <sys/resource.h>

t_stat sim_os_set_thread_priority (int below_normal_above)
{
if ((below_normal_above < -1) || (below_normal_above > 1))
    return SCPE_ARG;

errno = 0;
switch (below_normal_above) {
    case PRIORITY_BELOW_NORMAL:
        if ((getpriority (PRIO_PROCESS, 0) <= 0) &&     /* at or above normal pri? */
            (errno == 0))
            setpriority (PRIO_PROCESS, 0, 10);
        break;
    case PRIORITY_NORMAL:
        if (getpriority (PRIO_PROCESS, 0) != 0)         /* at or above normal pri? */
            setpriority (PRIO_PROCESS, 0, 0);
        break;
    case PRIORITY_ABOVE_NORMAL:
        if ((getpriority (PRIO_PROCESS, 0) <= 0) &&     /* at or above normal pri? */
            (errno == 0))
            setpriority (PRIO_PROCESS, 0, -10);
        break;
    }
return SCPE_OK;
}
#endif  /* defined(NEED_THREAD_PRIORITY) */

#endif

/* If one hasn't been provided yet, then just stub it */
#if defined(NEED_THREAD_PRIORITY)
t_stat sim_os_set_thread_priority (int below_normal_above)
{
return SCPE_OK;
}
#endif

#if defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1)
/* Make sure to use the substitute routines */
#undef sim_idle_ms_sleep
#undef sim_os_msec
#undef sim_os_ms_sleep
#endif /* defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) */

/* diff = min - sub */
void
sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub)
{
/* move the minuend value to the difference and operate there. */
*diff = *min;
/* Borrow as needed for the nsec value */
while (sub->tv_nsec > diff->tv_nsec) {
    --diff->tv_sec;
    diff->tv_nsec += 1000000000;
    }
diff->tv_nsec -= sub->tv_nsec;
diff->tv_sec -= sub->tv_sec;
/* Normalize the result */
while (diff->tv_nsec > 1000000000) {
    ++diff->tv_sec;
    diff->tv_nsec -= 1000000000;
    }
}

/* Forward declarations */

static double _timespec_to_double (struct timespec *time);
static void _double_to_timespec (struct timespec *time, double dtime);
static t_bool _rtcn_tick_catchup_check (int32 tmr, int32 time);
static void _rtcn_configure_calibrated_clock (int32 newtmr);
static t_bool _sim_coschedule_cancel (UNIT *uptr);
static t_bool _sim_wallclock_cancel (UNIT *uptr);
static t_bool _sim_wallclock_is_active (UNIT *uptr);
t_stat sim_timer_show_idle_mode (FILE* st, UNIT* uptr, int32 val, CONST void *  desc);


#if defined(SIM_ASYNCH_CLOCKS)
static int sim_timespec_compare (struct timespec *a, struct timespec *b)
{
while (a->tv_nsec > 1000000000) {
    a->tv_nsec -= 1000000000;
    ++a->tv_sec;
    }
while (b->tv_nsec > 1000000000) {
    b->tv_nsec -= 1000000000;
    ++b->tv_sec;
    }
if (a->tv_sec < b->tv_sec)
    return -1;
if (a->tv_sec > b->tv_sec)
    return 1;
if (a->tv_nsec < b->tv_nsec)
    return -1;
if (a->tv_nsec > b->tv_nsec)
    return 1;
else
    return 0;
}
#endif /* defined(SIM_ASYNCH_CLOCKS) */

/* OS independent clock calibration package */

static int32 rtc_ticks[SIM_NTIMERS+1] = { 0 };            /* ticks */
static uint32 rtc_hz[SIM_NTIMERS+1] = { 0 };              /* tick rate */
static uint32 rtc_last_hz[SIM_NTIMERS+1] = { 0 };         /* prior tick rate */
static uint32 rtc_rtime[SIM_NTIMERS+1] = { 0 };           /* real time */
static uint32 rtc_vtime[SIM_NTIMERS+1] = { 0 };           /* virtual time */
static double rtc_gtime[SIM_NTIMERS+1] = { 0 };           /* instruction time */
static uint32 rtc_nxintv[SIM_NTIMERS+1] = { 0 };          /* next interval */
static int32 rtc_based[SIM_NTIMERS+1] = { 0 };            /* base delay */
static int32 rtc_currd[SIM_NTIMERS+1] = { 0 };            /* current delay */
static int32 rtc_initd[SIM_NTIMERS+1] = { 0 };            /* initial delay */
static uint32 rtc_elapsed[SIM_NTIMERS+1] = { 0 };         /* sec since init */
static uint32 rtc_calibrations[SIM_NTIMERS+1] = { 0 };    /* calibration count */
static double rtc_clock_skew_max[SIM_NTIMERS+1] = { 0 };  /* asynchronous max skew */
static double rtc_clock_start_gtime[SIM_NTIMERS+1] = { 0 };/* reference instruction time for clock */
static double rtc_clock_tick_size[SIM_NTIMERS+1] = { 0 }; /* 1/hz */
static uint32 rtc_calib_initializations[SIM_NTIMERS+1] = { 0 };/* Initialization Count */
static double rtc_calib_tick_time[SIM_NTIMERS+1] = { 0 }; /* ticks time */
static double rtc_calib_tick_time_tot[SIM_NTIMERS+1] = { 0 };/* ticks time - total*/
static uint32 rtc_calib_ticks_acked[SIM_NTIMERS+1] = { 0 };/* ticks Acked */
static uint32 rtc_calib_ticks_acked_tot[SIM_NTIMERS+1] = { 0 };/* ticks Acked - total */
static uint32 rtc_clock_ticks[SIM_NTIMERS+1] = { 0 };/* ticks delivered since catchup base */
static uint32 rtc_clock_ticks_tot[SIM_NTIMERS+1] = { 0 };/* ticks delivered since catchup base - total */
static double rtc_clock_init_base_time[SIM_NTIMERS+1] = { 0 };/* reference time for clock initialization */
static double rtc_clock_tick_start_time[SIM_NTIMERS+1] = { 0 };/* reference time when ticking started */
static double rtc_clock_catchup_base_time[SIM_NTIMERS+1] = { 0 };/* reference time for catchup ticks */
static uint32 rtc_clock_catchup_ticks[SIM_NTIMERS+1] = { 0 };/* Record of catchups */
static uint32 rtc_clock_catchup_ticks_tot[SIM_NTIMERS+1] = { 0 };/* Record of catchups - total */
static t_bool rtc_clock_catchup_pending[SIM_NTIMERS+1] = { 0 };/* clock tick catchup pending */
static t_bool rtc_clock_catchup_eligible[SIM_NTIMERS+1] = { 0 };/* clock tick catchup eligible */
static uint32 rtc_clock_time_idled[SIM_NTIMERS+1] = { 0 };/* total time idled */
static uint32 rtc_clock_time_idled_last[SIM_NTIMERS+1] = { 0 };/* total time idled */
static uint32 rtc_clock_calib_skip_idle[SIM_NTIMERS+1] = { 0 };/* Calibrations skipped due to idling */
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_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);

#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*/
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"},
  {"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;


void sim_rtcn_init_all (void)
{
int32 tmr;

for (tmr = 0; tmr <= SIM_NTIMERS; tmr++)
    if (rtc_initd[tmr] != 0)
        sim_rtcn_init (rtc_initd[tmr], tmr);
return;
}

int32 sim_rtcn_init (int32 time, int32 tmr)
{
return sim_rtcn_init_unit (NULL, time, tmr);
}

int32 sim_rtcn_init_unit (UNIT *uptr, int32 time, int32 tmr)
{
if (time == 0)
    time = 1;
if (tmr == SIM_INTERNAL_CLK)
    tmr = SIM_NTIMERS;
else {
    if ((tmr < 0) || (tmr >= SIM_NTIMERS))
        return time;
    }
/*
 * If we'd previously succeeded in calibrating a tick value, then use that
 * delay as a better default to setup when we're re-initialized.
 * Re-initializing happens on any boot or after any breakpoint/continue.
 */
if (rtc_currd[tmr])
    time = rtc_currd[tmr];
if (!uptr)
    uptr = sim_clock_unit[tmr];
sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_init_unit(unit=%s, time=%d, tmr=%d)\n", sim_uname(uptr), time, tmr);
if (uptr) {
    if (!sim_clock_unit[tmr])
        sim_register_clock_unit_tmr (uptr, tmr);
    }
rtc_clock_start_gtime[tmr] = sim_gtime();
rtc_rtime[tmr] = sim_os_msec ();
rtc_vtime[tmr] = rtc_rtime[tmr];
rtc_nxintv[tmr] = 1000;
rtc_ticks[tmr] = 0;
rtc_last_hz[tmr] = rtc_hz[tmr];
rtc_hz[tmr] = 0;
rtc_based[tmr] = time;
rtc_currd[tmr] = time;
rtc_initd[tmr] = time;
rtc_elapsed[tmr] = 0;
rtc_calibrations[tmr] = 0;
rtc_clock_ticks_tot[tmr] += rtc_clock_ticks[tmr];
rtc_clock_ticks[tmr] = 0;
rtc_calib_tick_time_tot[tmr] += rtc_calib_tick_time[tmr];
rtc_calib_tick_time[tmr] = 0;
rtc_clock_catchup_pending[tmr] = FALSE;
rtc_clock_catchup_eligible[tmr] = FALSE;
rtc_clock_catchup_ticks_tot[tmr] += rtc_clock_catchup_ticks[tmr];
rtc_clock_catchup_ticks[tmr] = 0;
rtc_calib_ticks_acked_tot[tmr] += rtc_calib_ticks_acked[tmr];
rtc_calib_ticks_acked[tmr] = 0;
++rtc_calib_initializations[tmr];
rtc_clock_init_base_time[tmr] = sim_timenow_double ();
_rtcn_configure_calibrated_clock (tmr);
return time;
}

int32 sim_rtcn_calb (int32 ticksper, int32 tmr)
{
uint32 new_rtime, delta_rtime, last_idle_pct;
int32 delta_vtime;
double new_gtime;
int32 new_currd;
int32 itmr;

if (tmr == SIM_INTERNAL_CLK)
    tmr = SIM_NTIMERS;
else {
    if ((tmr < 0) || (tmr >= SIM_NTIMERS))
        return 10000;
    }
if (rtc_hz[tmr] != ticksper) {                          /* changing tick rate? */
    if (rtc_hz[tmr] == 0)
        rtc_clock_tick_start_time[tmr] = sim_timenow_double ();
    rtc_last_hz[tmr] = rtc_hz[tmr];
    rtc_hz[tmr] = ticksper;
    _rtcn_configure_calibrated_clock (tmr);
    if (ticksper != 0) {
        rtc_clock_tick_size[tmr] = 1.0 / ticksper;
        rtc_currd[tmr] = (int32)(sim_timer_inst_per_sec () / ticksper);
        }
    }
if (ticksper == 0)                                      /* running? */
    return 10000;
if (sim_clock_unit[tmr] == NULL) {                      /* Not using TIMER units? */
    rtc_clock_ticks[tmr] += 1;
    rtc_calib_tick_time[tmr] += rtc_clock_tick_size[tmr];
    }
if (rtc_clock_catchup_pending[tmr]) {                   /* catchup tick? */
    ++rtc_clock_catchup_ticks[tmr];                     /* accumulating which were catchups */
    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]);
    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) */
    /* Wrapping will happen initially sometime before a simulator has been running */
    /* for 49 days approximately every 49 days thereafter. */
    ++rtc_clock_calib_backwards[tmr];                   /* Count statistic */
    sim_debug (DBG_CAL, &sim_timer_dev, "time running backwards - OldTime: %u, NewTime: %u, result: %d\n", rtc_rtime[tmr], new_rtime, rtc_currd[tmr]);
    rtc_rtime[tmr] = new_rtime;                         /* reset wall time */
    return rtc_currd[tmr];                              /* can't calibrate */
    }
delta_rtime = new_rtime - rtc_rtime[tmr];               /* elapsed wtime */
rtc_rtime[tmr] = new_rtime;                             /* adv wall time */
rtc_vtime[tmr] = rtc_vtime[tmr] + 1000;                 /* adv sim time */
if (delta_rtime > 30000) {                              /* gap too big? */
    /* This simulator process has somehow been suspended for a significant */
    /* amount of time.  This will certainly happen if the host system has  */
    /* slept or hibernated.  It also might happen when a simulator         */
    /* developer stops the simulator at a breakpoint (a process, not simh  */
    /* breakpoint).  To accomodate this, we set the calibration state to   */
    /* ignore what happened and proceed from here.                         */
    ++rtc_clock_calib_gap2big[tmr];                     /* Count statistic */
    rtc_vtime[tmr] = rtc_rtime[tmr];                    /* sync virtual and real time */
    rtc_nxintv[tmr] = 1000;                             /* reset next interval */
    rtc_gtime[tmr] = sim_gtime();                       /* save instruction time */
    sim_debug (DBG_CAL, &sim_timer_dev, "gap too big: delta = %d - result: %d\n", delta_rtime, rtc_currd[tmr]);
    return rtc_currd[tmr];                              /* can't calibr */
    }
if (delta_rtime == 0)                                   /* avoid divide by zero  */
    last_idle_pct = 0;                                  /* force calibration */
else
    last_idle_pct = MIN(100, (uint32)(100.0 * (((double)(rtc_clock_time_idled[tmr] - rtc_clock_time_idled_last[tmr])) / ((double)delta_rtime))));
rtc_clock_time_idled_last[tmr] = rtc_clock_time_idled[tmr];
if (last_idle_pct > (100 - sim_idle_calib_pct)) {
    rtc_rtime[tmr] = new_rtime;                         /* save wall time */
    rtc_vtime[tmr] = rtc_vtime[tmr] + 1000;             /* adv sim time */
    rtc_gtime[tmr] = sim_gtime();                       /* save instruction time */
    ++rtc_clock_calib_skip_idle[tmr];
    sim_debug (DBG_CAL, &sim_timer_dev, "skipping calibration due to idling (%d%%) - result: %d\n", last_idle_pct, rtc_currd[tmr]);
    return rtc_currd[tmr];                              /* avoid calibrating idle checks */
    }
new_gtime = sim_gtime();
if ((last_idle_pct == 0) && (delta_rtime != 0))
    sim_idle_cyc_ms = (uint32)((new_gtime - rtc_gtime[tmr]) / delta_rtime);
if (sim_asynch_timer) {
    /* An asynchronous clock, merely needs to divide the number of */
    /* instructions actually executed by the clock rate. */
    new_currd = (int32)((new_gtime - rtc_gtime[tmr])/ticksper);
    /* avoid excessive swings in the calibrated result */
    if (new_currd > 10*rtc_currd[tmr])              /* don't swing big too fast */
        new_currd = 10*rtc_currd[tmr];
    else
        if (new_currd < rtc_currd[tmr]/10)          /* don't swing small too fast */
            new_currd = rtc_currd[tmr]/10;
    rtc_currd[tmr] = new_currd;
    rtc_gtime[tmr] = new_gtime;                     /* save instruction time */
    sim_debug (DBG_CAL, &sim_timer_dev, "asynch calibration result: %d\n", rtc_currd[tmr]);
    return rtc_currd[tmr];                          /* calibrated result */
    }
rtc_gtime[tmr] = new_gtime;                             /* save instruction time */
/* This self regulating algorithm depends directly on the assumption */
/* that this routine is called back after processing the number of */
/* instructions which was returned the last time it was called. */
if (delta_rtime == 0)                                   /* gap too small? */
    rtc_based[tmr] = rtc_based[tmr] * ticksper;         /* slew wide */
else
    rtc_based[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) /
                                ((double) delta_rtime));/* new base rate */
delta_vtime = rtc_vtime[tmr] - rtc_rtime[tmr];          /* gap */
if (delta_vtime > SIM_TMAX)                             /* limit gap */
    delta_vtime = SIM_TMAX;
else if (delta_vtime < -SIM_TMAX)
    delta_vtime = -SIM_TMAX;
rtc_nxintv[tmr] = 1000 + delta_vtime;                   /* next wtime */
rtc_currd[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) /
    1000.0);                                            /* next delay */
if (rtc_based[tmr] <= 0)                                /* never negative or zero! */
    rtc_based[tmr] = 1;
if (rtc_currd[tmr] <= 0)                                /* never negative or zero! */
    rtc_currd[tmr] = 1;
sim_debug (DBG_CAL, &sim_timer_dev, "calibrated tmr=%d, tickper=%d (base=%d, nxintv=%u, result: %d)\n", tmr, ticksper, rtc_based[tmr], rtc_nxintv[tmr], rtc_currd[tmr]);
/* Adjust calibration for other timers which depend on this timer's calibration */
for (itmr=0; itmr<=SIM_NTIMERS; itmr++)
    if ((itmr != tmr) && (rtc_hz[itmr] != 0))
        rtc_currd[itmr] = (rtc_currd[tmr] * ticksper) / rtc_hz[itmr];
AIO_SET_INTERRUPT_LATENCY(rtc_currd[tmr] * ticksper);   /* set interrrupt latency */
return rtc_currd[tmr];
}

/* Prior interfaces - default to timer 0 */

int32 sim_rtc_init (int32 time)
{
return sim_rtcn_init (time, 0);
}

int32 sim_rtc_calb (int32 ticksper)
{
return sim_rtcn_calb (ticksper, 0);
}

/* sim_timer_init - get minimum sleep time available on this host */

t_bool sim_timer_init (void)
{
int tmr;
uint32 clock_start, clock_last, clock_now;

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_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 */

clock_last = clock_start = sim_os_msec ();
sim_os_clock_resoluton_ms = 1000;
do {
    uint32 clock_diff;
    
    clock_now = sim_os_msec ();
    clock_diff = clock_now - clock_last;
    if ((clock_diff > 0) && (clock_diff < sim_os_clock_resoluton_ms))
        sim_os_clock_resoluton_ms = clock_diff;
    clock_last = clock_now;
    } while (clock_now < clock_start + 100);
sim_os_tick_hz = 1000/(sim_os_clock_resoluton_ms * (sim_idle_rate_ms/sim_os_clock_resoluton_ms));
return (sim_idle_rate_ms != 0);
}

/* sim_timer_idle_capable - tell if the host is Idle capable and what the host OS tick size is */

t_bool sim_timer_idle_capable (uint32 *host_ms_sleep_1, uint32 *host_tick_ms)
{
if (host_tick_ms)
    *host_tick_ms = sim_os_clock_resoluton_ms;
if (host_ms_sleep_1)
    *host_ms_sleep_1 = sim_os_sleep_min_ms;
return (sim_idle_rate_ms != 0);
}

/* sim_show_timers - show running timer information */
t_stat sim_show_timers (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc)
{
int tmr, clocks;
struct timespec now;
time_t time_t_now;
int32 calb_tmr = (sim_calb_tmr == -1) ? sim_calb_tmr_last : sim_calb_tmr;
double inst_per_sec = sim_timer_inst_per_sec ();

fprintf (st, "Minimum Host Sleep Time:       %d ms (%dHz)\n", sim_os_sleep_min_ms, sim_os_tick_hz);
if (sim_os_sleep_min_ms != sim_os_sleep_inc_ms)
    fprintf (st, "Minimum Host Sleep Incr Time:  %d ms\n", sim_os_sleep_inc_ms);
fprintf (st, "Host Clock Resolution:         %d ms\n", sim_os_clock_resoluton_ms);
fprintf (st, "Execution Rate:                %s instructions/sec\n", sim_fmt_numeric (inst_per_sec));
if (sim_idle_enab) {
    fprintf (st, "Idling:                        Enabled\n");
    fprintf (st, "Time before Idling starts:     %d seconds\n", sim_idle_stable);
    }
if (sim_throt_type != SIM_THROT_NONE) {
    sim_show_throt (st, NULL, uptr, val, desc);
    }
fprintf (st, "Calibrated Timer:              %s\n", (calb_tmr == -1) ? "Undetermined" : 
                                                    ((calb_tmr == SIM_NTIMERS) ? "Internal Timer" : 
                                                    (sim_clock_unit[calb_tmr] ? sim_uname(sim_clock_unit[calb_tmr]) : "")));
if (calb_tmr == SIM_NTIMERS)
    fprintf (st, "Catchup Ticks:                 %s for clocks ticking faster than %d Hz\n", sim_catchup_ticks ? "Enabled" : "Disabled", sim_os_tick_hz);
if (sim_idle_calib_pct == 0)
    fprintf (st, "Calibration:                   Always\n");
else
    fprintf (st, "Calibration:                   Skipped when Idle exceeds %d%%\n", sim_idle_calib_pct);
fprintf (st, "\n");
for (tmr=clocks=0; tmr<=SIM_NTIMERS; ++tmr) {
    if (0 == rtc_initd[tmr])
        continue;
    
    if (sim_clock_unit[tmr]) {
        ++clocks;
        fprintf (st, "%s clock device is %s%s%s\n", sim_name, 
                                                    (tmr == SIM_NTIMERS) ? "Internal Calibrated Timer(" : "", 
                                                    sim_uname(sim_clock_unit[tmr]), 
                                                    (tmr == SIM_NTIMERS) ? ")" : "");
        }

    fprintf (st, "%s%sTimer %d:\n", sim_asynch_timer ? "Asynchronous " : "", rtc_hz[tmr] ? "Calibrated " : "Uncalibrated ", tmr);
    if (rtc_hz[tmr]) {
        fprintf (st, "  Running at:                %d Hz\n", rtc_hz[tmr]);
        fprintf (st, "  Tick Size:                 %s\n", sim_fmt_secs (rtc_clock_tick_size[tmr]));
        fprintf (st, "  Ticks in current second:   %d\n",   rtc_ticks[tmr]);
        }
    fprintf (st, "  Seconds Running:           %s (%s)\n",   sim_fmt_numeric ((double)rtc_elapsed[tmr]), sim_fmt_secs ((double)rtc_elapsed[tmr]));
    if (tmr == calb_tmr) {
        fprintf (st, "  Calibration Opportunities: %s\n",   sim_fmt_numeric ((double)rtc_calibrations[tmr]));
        if (sim_idle_calib_pct)
            fprintf (st, "  Calib Skip Idle Thresh %%:  %u\n",   sim_idle_calib_pct);
        if (rtc_clock_calib_skip_idle[tmr])
            fprintf (st, "  Calibs Skip While Idle:    %u\n",   rtc_clock_calib_skip_idle[tmr]);
        if (rtc_clock_calib_backwards[tmr])
            fprintf (st, "  Calibs Skip Backwards:     %u\n",   rtc_clock_calib_backwards[tmr]);
        if (rtc_clock_calib_gap2big[tmr])
            fprintf (st, "  Calibs Skip Gap Too Big:   %u\n",   rtc_clock_calib_gap2big[tmr]);
        }
    if (rtc_gtime[tmr])
        fprintf (st, "  Instruction Time:          %.0f\n", rtc_gtime[tmr]);
    if ((!sim_asynch_timer) && (sim_throt_type == SIM_THROT_NONE)) {
        fprintf (st, "  Real Time:                 %u\n",   rtc_rtime[tmr]);
        fprintf (st, "  Virtual Time:              %u\n",   rtc_vtime[tmr]);
        fprintf (st, "  Next Interval:             %s\n",   sim_fmt_numeric ((double)rtc_nxintv[tmr]));
        fprintf (st, "  Base Tick Delay:           %s\n",   sim_fmt_numeric ((double)rtc_based[tmr]));
        fprintf (st, "  Initial Insts Per Tick:    %s\n",   sim_fmt_numeric ((double)rtc_initd[tmr]));
        }
    fprintf (st, "  Current Insts Per Tick:    %s\n",   sim_fmt_numeric ((double)rtc_currd[tmr]));
    fprintf (st, "  Initializations:           %d\n",   rtc_calib_initializations[tmr]);
    fprintf (st, "  Ticks:                     %s\n", sim_fmt_numeric ((double)(rtc_clock_ticks[tmr])));
    if (rtc_clock_ticks_tot[tmr]+rtc_clock_ticks[tmr] != rtc_clock_ticks[tmr])
        fprintf (st, "  Total Ticks:               %s\n", sim_fmt_numeric ((double)(rtc_clock_ticks_tot[tmr]+rtc_clock_ticks[tmr])));
    if (rtc_clock_skew_max[tmr] != 0.0)
        fprintf (st, "  Peak Clock Skew:           %s%s\n", sim_fmt_secs (fabs(rtc_clock_skew_max[tmr])), (rtc_clock_skew_max[tmr] < 0) ? " fast" : " slow");
    if (rtc_calib_ticks_acked[tmr])
        fprintf (st, "  Ticks Acked:               %s\n",   sim_fmt_numeric ((double)rtc_calib_ticks_acked[tmr]));
    if (rtc_calib_ticks_acked_tot[tmr]+rtc_calib_ticks_acked[tmr] != rtc_calib_ticks_acked[tmr])
        fprintf (st, "  Total Ticks Acked:         %s\n",   sim_fmt_numeric ((double)(rtc_calib_ticks_acked_tot[tmr]+rtc_calib_ticks_acked[tmr])));
    if (rtc_calib_tick_time[tmr])
        fprintf (st, "  Tick Time:                 %s\n",   sim_fmt_secs (rtc_calib_tick_time[tmr]));
    if (rtc_calib_tick_time_tot[tmr]+rtc_calib_tick_time[tmr] != rtc_calib_tick_time[tmr])
        fprintf (st, "  Total Tick Time:           %s\n",   sim_fmt_secs (rtc_calib_tick_time_tot[tmr]+rtc_calib_tick_time[tmr]));
    if (rtc_clock_catchup_ticks[tmr])
        fprintf (st, "  Catchup Ticks Sched:       %s\n",   sim_fmt_numeric ((double)rtc_clock_catchup_ticks[tmr]));
    if (rtc_clock_catchup_ticks_tot[tmr]+rtc_clock_catchup_ticks[tmr] != rtc_clock_catchup_ticks[tmr])
        fprintf (st, "  Total Catchup Ticks Sched: %s\n",   sim_fmt_numeric ((double)(rtc_clock_catchup_ticks_tot[tmr]+rtc_clock_catchup_ticks[tmr])));
    if (rtc_clock_init_base_time[tmr]) {
        _double_to_timespec (&now, rtc_clock_init_base_time[tmr]);
        time_t_now = (time_t)now.tv_sec;
        fprintf (st, "  Initialize Base Time:      %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000));
        }
    if (rtc_clock_tick_start_time[tmr]) {
        _double_to_timespec (&now, rtc_clock_tick_start_time[tmr]);
        time_t_now = (time_t)now.tv_sec;
        fprintf (st, "  Tick Start Time:           %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000));
        }
    clock_gettime (CLOCK_REALTIME, &now);
    time_t_now = (time_t)now.tv_sec;
    fprintf (st, "  Wall Clock Time Now:       %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000));
    if (rtc_clock_catchup_eligible[tmr]) {
        _double_to_timespec (&now, rtc_clock_catchup_base_time[tmr]+rtc_calib_tick_time[tmr]);
        time_t_now = (time_t)now.tv_sec;
        fprintf (st, "  Catchup Tick Time:         %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000));
        _double_to_timespec (&now, rtc_clock_catchup_base_time[tmr]);
        time_t_now = (time_t)now.tv_sec;
        fprintf (st, "  Catchup Base Time:         %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000));
        }
    if (rtc_clock_time_idled[tmr])
        fprintf (st, "  Total Time Idled:          %s\n",   sim_fmt_secs (rtc_clock_time_idled[tmr]/1000.0));
    }
if (clocks == 0)
    fprintf (st, "%s clock device is not specified, co-scheduling is unavailable\n", sim_name);
return SCPE_OK;
}

t_stat sim_show_clock_queues (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
{
int tmr;

#if defined (SIM_ASYNCH_CLOCKS)
pthread_mutex_lock (&sim_timer_lock);
if (sim_asynch_timer) {
    const char *tim;
    struct timespec due;
    time_t time_t_due;

    if (sim_wallclock_queue == QUEUE_LIST_END)
        fprintf (st, "%s wall clock event queue empty\n", sim_name);
    else {
        fprintf (st, "%s wall clock event queue status\n", sim_name);
        for (uptr = sim_wallclock_queue; uptr != QUEUE_LIST_END; uptr = uptr->a_next) {
            if ((dptr = find_dev_from_unit (uptr)) != NULL) {
                fprintf (st, "  %s", sim_dname (dptr));
                if (dptr->numunits > 1)
                    fprintf (st, " unit %d", (int32) (uptr - dptr->units));
                }
            else
                fprintf (st, "  Unknown");
            tim = sim_fmt_secs(uptr->a_usec_delay/1000000.0);
            _double_to_timespec (&due, uptr->a_due_time);
            time_t_due = (time_t)due.tv_sec;
            fprintf (st, " after %s due at %8.8s.%06d\n", tim, 11+ctime(&time_t_due), (int)(due.tv_nsec/1000));
            }
        }
    }
#endif /* SIM_ASYNCH_CLOCKS */
for (tmr=0; tmr<=SIM_NTIMERS; ++tmr) {
    if (sim_clock_unit[tmr] == NULL)
        continue;
    if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) {
        int32 accum;

        fprintf (st, "%s clock (%s) co-schedule event queue status\n",
                 sim_name, sim_uname(sim_clock_unit[tmr]));
        accum = 0;
        for (uptr = sim_clock_cosched_queue[tmr]; uptr != QUEUE_LIST_END; uptr = uptr->next) {
            if ((dptr = find_dev_from_unit (uptr)) != NULL) {
                fprintf (st, "  %s", sim_dname (dptr));
                if (dptr->numunits > 1)
                    fprintf (st, " unit %d", (int32) (uptr - dptr->units));
                }
            else
                fprintf (st, "  Unknown");
            if (accum == 0)
                fprintf (st, " on next tick");
            else
                fprintf (st, " after %d tick%s", accum, (accum > 1) ? "s" : "");
            if (uptr->usecs_remaining)
                fprintf (st, " plus %.0f usecs", uptr->usecs_remaining);
            fprintf (st, "\n");
            accum = accum + uptr->time;
            }
        }
    }
#if defined (SIM_ASYNCH_IO)
pthread_mutex_unlock (&sim_timer_lock);
#endif /* SIM_ASYNCH_IO */
return SCPE_OK;
}

REG sim_timer_reg[] = {
    { DRDATAD (IDLE_CYC_MS,      sim_idle_cyc_ms,        32, "Cycles Per Millisecond"), PV_RSPC|REG_RO},
    { DRDATAD (ROM_DELAY,        sim_rom_delay,          32, "ROM memory reference delay"), PV_RSPC|REG_RO},
    { NULL }
    };

REG sim_throttle_reg[] = {
    { DRDATAD (THROT_MS_START,   sim_throt_ms_start,     32, ""), PV_RSPC|REG_RO},
    { DRDATAD (THROT_MS_STOP,    sim_throt_ms_stop,      32, ""), PV_RSPC|REG_RO},
    { DRDATAD (THROT_TYPE,       sim_throt_type,         32, ""), PV_RSPC|REG_RO},
    { DRDATAD (THROT_VAL,        sim_throt_val,          32, ""), PV_RSPC|REG_RO},
    { DRDATAD (THROT_STATE,      sim_throt_state,        32, ""), PV_RSPC|REG_RO},
    { DRDATAD (THROT_SLEEP_TIME, sim_throt_sleep_time,   32, ""), PV_RSPC|REG_RO},
    { DRDATAD (THROT_WAIT,       sim_throt_wait,         32, ""), PV_RSPC|REG_RO},
    { NULL }
    };

/* Clear, Set and show catchup */

/* Set/Clear catchup */

t_stat sim_timer_set_catchup (int32 flag, CONST char *cptr)
{
if (flag) {
    if (!sim_catchup_ticks)
        sim_catchup_ticks = TRUE;
    }
else {
    if (sim_catchup_ticks)
        sim_catchup_ticks = FALSE;
    }
return SCPE_OK;
}

t_stat sim_timer_show_catchup (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fprintf (st, "Calibrated Ticks%s", sim_catchup_ticks ? " with Catchup Ticks" : "");
return SCPE_OK;
}

/* Set idle calibration threshold */

t_stat sim_timer_set_idle_pct (int32 flag, CONST char *cptr)
{
t_stat r;
int32 newpct;

if (cptr == NULL)
    return SCPE_ARG;
newpct = (int32) get_uint (cptr, 10, 100, &r);
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/Clear asynch */

t_stat sim_timer_set_async (int32 flag, CONST char *cptr)
{
if (flag) {
    if (sim_asynch_enabled && (!sim_asynch_timer)) {
        sim_asynch_timer = TRUE;
        sim_timer_change_asynch ();
        }
    }
else {
    if (sim_asynch_timer) {
        sim_asynch_timer = FALSE;
        sim_timer_change_asynch ();
        }
    }
return SCPE_OK;
}

static CTAB set_timer_tab[] = {
#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 },
    { 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_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};

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_throttle_dev = {
    "INT-THROTTLE", &sim_throttle_unit, sim_throttle_reg, NULL, 1};


/* SET CLOCK command */

t_stat sim_set_timers (int32 arg, CONST char *cptr)
{
char *cvptr, gbuf[CBUFSIZE];
CTAB *ctptr;
t_stat r;

if ((cptr == NULL) || (*cptr == 0))
    return SCPE_2FARG;
while (*cptr != 0) {                                    /* do all mods */
    cptr = get_glyph_nc (cptr, gbuf, ',');              /* get modifier */
    if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
        *cvptr++ = 0;
    get_glyph (gbuf, gbuf, 0);                          /* modifier to UC */
    if ((ctptr = find_ctab (set_timer_tab, gbuf))) {    /* match? */
        r = ctptr->action (ctptr->arg, cvptr);          /* do the rest */
        if (r != SCPE_OK)
            return r;
        }
    else return SCPE_NOPARAM;
    }
return SCPE_OK;
}

/* sim_idle - idle simulator until next event or for specified interval

   Inputs:
        tmr =   calibrated timer to use

   Must solve the linear equation

        ms_to_wait = w * ms_per_wait

   Or
        w = ms_to_wait / ms_per_wait
*/

t_bool sim_idle (uint32 tmr, int sin_cyc)
{
uint32 w_ms, w_idle, act_ms;
int32 act_cyc;

if (rtc_clock_catchup_pending[tmr]) {                   /* Catchup clock tick pending? */
    sim_debug (DBG_CAL, &sim_timer_dev, "sim_idle(tmr=%d, sin_cyc=%d) - accelerating pending catch-up tick before idling %s\n", tmr, sin_cyc, sim_uname (sim_clock_unit[tmr]));
    sim_activate_abs (&sim_timer_units[tmr], 0);
    sim_interval -= sin_cyc;
    return FALSE;
    }
if ((!sim_idle_enab)                             ||     /* idling disabled */
    ((sim_clock_queue == QUEUE_LIST_END) &&             /* or clock queue empty? */
     (!sim_asynch_timer))||                             /*     and not asynch? */
    ((sim_clock_queue != QUEUE_LIST_END) &&             /* or clock queue not empty */
     ((sim_clock_queue->flags & UNIT_IDLE) == 0))||     /*   and event not idle-able? */
    (rtc_elapsed[tmr] < sim_idle_stable)) {             /* or timer not stable? */
    sim_debug (DBG_IDL, &sim_timer_dev, "Can't idle: %s - elapsed: %d.%03d\n", !sim_idle_enab ? "idle disabled" : 
                                                                             ((rtc_elapsed[tmr] < sim_idle_stable) ? "not stable" : 
                                                                                                                     ((sim_clock_queue != QUEUE_LIST_END) ? sim_uname (sim_clock_queue) : 
                                                                                                                                                            "")), rtc_elapsed[tmr], rtc_ticks[tmr]);
    sim_interval -= sin_cyc;
    return FALSE;
    }
if (_rtcn_tick_catchup_check(tmr, 0)) {
    sim_debug (DBG_CAL, &sim_timer_dev, "sim_idle(tmr=%d, sin_cyc=%d) - rescheduling catchup tick for %s\n", tmr, sin_cyc, sim_uname (sim_clock_unit[tmr]));
    sim_interval -= sin_cyc;
    return FALSE;
    }
/*
   When a simulator is in an instruction path (or under other conditions 
   which would indicate idling), the countdown of sim_interval will not 
   be happening at a pace which is consistent with the rate it happens 
   when not in the 'idle capable' state.  The consequence of this is that 
   the clock calibration may produce calibrated results which vary much 
   more than they do when not in the idle able state.  Sim_idle also uses 
   the calibrated tick size to approximate an adjustment to sim_interval
   to reflect the number of instructions which would have executed during 
   the actual idle time, so consistent calibrated numbers produce better 
   adjustments. 
   
   To negate this effect, we accumulate the time actually idled here.
   sim_rtcn_calb compares the accumulated idle time during the most recent 
   second and if it exceeds the percentage defined by and sim_idle_calib_pct
   calibration is suppressed. Thus recalibration only happens if things 
   didn't idle too much.

   we also check check sim_idle_enab above so that all simulators can avoid
   directly checking sim_idle_enab before calling sim_idle so that all of 
   the bookkeeping on sim_idle_idled is done here in sim_timer where it 
   means something, while not idling when it isn't enabled.  
   */
sim_debug (DBG_TRC, &sim_timer_dev, "sim_idle(tmr=%d, sin_cyc=%d)\n", tmr, sin_cyc);
if (sim_idle_cyc_ms == 0)
    sim_idle_cyc_ms = (rtc_currd[tmr] * rtc_hz[tmr]) / 1000;/* cycles per msec */
if ((sim_idle_rate_ms == 0) || (sim_idle_cyc_ms == 0)) {/* not possible? */
    sim_interval -= sin_cyc;
    sim_debug (DBG_IDL, &sim_timer_dev, "not possible idle_rate_ms=%d - cyc/ms=%d\n", sim_idle_rate_ms, sim_idle_cyc_ms);
    return FALSE;
    }
w_ms = (uint32) sim_interval / sim_idle_cyc_ms;         /* ms to wait */
/* When the host system has a clock tick which is less frequent than the    */
/* simulated system's clock, idling will cause delays which will miss       */
/* simulated clock ticks.  To accomodate this, and still allow idling, if   */
/* the simulator acknowledges the processing of clock ticks, then catchup   */
/* ticks can be used to make up for missed ticks. */
if (rtc_clock_catchup_eligible[tmr])
    w_idle = (sim_interval * 1000) / rtc_currd[tmr];    /* 1000 * pending fraction of tick */
else
    w_idle = (w_ms * 1000) / sim_idle_rate_ms;          /* 1000 * intervals to wait */
if (w_idle < 500) {                                     /* shorter than 1/2 the interval? */
    sim_interval -= sin_cyc;
    sim_debug (DBG_IDL, &sim_timer_dev, "no wait\n");
    return FALSE;
    }
if (sim_clock_queue == QUEUE_LIST_END)
    sim_debug (DBG_IDL, &sim_timer_dev, "sleeping for %d ms - pending event in %d instructions\n", w_ms, sim_interval);
else
    sim_debug (DBG_IDL, &sim_timer_dev, "sleeping for %d ms - pending event on %s in %d instructions\n", w_ms, sim_uname(sim_clock_queue), sim_interval);
act_ms = sim_idle_ms_sleep (w_ms);                      /* wait */
rtc_clock_time_idled[tmr] += act_ms;
act_cyc = act_ms * sim_idle_cyc_ms;
act_cyc += (sim_idle_cyc_ms * sim_idle_rate_ms) / 2;    /* account for half an interval's worth of cycles */
if (sim_interval > act_cyc)
    sim_interval = sim_interval - act_cyc;              /* count down sim_interval */
else
    sim_interval = 0;                                   /* or fire immediately */
if (sim_clock_queue == QUEUE_LIST_END)
    sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event in %d instructions\n", act_ms, sim_interval);
else
    sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event on %s in %d instructions\n", act_ms, sim_uname(sim_clock_queue), sim_interval);
return TRUE;
}

/* Set idling - implicitly disables throttling */

t_stat sim_set_idle (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
t_stat r;
uint32 v;

if (cptr && *cptr) {
    v = (uint32) get_uint (cptr, 10, SIM_IDLE_STMAX, &r);
    if ((r != SCPE_OK) || (v < SIM_IDLE_STMIN))
        return sim_messagef (SCPE_ARG, "Invalid Stability value: %s.  Valid values range from %d to %d.\n", cptr, SIM_IDLE_STMIN, SIM_IDLE_STMAX);
    sim_idle_stable = v;
    }
sim_idle_enab = TRUE;
if (sim_throt_type != SIM_THROT_NONE) {
    sim_set_throt (0, NULL);
    sim_printf ("Throttling disabled\n");
    }
return SCPE_OK;
}

/* Clear idling */

t_stat sim_clr_idle (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
sim_idle_enab = FALSE;
return SCPE_OK;
}

/* Show idling */

t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
if (sim_idle_enab)
    fprintf (st, "idle enabled");
else
    fprintf (st, "idle disabled");
if (sim_switches & SWMASK ('D'))
    fprintf (st, ", stability wait = %ds, minimum sleep resolution = %dms", sim_idle_stable, sim_os_sleep_min_ms);
return SCPE_OK;
}

/* Throttling package */

t_stat sim_set_throt (int32 arg, CONST char *cptr)
{
CONST char *tptr;
char c;
t_value val, val2 = 0;

if (arg == 0) {
    if ((cptr != NULL) && (*cptr != 0))
        return sim_messagef (SCPE_ARG, "Unexpected NOTHROTTLE argument: %s\n", cptr);
    sim_throt_type = SIM_THROT_NONE;
    sim_throt_cancel ();
    }
else if (sim_idle_rate_ms == 0) {
    return sim_messagef (SCPE_NOFNC, "Throttling is not available, Minimum OS sleep time is %dms\n", sim_os_sleep_min_ms);
    }
else {
    if (*cptr == '\0')
        return sim_messagef (SCPE_ARG, "Missing throttle mode specification\n");
    val = strtotv (cptr, &tptr, 10);
    if (cptr == tptr)
        return sim_messagef (SCPE_ARG, "Invalid throttle specification: %s\n", cptr);
    sim_throt_sleep_time = sim_idle_rate_ms;
    c = (char)toupper (*tptr++);
    if (c == '/') {
        val2 = strtotv (tptr, &tptr, 10);
        if ((*tptr != '\0') || (val == 0))
            return sim_messagef (SCPE_ARG, "Invalid throttle delay specifier: %s\n", cptr);
        }
    if (c == 'M') 
        sim_throt_type = SIM_THROT_MCYC;
    else if (c == 'K')
        sim_throt_type = SIM_THROT_KCYC;
    else if ((c == '%') && (val > 0) && (val < 100))
        sim_throt_type = SIM_THROT_PCT;
    else if ((c == '/') && (val2 != 0))
        sim_throt_type = SIM_THROT_SPC;
    else return sim_messagef (SCPE_ARG, "Invalid throttle specification: %s\n", cptr);
    if (sim_idle_enab) {
        sim_printf ("Idling disabled\n");
        sim_clr_idle (NULL, 0, NULL, NULL);
        }
    sim_throt_val = (uint32) val;
    if (sim_throt_type == SIM_THROT_SPC) {
        if (val2 >= sim_idle_rate_ms)
            sim_throt_sleep_time = (uint32) val2;
        else {
            if ((sim_idle_rate_ms % val2) == 0) {
                sim_throt_sleep_time = sim_idle_rate_ms;
                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_register_internal_device (&sim_throttle_dev);       /* Register Throttle Device */
sim_throt_cps = SIM_INITIAL_IPS;    /* Initial value while correct one is determined */
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");
else {
    switch (sim_throt_type) {

    case SIM_THROT_MCYC:
        fprintf (st, "Throttle:                      %d megacycles\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_KCYC:
        fprintf (st, "Throttle:                      %d kilocycles\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_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:                      sleep %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) {
        if (sim_throt_state != SIM_THROT_STATE_THROTTLE)
            fprintf (st, "Throttle State:                %s - wait: %d\n", (sim_throt_state == SIM_THROT_STATE_INIT) ? "Waiting for Init" : "Timing", sim_throt_wait);
        }
    }
return SCPE_OK;
}

void sim_throt_sched (void)
{
sim_throt_state = SIM_THROT_STATE_INIT;
if (sim_throt_type)
    sim_activate (&sim_throttle_unit, SIM_THROT_WINIT);
}

void sim_throt_cancel (void)
{
sim_cancel (&sim_throttle_unit);
}

/* Throttle service

   Throttle service has three distinct states used while dynamically
   determining a throttling interval:

       SIM_THROT_STATE_INIT     take initial measurement
       SIM_THROT_STATE_TIME     take final measurement, calculate wait values
       SIM_THROT_STATE_THROTTLE periodic waits to slow down the CPU
*/
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_throt_ms_start = sim_os_msec ();
        sim_throt_inst_start = sim_gtime();
        sim_throt_wait = SIM_THROT_WST;
        sim_throt_state = SIM_THROT_STATE_TIME;         /* next state */
        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? */
            if (sim_throt_wait >= 100000000) {          /* too many inst? */
                sim_throt_state = SIM_THROT_STATE_INIT; /* fails in 32b! */
                sim_printf ("Can't throttle.  Host CPU is too fast with a minimum sleep time of %d ms\n", sim_idle_rate_ms);
                sim_set_throt (0, NULL);                /* disable throttling */
                return SCPE_OK;
                }
            sim_idle_ms_sleep (sim_idle_rate_ms);       /* start on a tick boundart to calibrate */
            sim_throt_wait = sim_throt_wait * SIM_THROT_WMUL;
            sim_throt_ms_start = sim_os_msec ();
            sim_throt_inst_start = sim_gtime();
            }
        else {                                          /* long enough */
            a_cps = ((double) sim_throt_wait) * 1000.0 / (double) delta_ms;
            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 (d_cps >= a_cps) {
                sim_throt_state = SIM_THROT_STATE_INIT;
                sim_printf ("Host CPU is too slow to simulate %s instructions per second\n", sim_fmt_numeric(d_cps));
                sim_printf ("Throttling disabled.\n");
                sim_set_throt (0, NULL);
                return SCPE_OK;
                }
            while (1) {
                sim_throt_wait = (int32)                /* time between waits */
                    ((a_cps * d_cps * ((double) sim_throt_sleep_time)) /
                     (1000.0 * (a_cps - d_cps)));
                if (sim_throt_wait >= SIM_THROT_WMIN)   /* long enough? */
                    break;
                sim_throt_sleep_time += sim_os_sleep_inc_ms;
                sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Wait too small, increasing sleep time to %d ms.  Values a_cps = %f, d_cps = %f, wait = %d\n", 
                                                    sim_throt_sleep_time, a_cps, d_cps, sim_throt_wait);
                }
            sim_throt_ms_start = sim_throt_ms_stop;
            sim_throt_inst_start = sim_gtime();
            sim_throt_state = SIM_THROT_STATE_THROTTLE;
            sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Throttle values a_cps = %f, d_cps = %f, wait = %d, sleep = %d ms\n", 
                                                a_cps, d_cps, sim_throt_wait, sim_throt_sleep_time);
            sim_throt_cps = d_cps;                  /* save the desired rate */
            /* Run through all timers and adjust the calibration for each */
            /* one that is running to reflect the throttle rate */
            for (tmr=0; tmr<=SIM_NTIMERS; tmr++)
                if (rtc_hz[tmr]) {                                      /* running? */
                    rtc_gtime[tmr] = sim_gtime();                       /* save instruction time */
                    rtc_currd[tmr] = (int32)(sim_throt_cps / rtc_hz[tmr]);/* use throttle calibration */
                    }
            }
        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 (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)((1000.0 * sim_throt_val) / (double)delta_ms);
        break;
        }

sim_activate (uptr, sim_throt_wait);                    /* reschedule */
return SCPE_OK;
}

/* Clock assist activites */
t_stat sim_timer_tick_svc (UNIT *uptr)
{
int32 tmr = (int32)(uptr-sim_timer_units);
t_stat stat;

rtc_clock_ticks[tmr] += 1;
rtc_calib_tick_time[tmr] += rtc_clock_tick_size[tmr];
/*
 * Some devices may depend on executing during the same instruction or 
 * immediately after the clock tick event.  To satisfy this, we directly 
 * run the clock event here and if it completes successfully, schedule any
 * currently coschedule units to run now.  Ticks should never return a 
 * non-success status, while co-schedule activities might, so they are 
 * queued to run from sim_process_event
 */
sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_tick_svc(tmr=%d) - scheduling %s - cosched interval: %d\n", tmr, sim_uname (sim_clock_unit[tmr]), sim_cosched_interval[tmr]);
if (sim_clock_unit[tmr]->action == NULL)
    return SCPE_IERR;
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)) {
    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;
        }
    do {
        UNIT *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;
        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_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_activate (cptr, 0);
            }
        } while ((sim_cosched_interval[tmr] <= 0) &&
             (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END));
    }
return stat;
}

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

/* 
 * If the host system has a relatively large clock tick (as compared to
 * the desired simulated hz) ticks will naturally be scheduled late and
 * these delays will accumulate.  The net result will be unreasonably
 * slow ticks being delivered to the simulated system.
 * Additionally, when a simulator is idling and/or throttling, it will
 * deliberately call sim_os_ms_sleep and those sleep operations will be
 * variable and subject to the host system's minimum sleep resolution
 * which can exceed the desired sleep interval and add to the concept
 * of slow tick delivery to the simulated system.
 * We accomodate these problems and make up for lost ticks by injecting
 * catch-up ticks to the simulator.
 *
 * When necessary, catch-up ticks are scheduled to run under one 
 * of two conditions:
 *   1) after indicated number of instructions in a call by the simulator
 *      to sim_rtcn_tick_ack.  sim_rtcn_tick_ack exists to provide a 
 *      mechanism to inform the simh timer facilities when the simulated 
 *      system has accepted the most recent clock tick interrupt.
 *   2) immediately when the simulator calls sim_idle
 *
 * catchup ticks are only scheduled (eligible to happen) under these 
 * conditions after at least one tick has been acknowledged.
 */

/* _rtcn_tick_catchup_check - idle simulator until next event or for specified interval

   Inputs:
        tmr =   calibrated timer to check/schedule
        time =  instruction delay for next tick

   Returns TRUE if a catchup tick has been scheduled
*/

static t_bool _rtcn_tick_catchup_check (int32 tmr, int32 time)
{
if ((!sim_catchup_ticks) || 
    ((tmr < 0) || (tmr >= SIM_NTIMERS)))
    return FALSE;
if ((rtc_hz[tmr] > sim_os_tick_hz) &&           /* faster than host tick */
    (!rtc_clock_catchup_eligible[tmr]) &&       /* not eligible yet? */
    (time != -1)) {                             /* called from ack? */
    rtc_clock_catchup_base_time[tmr] = sim_timenow_double();
    rtc_clock_ticks_tot[tmr] += rtc_clock_ticks[tmr];
    rtc_clock_ticks[tmr] = 0;
    rtc_calib_tick_time_tot[tmr] += rtc_calib_tick_time[tmr];
    rtc_calib_tick_time[tmr] = 0.0;
    rtc_clock_catchup_ticks_tot[tmr] += rtc_clock_catchup_ticks[tmr];
    rtc_clock_catchup_ticks[tmr] = 0;
    rtc_calib_ticks_acked_tot[tmr] += rtc_calib_ticks_acked[tmr];
    rtc_calib_ticks_acked[tmr] = 0;
    rtc_clock_catchup_eligible[tmr] = TRUE;
    sim_debug (DBG_QUE, &sim_timer_dev, "_rtcn_tick_catchup_check() - Enabling catchup ticks for %s\n", sim_uname (sim_clock_unit[tmr]));
    return TRUE;
    }
if (rtc_clock_catchup_eligible[tmr])
    {
    double tnow = sim_timenow_double();

    if (tnow > (rtc_clock_catchup_base_time[tmr] + (rtc_calib_tick_time[tmr] + rtc_clock_tick_size[tmr]))) {
        sim_debug (DBG_QUE, &sim_timer_dev, "_rtcn_tick_catchup_check(%d) - scheduling catchup tick for %s which is behind %s\n", time, sim_uname (sim_clock_unit[tmr]), sim_fmt_secs (tnow > (rtc_clock_catchup_base_time[tmr] + (rtc_calib_tick_time[tmr] + rtc_clock_tick_size[tmr]))));
        rtc_clock_catchup_pending[tmr] = TRUE;
        sim_activate_abs (&sim_timer_units[tmr], (time < 0) ? 0 : time);
        return TRUE;
        }
    }
return FALSE;
}

t_stat sim_rtcn_tick_ack (uint32 time, int32 tmr)
{
if ((tmr < 0) || (tmr >= SIM_NTIMERS))
    return SCPE_TIMER;
sim_debug (DBG_ACK, &sim_timer_dev, "sim_rtcn_tick_ack - for %s\n", sim_uname (sim_clock_unit[tmr]));
_rtcn_tick_catchup_check (tmr, (int32)time);
++rtc_calib_ticks_acked[tmr];
return SCPE_OK;
}


static double _timespec_to_double (struct timespec *time)
{
return ((double)time->tv_sec)+(double)(time->tv_nsec)/1000000000.0;
}

static void _double_to_timespec (struct timespec *time, double dtime)
{
time->tv_sec = (time_t)floor(dtime);
time->tv_nsec = (long)((dtime-floor(dtime))*1000000000.0);
}

double sim_timenow_double (void)
{
struct timespec now;

clock_gettime (CLOCK_REALTIME, &now);
return _timespec_to_double (&now);
}

#if defined(SIM_ASYNCH_CLOCKS)

pthread_t           sim_timer_thread;           /* Wall Clock Timing Thread Id */
pthread_cond_t      sim_timer_startup_cond;
t_bool              sim_timer_thread_running = FALSE;

static void *
_timer_thread(void *arg)
{
int sched_policy;
struct sched_param sched_priority;

/* Boost Priority for this I/O thread vs the CPU instruction execution 
   thread which, in general, won't be readily yielding the processor when 
   this thread needs to run */
pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority);
++sched_priority.sched_priority;
pthread_setschedparam (pthread_self(), sched_policy, &sched_priority);

sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - starting\n");

pthread_mutex_lock (&sim_timer_lock);
pthread_cond_signal (&sim_timer_startup_cond);   /* Signal we're ready to go */
while (sim_asynch_timer && sim_is_running) {
    struct timespec start_time, stop_time;
    struct timespec due_time;
    double wait_usec;
    int32 inst_delay;
    double inst_per_sec;
    UNIT *uptr, *cptr, *prvptr;

    if (sim_wallclock_entry) {                          /* something to insert in queue? */

        sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - timing %s for %s\n", 
                   sim_uname(sim_wallclock_entry), sim_fmt_secs (sim_wallclock_entry->a_usec_delay/1000000.0));

        uptr = sim_wallclock_entry;
        sim_wallclock_entry = NULL;

        prvptr = NULL;
        for (cptr = sim_wallclock_queue; cptr != QUEUE_LIST_END; cptr = cptr->a_next) {
            if (uptr->a_due_time < cptr->a_due_time)
                break;
            prvptr = cptr;
            }
        if (prvptr == NULL) {                           /* insert at head */
            cptr = uptr->a_next = sim_wallclock_queue;
            sim_wallclock_queue = uptr;
            }
        else {
            cptr = uptr->a_next = prvptr->a_next;       /* insert at prvptr */
            prvptr->a_next = uptr;
            }
        }

    /* determine wait time */
    if (sim_wallclock_queue != QUEUE_LIST_END) {
        /* due time adjusted by 1/2 a minimal sleep interval */
        /* the goal being to let the last fractional part of the due time */
        /* be done by counting instructions */
        _double_to_timespec (&due_time, sim_wallclock_queue->a_due_time-(((double)sim_idle_rate_ms)*0.0005));
        }
    else {
        due_time.tv_sec = 0x7FFFFFFF;                   /* Sometime when 32 bit time_t wraps */
        due_time.tv_nsec = 0;
        }
    clock_gettime(CLOCK_REALTIME, &start_time);
    wait_usec = floor(1000000.0*(_timespec_to_double (&due_time) - _timespec_to_double (&start_time)));
    if (sim_wallclock_queue == QUEUE_LIST_END)
        sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting forever\n");
    else
        sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting for %.0f usecs until %.6f for %s\n", wait_usec, sim_wallclock_queue->a_due_time, sim_uname(sim_wallclock_queue));
    if ((wait_usec <= 0.0) || 
        (0 != pthread_cond_timedwait (&sim_timer_wake, &sim_timer_lock, &due_time))) {

        if (sim_wallclock_queue == QUEUE_LIST_END)      /* queue empty? */
            continue;                                   /* wait again */
        inst_per_sec = sim_timer_inst_per_sec ();

        uptr = sim_wallclock_queue;
        sim_wallclock_queue = uptr->a_next;
        uptr->a_next = NULL;                            /* hygiene */

        clock_gettime(CLOCK_REALTIME, &stop_time);
        if (1 != sim_timespec_compare (&due_time, &stop_time))
            inst_delay = 0;
        else
            inst_delay = (int32)(inst_per_sec*(_timespec_to_double(&due_time)-_timespec_to_double(&stop_time)));
        sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - slept %.0fms - activating(%s,%d)\n", 
                   1000.0*(_timespec_to_double (&stop_time)-_timespec_to_double (&start_time)), sim_uname(uptr), inst_delay);
        sim_activate (uptr, inst_delay);
        }
    else {/* Something wants to adjust the queue since the wait condition was signaled */
        }
    }
pthread_mutex_unlock (&sim_timer_lock);

sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - exiting\n");

return NULL;
}

#endif /* defined(SIM_ASYNCH_CLOCKS) */

/*
   In the event that there are no active clock devices, no instruction 
   rate calibration will be performed.  This is more likely on simpler
   simulators which don't have a full spectrum of standard devices or 
   possibly when a clock device exists but its use is optional.

   Additonally, when a host system has a natural clock tick (or minimal 
   sleep time) which is greater than the tick size that a simulator 
   wants to run a clock at, we run this clock at the rate implied by
   the host system's minimal sleep time or 50Hz.
   
   To solve this we merely run an internal clock at 10Hz.
 */

#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_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 
  clock properly counting instruction execution relative to time.  The best 
  way to assure reliable calibration is to use a clock which ticks no 
  faster than the host system's clock.  This is optimal so that accurate 
  time measurements are taken.  If the simulated system doesn't have a 
  clock with an appropriate tick rate, an internal clock is run that meets 
  this requirement, 
 */
static void _rtcn_configure_calibrated_clock (int32 newtmr)
{
int32 tmr;

/* Look for a timer running slower than the host system clock */
sim_int_clk_tps = MIN(CLK_TPS, sim_os_tick_hz);
for (tmr=0; tmr<SIM_NTIMERS; tmr++) {
    if ((rtc_hz[tmr]) &&
        (rtc_hz[tmr] <= (uint32)sim_os_tick_hz))
        break;
    }
if (tmr == SIM_NTIMERS) {                   /* None found? */
    if ((tmr != newtmr) && (!sim_is_active (&SIM_INTERNAL_UNIT))) {
        if ((sim_calb_tmr != SIM_NTIMERS) &&/* not internal timer? */
            (sim_calb_tmr != -1) &&         /* previously active? */
            (!rtc_hz[sim_calb_tmr])) {      /* now stopped? */
            sim_debug (DBG_CAL, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Cleaning up stopped timer %s support\n", newtmr, sim_uname(sim_clock_unit[sim_calb_tmr]));
            /* Migrate any coscheduled devices to the standard queue */
            /* with appropriate usecs_remaining reflecting their currently */
            /* scheduled firing time.  sim_process_event() will coschedule */
            /* appropriately. */
            /* temporarily restore prior hz to get correct remaining time */
            rtc_hz[sim_calb_tmr] = rtc_last_hz[sim_calb_tmr];
            while (sim_clock_cosched_queue[sim_calb_tmr] != QUEUE_LIST_END) {
                UNIT *uptr = sim_clock_cosched_queue[sim_calb_tmr];
                double usecs_remaining = sim_timer_activate_time_usecs (uptr) - 1;

                _sim_coschedule_cancel (uptr);
                _sim_activate (uptr, 1);
                uptr->usecs_remaining = usecs_remaining;
                }
            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_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]);
    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 {
    if ((sim_calb_tmr != -1) &&
        (rtc_hz[sim_calb_tmr] == 0)) {
        /* Migrate any coscheduled devices to the standard queue */
        /* with appropriate usecs_remaining reflecting their currently */
        /* scheduled firing time.  sim_process_event() will coschedule */
        /* appropriately. */
        /* temporarily restore prior hz to get correct remaining time */
        rtc_hz[sim_calb_tmr] = rtc_last_hz[sim_calb_tmr];
        while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) {
            UNIT *uptr = sim_clock_cosched_queue[tmr];
            double usecs_remaining = sim_timer_activate_time_usecs (uptr) - 1;

            _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_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;
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 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);
    pthread_attr_init (&attr);
    pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
    pthread_create (&sim_timer_thread, &attr, _timer_thread, NULL);
    pthread_attr_destroy( &attr);
    pthread_cond_wait (&sim_timer_startup_cond, &sim_timer_lock); /* Wait for thread to stabilize */
    pthread_cond_destroy (&sim_timer_startup_cond);
    sim_timer_thread_running = TRUE;
    }
pthread_mutex_unlock (&sim_timer_lock);
#endif
}

void sim_stop_timer_services (void)
{
int tmr;

sim_debug (DBG_TRC, &sim_timer_dev, "sim_stop_timer_services()\n");

for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
    int32 accum;

    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 */
        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];
            double usecs_remaining = cptr->usecs_remaining;

            cptr->usecs_remaining = 0;
            sim_clock_cosched_queue[tmr] = cptr->next;
            cptr->next = NULL;
            cptr->cancel = NULL;
            accum += cptr->time;
            sim_debug (DBG_QUE, &sim_timer_dev, "sim_stop_timer_services() - tmr=%d scheduling %s after %d\n", tmr, sim_uname (cptr), clock_time + accum*rtc_currd[tmr]);
            _sim_activate (cptr, clock_time + accum*rtc_currd[tmr]);
            cptr->usecs_remaining = usecs_remaining;
            }
        sim_cosched_interval[tmr] = 0;
        }
    }
sim_cancel (&SIM_INTERNAL_UNIT);                    /* Make sure Internal Timer is stopped */
sim_cancel (&sim_timer_units[SIM_NTIMERS]);
sim_calb_tmr_last = sim_calb_tmr;                   /* Save calibrated timer value for display */
sim_inst_per_sec_last = sim_timer_inst_per_sec ();  /* Save execution rate for display */
sim_calb_tmr = -1;
#if defined(SIM_ASYNCH_CLOCKS)
pthread_mutex_lock (&sim_timer_lock);
if (sim_timer_thread_running) {
    sim_debug (DBG_TRC, &sim_timer_dev, "sim_stop_timer_services() - stopping\n");
    pthread_cond_signal (&sim_timer_wake);
    pthread_mutex_unlock (&sim_timer_lock);
    pthread_join (sim_timer_thread, NULL);
    sim_timer_thread_running = FALSE;
    /* Any wallclock queued events are now migrated to the normal event queue */
    while (sim_wallclock_queue != QUEUE_LIST_END) {
        UNIT *uptr = sim_wallclock_queue;
        double inst_delay_d = uptr->a_due_gtime - sim_gtime ();
        int32 inst_delay;

        uptr->cancel (uptr);
        if (inst_delay_d < 0.0)
            inst_delay_d = 0.0;
        /* Bound delay to avoid overflow.  */
        /* Long delays are usually canceled before they expire */
        if (inst_delay_d > (double)0x7FFFFFFF)
            inst_delay_d = (double)0x7FFFFFFF;
        inst_delay = (int32)inst_delay_d;
        if ((inst_delay == 0) && (inst_delay_d != 0.0))
            inst_delay = 1;     /* Minimum non-zero delay is 1 instruction */
        _sim_activate (uptr, inst_delay);            /* queue it now */
        }
    }
else
    pthread_mutex_unlock (&sim_timer_lock);
#endif
}

t_stat sim_timer_change_asynch (void)
{
#if defined(SIM_ASYNCH_CLOCKS)
if (sim_asynch_enabled && sim_asynch_timer)
    sim_start_timer_services ();
else
    sim_stop_timer_services ();
#endif
return SCPE_OK;
}

/* Instruction Execution rate. */
/*  returns a double since it is mostly used in double expressions and
    to avoid overflow if/when strange timing delays might produce unexpected results */

double sim_timer_inst_per_sec (void)
{
double inst_per_sec = sim_inst_per_sec_last;

if (sim_calb_tmr == -1)
    return inst_per_sec;
inst_per_sec = ((double)rtc_currd[sim_calb_tmr])*rtc_hz[sim_calb_tmr];
if (0 == inst_per_sec)
    inst_per_sec = ((double)rtc_currd[sim_calb_tmr])*sim_int_clk_tps;
return inst_per_sec;
}

t_stat sim_timer_activate (UNIT *uptr, int32 interval)
{
AIO_VALIDATE;
return sim_timer_activate_after (uptr, (double)((interval * 1000000.0) / sim_timer_inst_per_sec ()));
}

t_stat sim_timer_activate_after (UNIT *uptr, double usec_delay)
{
UNIT *ouptr = uptr;
int inst_delay, tmr;
double inst_delay_d, inst_per_usec;
t_stat stat;

AIO_VALIDATE;
/* If this is a clock unit, we need to schedule the related timer unit instead */
for (tmr=0; tmr<=SIM_NTIMERS; tmr++)
    if (sim_clock_unit[tmr] == uptr) {
        uptr = &sim_timer_units[tmr];
        break;
        }
if (sim_is_active (uptr))                               /* already active? */
    return SCPE_OK;
uptr->usecs_remaining = 0;
if (usec_delay <= 0.0) {
    sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - invalid usec value\n", 
               sim_uname(uptr), usec_delay);
    return SCPE_ARG;
    }
/* 
 * Handle long delays by aligning with the calibrated timer's calibration
 * activities.  Delays which would expire prior to the next calibration
 * are specifically scheduled directly based on the the current instruction
 * execution rate.  Longer delays are coscheduled to fire on the first tick
 * after the next calibration and at that time are either scheduled directly
 * or re-coscheduled for the next calibration time, repeating until the total
 * desired time has elapsed.
 */
inst_per_usec = sim_timer_inst_per_sec () / 1000000.0;
inst_delay_d = floor(inst_per_usec * usec_delay);
inst_delay = (int32)inst_delay_d;
if ((inst_delay == 0) && (usec_delay != 0))
    inst_delay_d = inst_delay = 1;  /* Minimum non-zero delay is 1 instruction */
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? */
            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;
            }
        }
    }
/* 
 * We're here to schedule if:
 * No Calibrated Timer, OR
 * Scheduling the Calibrated Timer OR
 * Short delay
 */
/*
 * Bound delay to avoid overflow.
 * Long delays are usually canceled before they expire, however bounding the 
 * delay will cause sim_activate_time to return inconsistent results when 
 * truncation has happened.
 */
if (inst_delay_d > (double)0x7fffffff)
    inst_delay_d = (double)0x7fffffff;              /* Bound delay to avoid overflow.  */
inst_delay = (int32)inst_delay_d;
#if defined(SIM_ASYNCH_CLOCKS)
if ((sim_asynch_timer) &&
    (usec_delay > sim_idle_rate_ms*1000.0)) {
    double d_now = sim_timenow_double ();
    UNIT *cptr, *prvptr;

    uptr->a_usec_delay = usec_delay;
    uptr->a_due_time = d_now + (usec_delay / 1000000.0);
    uptr->a_due_gtime = sim_gtime () + (sim_timer_inst_per_sec () * (usec_delay / 1000000.0));
    uptr->cancel = &_sim_wallclock_cancel;              /* bind cleanup method */
    uptr->a_is_active = &_sim_wallclock_is_active;
    if (tmr <= SIM_NTIMERS) {                            /* Timer Unit? */
        sim_clock_unit[tmr]->cancel = &_sim_wallclock_cancel;
        sim_clock_unit[tmr]->a_is_active = &_sim_wallclock_is_active;
        }

    sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - queueing wallclock addition at %.6f\n", 
               sim_uname(uptr), usec_delay, uptr->a_due_time);

    pthread_mutex_lock (&sim_timer_lock);
    for (cptr = sim_wallclock_queue, prvptr = NULL; cptr != QUEUE_LIST_END; cptr = cptr->a_next) {
        if (uptr->a_due_time < cptr->a_due_time)
            break;
        prvptr = cptr;
        }
    if (prvptr == NULL) {                           /* inserting at head */
        uptr->a_next = QUEUE_LIST_END;              /* Temporarily mark as active */
        while (sim_wallclock_entry) {               /* wait for any prior entry has been digested */
            sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - queue insert entry %s busy waiting for 1ms\n", 
                       sim_uname(uptr), usec_delay, sim_uname(sim_wallclock_entry));
            pthread_mutex_unlock (&sim_timer_lock);
            sim_os_ms_sleep (1);
            pthread_mutex_lock (&sim_timer_lock);
            }
        sim_wallclock_entry = uptr;
        pthread_mutex_unlock (&sim_timer_lock);
        pthread_cond_signal (&sim_timer_wake);      /* wake the timer thread to deal with it */
        return SCPE_OK;
        }
    else {                                          /* inserting at prvptr */
        uptr->a_next = prvptr->a_next;
        prvptr->a_next = uptr;
        pthread_mutex_unlock (&sim_timer_lock);
        return SCPE_OK;
        }
    }
#endif
stat = _sim_activate (uptr, inst_delay);                /* queue it now */
uptr->usecs_remaining = ((stat == SCPE_OK) && (0.0 < (usec_delay - ceil(inst_delay / inst_per_usec) ))) ? 
                            usec_delay - floor(inst_delay / inst_per_usec) :
                            0.0;
sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - queue addition at %d - remnant: %.0f\n", 
           sim_uname(uptr), usec_delay, inst_delay, uptr->usecs_remaining);
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;
}

/* Clock coscheduling routines */

t_stat sim_register_clock_unit_tmr (UNIT *uptr, int32 tmr)
{
if (tmr == SIM_INTERNAL_CLK)
    tmr = SIM_NTIMERS;
else {
    if ((tmr < 0) || (tmr > SIM_NTIMERS))
        return SCPE_IERR;
    }
if (NULL == uptr) {                         /* deregistering? */
    /* Migrate any coscheduled devices to the standard queue */
    /* they will fire and subsequently requeue themselves */
    while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) {
        UNIT *uptr = sim_clock_cosched_queue[tmr];
        double usecs_remaining = sim_timer_activate_time_usecs (uptr);

        _sim_coschedule_cancel (uptr);
        _sim_activate (uptr, 1);
        uptr->usecs_remaining = usecs_remaining;
        }
    if (sim_clock_unit[tmr]) {
        sim_cancel (sim_clock_unit[tmr]);
        sim_clock_unit[tmr]->dynflags &= ~UNIT_TMR_UNIT;
        }
    sim_clock_unit[tmr] = NULL;
    sim_cancel (&sim_timer_units[tmr]);
    return SCPE_OK;
    }
if (NULL == sim_clock_unit[tmr])
    sim_clock_cosched_queue[tmr] = QUEUE_LIST_END;
sim_clock_unit[tmr] = uptr;
uptr->dynflags |= UNIT_TMR_UNIT;
sim_timer_units[tmr].flags = ((tmr == SIM_NTIMERS) ? 0 : UNIT_DIS) | 
                             (sim_clock_unit[tmr] ? UNIT_IDLE : 0);
return SCPE_OK;
}

/* Default timer is 0, otherwise use a calibrated one if it exists */
int32 sim_rtcn_calibrated_tmr (void)
{
return ((rtc_currd[0] && rtc_hz[0]) ? 0 : ((sim_calb_tmr != -1) ? sim_calb_tmr : 0));
}

int32 sim_rtcn_tick_size (int32 tmr)
{
return (rtc_currd[tmr]) ? rtc_currd[tmr] : 10000;
}

t_stat sim_register_clock_unit (UNIT *uptr)
{
return sim_register_clock_unit_tmr (uptr, 0);
}

t_stat sim_clock_coschedule (UNIT *uptr, int32 interval)
{
int32 tmr = sim_rtcn_calibrated_tmr ();
int32 ticks = (interval + (sim_rtcn_tick_size (tmr)/2))/sim_rtcn_tick_size (tmr);/* Convert to ticks */

sim_debug (DBG_QUE, &sim_timer_dev, "sim_clock_coschedule(%s, interval=%d, ticks=%d)\n", sim_uname(uptr), interval, ticks);
return sim_clock_coschedule_tmr (uptr, tmr, ticks);
}

t_stat sim_clock_coschedule_abs (UNIT *uptr, int32 interval)
{
sim_debug (DBG_QUE, &sim_timer_dev, "sim_clock_coschedule_abs(%s, interval=%d)\n", sim_uname(uptr), interval);
sim_cancel (uptr);
return sim_clock_coschedule (uptr, interval);
}

/* ticks - 0 means on the next tick, 1 means the second tick, etc.  */

t_stat sim_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 ticks)
{
if (ticks < 0)
    return SCPE_ARG;
if (sim_is_active (uptr)) {
    sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule_tmr(%s, tmr=%d, ticks=%d) - already active\n", sim_uname (uptr), tmr, ticks);
    return SCPE_OK;
    }
if (tmr == SIM_INTERNAL_CLK)
    tmr = SIM_NTIMERS;
else {
    if ((tmr < 0) || (tmr > SIM_NTIMERS))
        return sim_activate (uptr, MAX(1, ticks) * 10000);
    }
if ((NULL == sim_clock_unit[tmr]) || (rtc_hz[tmr] == 0)) {
    sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule_tmr(%s, tmr=%d, ticks=%d) - no clock activating after %d instructions\n", sim_uname (uptr), tmr, ticks, ticks * (rtc_currd[tmr] ? rtc_currd[tmr] : rtc_currd[sim_rtcn_calibrated_tmr ()]));
    return sim_activate (uptr, ticks * (rtc_currd[tmr] ? rtc_currd[tmr] : rtc_currd[sim_rtcn_calibrated_tmr ()]));
    }
else {
    UNIT *cptr, *prvptr;
    int32 accum;

    if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END)
        sim_clock_cosched_queue[tmr]->time = sim_cosched_interval[tmr];
    prvptr = NULL;
    accum = 0;
    for (cptr = sim_clock_cosched_queue[tmr]; cptr != QUEUE_LIST_END; cptr = cptr->next) {
        if (ticks < (accum + cptr->time))
            break;
        accum += cptr->time;
        prvptr = cptr;
        }
    if (prvptr == NULL) {
        cptr = uptr->next = sim_clock_cosched_queue[tmr];
        sim_clock_cosched_queue[tmr] = uptr;
        }
    else {
        cptr = uptr->next = prvptr->next;
        prvptr->next = uptr;
        }
    uptr->time = ticks - accum;
    if (cptr != QUEUE_LIST_END)
        cptr->time = cptr->time - uptr->time;
    uptr->cancel = &_sim_coschedule_cancel;             /* bind cleanup method */
    if (uptr == sim_clock_cosched_queue[tmr])
        sim_cosched_interval[tmr] = sim_clock_cosched_queue[tmr]->time;
    sim_debug (DBG_QUE, &sim_timer_dev, "sim_clock_coschedule_tmr(%s, tmr=%d, ticks=%d, hz=%d) - queueing for clock co-schedule, interval now: %d\n", sim_uname (uptr), tmr, ticks, rtc_hz[tmr], sim_cosched_interval[tmr]);
    }
return SCPE_OK;
}

t_stat sim_clock_coschedule_tmr_abs (UNIT *uptr, int32 tmr, int32 ticks)
{
sim_cancel (uptr);
return sim_clock_coschedule_tmr (uptr, tmr, ticks);
}

/* Cancel a unit on the coschedule queue */
static t_bool _sim_coschedule_cancel (UNIT *uptr)
{
AIO_UPDATE_QUEUE;
if (uptr->next) {                           /* On a queue? */
    int tmr;
    UNIT *nptr;

    for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
        if (sim_clock_unit[tmr]) {
            if (uptr == sim_clock_cosched_queue[tmr]) {
                nptr = sim_clock_cosched_queue[tmr] = uptr->next;
                uptr->next = NULL;
                }
            else {
                UNIT *cptr;

                for (cptr = sim_clock_cosched_queue[tmr];
                     (cptr != QUEUE_LIST_END);
                     cptr = cptr->next) {
                    if (cptr->next == uptr) {
                        nptr = cptr->next = (uptr)->next;
                        uptr->next = NULL;
                        break;
                        }
                    }
                }
            if (uptr->next == NULL) {           /* found? */
                uptr->cancel = NULL;
                uptr->usecs_remaining = 0;
                if (nptr != QUEUE_LIST_END)
                    nptr->time += uptr->time;
                sim_debug (DBG_QUE, &sim_timer_dev, "Canceled Clock Coscheduled Event for %s\n", sim_uname(uptr));
                return TRUE;
                }
            }
        }
    }
return FALSE;
}

t_bool sim_timer_is_active (UNIT *uptr)
{
int32 tmr;

if (!(uptr->dynflags & UNIT_TMR_UNIT))
    return FALSE;
for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
    if (sim_clock_unit[tmr] == uptr)
        return sim_is_active (&sim_timer_units[tmr]);
    }
return FALSE;
}

t_bool sim_timer_cancel (UNIT *uptr)
{
int32 tmr;

if (!(uptr->dynflags & UNIT_TMR_UNIT))
    return SCPE_IERR;
for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
    if (sim_clock_unit[tmr] == uptr)
        return sim_cancel (&sim_timer_units[tmr]);
    }
return SCPE_IERR;
}

#if defined(SIM_ASYNCH_CLOCKS)
static t_bool _sim_wallclock_cancel (UNIT *uptr)
{
int32 tmr;
t_bool b_return = FALSE;

AIO_UPDATE_QUEUE;
pthread_mutex_lock (&sim_timer_lock);
/* If this is a clock unit, we need to cancel both this and the related timer unit */
for (tmr=0; tmr<=SIM_NTIMERS; tmr++)
    if (sim_clock_unit[tmr] == uptr) {
        uptr = &sim_timer_units[tmr];
        break;
        }
if (uptr->a_next) {
    UNIT *cptr;

    if (uptr == sim_wallclock_entry) {  /* Pending on the queue? */
        sim_wallclock_entry = NULL;
        uptr->a_next = NULL;
        }
    else {
        if (uptr == sim_wallclock_queue) {
            sim_wallclock_queue = uptr->a_next;
            uptr->a_next = NULL;
            sim_debug (DBG_QUE, &sim_timer_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));
            pthread_cond_signal (&sim_timer_wake);
            }
        else {
            for (cptr = sim_wallclock_queue;
                (cptr != QUEUE_LIST_END);
                cptr = cptr->a_next) {
                if (cptr->a_next == (uptr)) {
                    cptr->a_next = (uptr)->a_next;
                    uptr->a_next = NULL;
                    sim_debug (DBG_QUE, &sim_timer_dev, "Canceled Timer Event for %s\n", sim_uname(uptr));
                    break;
                    }
                }
            }
        }
    if (uptr->a_next == NULL) {
        uptr->a_due_time = uptr->a_due_gtime = uptr->a_usec_delay = 0;
        uptr->cancel = NULL;
        uptr->a_is_active = NULL;
        if (tmr <= SIM_NTIMERS) {                        /* Timer Unit? */
            sim_clock_unit[tmr]->cancel = NULL;
            sim_clock_unit[tmr]->a_is_active = NULL;
            }
        b_return = TRUE;
        }
    }
pthread_mutex_unlock (&sim_timer_lock);
return b_return;
}

static t_bool _sim_wallclock_is_active (UNIT *uptr)
{
int32 tmr;

if (uptr->a_next)
    return TRUE;
/* If this is a clock unit, we need to examine the related timer unit instead */
for (tmr=0; tmr<=SIM_NTIMERS; tmr++)
    if (sim_clock_unit[tmr] == uptr)
        return (sim_timer_units[tmr].a_next != NULL);
return FALSE;
}
#endif /* defined(SIM_ASYNCH_CLOCKS) */

int32 _sim_timer_activate_time (UNIT *uptr)
{
UNIT *cptr;
int32 tmr;

#if defined(SIM_ASYNCH_CLOCKS)
if (uptr->a_is_active == &_sim_wallclock_is_active) {
    double d_result;

    pthread_mutex_lock (&sim_timer_lock);
    if (uptr == sim_wallclock_entry) {
        d_result = uptr->a_due_gtime - sim_gtime ();
        if (d_result < 0.0)
            d_result = 0.0;
        if (d_result > (double)0x7FFFFFFE)
            d_result = (double)0x7FFFFFFE;
        pthread_mutex_unlock (&sim_timer_lock);
        return ((int32)d_result) + 1;
        }
    for (cptr = sim_wallclock_queue;
         cptr != QUEUE_LIST_END;
         cptr = cptr->a_next)
        if (uptr == cptr) {
            d_result = uptr->a_due_gtime - sim_gtime ();
            if (d_result < 0.0)
                d_result = 0.0;
            if (d_result > (double)0x7FFFFFFE)
                d_result = (double)0x7FFFFFFE;
            pthread_mutex_unlock (&sim_timer_lock);
            return ((int32)d_result) + 1;
            }
    pthread_mutex_unlock (&sim_timer_lock);
    }
if (uptr->a_next)
    return uptr->a_event_time + 1;
#endif /* defined(SIM_ASYNCH_CLOCKS) */

if (uptr->cancel == &_sim_coschedule_cancel) {
    for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
        int32 accum = 0;

        for (cptr = sim_clock_cosched_queue[tmr]; cptr != QUEUE_LIST_END; cptr = cptr->next) {
            if (cptr == sim_clock_cosched_queue[tmr]) {
                if (sim_cosched_interval[tmr] > 0)
                    accum += sim_cosched_interval[tmr];
                }
            else
                accum += cptr->time;
            if (cptr == uptr)
                return (rtc_currd[tmr] * accum) + sim_activate_time (&sim_timer_units[tmr]);
            }
        }
    }
for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
    if ((uptr == &sim_timer_units[tmr]) && (uptr->next)){
        return _sim_activate_time (&sim_timer_units[tmr]);
        }
    }
return -1;                                          /* Not found. */    
}

double sim_timer_activate_time_usecs (UNIT *uptr)
{
UNIT *cptr;
int32 tmr;
double result = -1.0;

/* If this is a clock unit, we need to return the related clock assist unit instead */
for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
    if (sim_clock_unit[tmr] == uptr) {
        uptr = &sim_timer_units[tmr];
        break;
        }
    }

if (!sim_is_active (uptr)) {
    sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) - not active\n", sim_uname (uptr));
    return result;
    }
#if defined(SIM_ASYNCH_CLOCKS)
if (uptr->a_is_active == &_sim_wallclock_is_active) {
    pthread_mutex_lock (&sim_timer_lock);
    if (uptr == sim_wallclock_entry) {
        result = uptr->a_due_gtime - sim_gtime ();
        if (result < 0.0)
            result = 0.0;
        pthread_mutex_unlock (&sim_timer_lock);
        result = uptr->usecs_remaining + (1000000.0 * (result / sim_timer_inst_per_sec ())) + 1;
        sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) wallclock_entry - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ());
        return result;
        }
    for (cptr = sim_wallclock_queue;
         cptr != QUEUE_LIST_END;
         cptr = cptr->a_next)
        if (uptr == cptr) {
            result = uptr->a_due_gtime - sim_gtime ();
            if (result < 0.0)
                result = 0.0;
            pthread_mutex_unlock (&sim_timer_lock);
            result = uptr->usecs_remaining + (1000000.0 * (result / sim_timer_inst_per_sec ())) + 1;
            sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) wallclock - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ());
            return result;
            }
    pthread_mutex_unlock (&sim_timer_lock);
    }
if (uptr->a_next) {
    result = uptr->usecs_remaining + (1000000.0 * (uptr->a_event_time / sim_timer_inst_per_sec ())) + 1;
    sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) asynch - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ());
    return result;
    }
#endif /* defined(SIM_ASYNCH_CLOCKS) */

if (uptr->cancel == &_sim_coschedule_cancel) {
    for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
        int32 accum = 0;

        for (cptr = sim_clock_cosched_queue[tmr]; cptr != QUEUE_LIST_END; cptr = cptr->next) {
            if (cptr == sim_clock_cosched_queue[tmr]) {
                if (sim_cosched_interval[tmr] > 0)
                    accum += sim_cosched_interval[tmr];
                }
            else
                accum += cptr->time;
            if (cptr == uptr) {
                result = uptr->usecs_remaining + ceil(1000000.0 * ((rtc_currd[tmr] * accum) + sim_activate_time (&sim_timer_units[tmr]) - 1) / sim_timer_inst_per_sec ());
                sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) coscheduled - %.0f usecs, inst_per_sec=%.0f, tmr=%d, ticksize=%d, ticks=%d, inst_til_tick=%d\n", sim_uname (uptr), result, sim_timer_inst_per_sec (), tmr, rtc_currd[tmr], accum, sim_activate_time (&sim_timer_units[tmr]) - 1);
                return result;
                }
            }
        }
    }
for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
    if ((uptr == sim_clock_unit[tmr]) && (uptr->next)) {
        result = sim_clock_unit[tmr]->usecs_remaining + (1000000.0 * (sim_activate_time (&sim_timer_units[tmr]) - 1)) / sim_timer_inst_per_sec ();
        sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) clock - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ());
        return result;
        }
    if ((uptr == &sim_timer_units[tmr]) && (uptr->next)){
        result = uptr->usecs_remaining + (1000000.0 * (sim_activate_time (uptr) - 1)) / sim_timer_inst_per_sec ();
        sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) clock - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ());
        return result;
        }
    }
result = uptr->usecs_remaining + (1000000.0 * (sim_activate_time (uptr) - 1)) / sim_timer_inst_per_sec ();
sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) clock - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ());
return result;                                          /* Not found. */    
}

/* read only memory delayed support

   Some simulation activities need a 'regulated' memory access
   time to meet timing assumptions in the code being executed.

   The default calibration determines a way to limit activities
   to 1Mhz for each call to sim_rom_read_with_delay().  If a 
   simulator needs a different delay factor, the 1 Mhz initial 
   value can be queried with sim_get_rom_delay_factor() and the 
   result can be adjusted as nessary and the operating delay
   can be set with sim_set_rom_delay_factor().
*/

SIM_NOINLINE static int32 _rom_swapb(int32 val)
{
return ((val << 24) & 0xff000000) | (( val << 8) & 0xff0000) |
    ((val >> 8) & 0xff00) | ((val >> 24) & 0xff);
}

static volatile int32 rom_loopval = 0;

SIM_NOINLINE int32 sim_rom_read_with_delay (int32 val)
{
uint32 i, l = sim_rom_delay;

for (i = 0; i < l; i++)
    rom_loopval |= (rom_loopval + val) ^ _rom_swapb (_rom_swapb (rom_loopval + val));
return val + rom_loopval;
}

SIM_NOINLINE uint32 sim_get_rom_delay_factor (void)
{
/* Calibrate the loop delay factor at startup.
   Do this 4 times and use the largest value computed. 
   The goal here is to come up with a delay factor which will throttle
   a 6 byte delay loop running from ROM address space to execute
   1 instruction per usec */

if (sim_rom_delay == 0) {
    uint32 i, ts, te, c = 10000, samples = 0;
    while (1) {
        c = c * 2;
        te = sim_os_msec();
        while (te == (ts = sim_os_msec ()));            /* align on ms tick */

/* This is merely a busy wait with some "work" that won't get optimized
   away by a good compiler. loopval always is zero.  To avoid smart compilers,
   the loopval variable is referenced in the function arguments so that the
   function expression is not loop invariant.  It also must be referenced
   by subsequent code to avoid the whole computation being eliminated. */

        for (i = 0; i < c; i++)
            rom_loopval |= (rom_loopval + ts) ^ _rom_swapb (_rom_swapb (rom_loopval + ts));
        te = sim_os_msec (); 
        if ((te - ts) < 50)                         /* sample big enough? */
            continue;
        if (sim_rom_delay < (rom_loopval + (c / (te - ts) / 1000) + 1))
            sim_rom_delay = rom_loopval + (c / (te - ts) / 1000) + 1;
        if (++samples >= 4)
            break;
        c = c / 2;
        }
    if (sim_rom_delay < 5)
        sim_rom_delay = 5;
    }
return sim_rom_delay;
}

void sim_set_rom_delay_factor (uint32 delay)
{
sim_rom_delay = delay;
}
Added src/sim_timer.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_timer.h: simulator timer library headers

   Copyright (c) 1993-2008, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   28-Apr-07    RMS     Added sim_rtc_init_all
   17-Oct-06    RMS     Added idle support
   02-Jan-04    RMS     Split out from SCP
*/

#ifndef SIM_TIMER_H_
#define SIM_TIMER_H_   0


#ifdef  __cplusplus
extern "C" {
#endif

/* Pick up a struct timespec definition if it is available */
#include <time.h>
#if defined(__struct_timespec_defined)
#define _TIMESPEC_DEFINED
#endif
#if defined(SIM_ASYNCH_IO) || defined(USE_READER_THREAD)
#include <pthread.h>
#endif

#if defined (__APPLE__)
#define HAVE_STRUCT_TIMESPEC 1   /* OSX defined the structure but doesn't tell us */
#endif

/* on HP-UX, CLOCK_REALTIME is enum, not preprocessor define */
#if !defined(CLOCK_REALTIME) && !defined(__hpux)
#define CLOCK_REALTIME 1
#define NEED_CLOCK_GETTIME 1
#if  defined(_MSC_VER)      /* Visual Studio/Visual C++ */
#if _MSC_VER >= 1900        /* Visual Studio Community (2015) */
#define HAVE_STRUCT_TIMESPEC 1
#define _TIMESPEC_DEFINED 1
#endif /* _MSC_VER >= 1900 */
#endif /* defined(_MSC_VER) */
#if !defined(HAVE_STRUCT_TIMESPEC)
#define HAVE_STRUCT_TIMESPEC 1
#if !defined(_TIMESPEC_DEFINED)
#define _TIMESPEC_DEFINED
struct timespec {
    time_t tv_sec;
    long   tv_nsec;
};
#endif /* !defined(_TIMESPEC_DEFINED) */
#endif /* !defined(HAVE_STRUCT_TIMESPEC) */
int clock_gettime(int clock_id, struct timespec *tp);
#endif


#define SIM_NTIMERS     8                           /* # timers */
#define SIM_TMAX        500                         /* max timer makeup */

#define SIM_INITIAL_IPS 500000                      /* uncalibrated assumption */
                                                    /* about instructions per second */

#define SIM_IDLE_CAL    10                          /* ms to calibrate */
#define SIM_IDLE_STMIN  2                           /* min sec for stability */
#define SIM_IDLE_STDFLT 20                          /* dft sec for stability */
#define SIM_IDLE_STMAX  600                         /* max sec for stability */

#define SIM_THROT_WINIT     1000                    /* cycles to skip */
#define SIM_THROT_WST       10000                   /* initial wait */
#define SIM_THROT_WMUL      4                       /* multiplier */
#define SIM_THROT_WMIN      50                      /* min wait */
#define SIM_THROT_DRIFT_PCT 5                       /* drift percentage for recalibrate */
#define SIM_THROT_MSMIN     10                      /* min for measurement */
#define SIM_THROT_NONE      0                       /* throttle parameters */
#define SIM_THROT_MCYC      1                       /* MegaCycles Per Sec */
#define SIM_THROT_KCYC      2                       /* KiloCycles Per Sec */
#define SIM_THROT_PCT       3                       /* Max Percent of host CPU */
#define SIM_THROT_SPC       4                       /* Specific periodic Delay */
#define SIM_THROT_STATE_INIT      0                 /* Starting */
#define SIM_THROT_STATE_TIME      1                 /* Checking Time */
#define SIM_THROT_STATE_THROTTLE  2                 /* Throttling  */

#define TIMER_DBG_IDLE  0x001                       /* Debug Flag for Idle Debugging */
#define TIMER_DBG_QUEUE 0x002                       /* Debug Flag for Asynch Queue Debugging */
#define TIMER_DBG_MUX   0x004                       /* Debug Flag for Asynch Queue Debugging */

t_bool sim_timer_init (void);
void sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub);
double sim_timenow_double (void);
int32 sim_rtcn_init (int32 time, int32 tmr);
int32 sim_rtcn_init_unit (UNIT *uptr, int32 time, int32 tmr);
void sim_rtcn_get_time (struct timespec *now, int tmr);
t_stat sim_rtcn_tick_ack (uint32 time, int32 tmr);
void sim_rtcn_init_all (void);
int32 sim_rtcn_calb (int32 ticksper, int32 tmr);
int32 sim_rtc_init (int32 time);
int32 sim_rtc_calb (int32 ticksper);
t_stat sim_set_timers (int32 arg, CONST char *cptr);
t_stat sim_show_timers (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc);
t_stat sim_show_clock_queues (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
t_bool sim_idle (uint32 tmr, int sin_cyc);
t_stat sim_set_throt (int32 arg, CONST char *cptr);
t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr);
t_stat sim_set_idle (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat sim_clr_idle (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
void sim_throt_sched (void);
void sim_throt_cancel (void);
uint32 sim_os_msec (void);
void sim_os_sleep (unsigned int sec);
uint32 sim_os_ms_sleep (unsigned int msec);
uint32 sim_os_ms_sleep_init (void);
void sim_start_timer_services (void);
void sim_stop_timer_services (void);
t_stat sim_timer_change_asynch (void);
t_stat sim_timer_activate (UNIT *uptr, int32 interval);
t_stat sim_timer_activate_after (UNIT *uptr, double usec_delay);
int32 _sim_timer_activate_time (UNIT *uptr);
double sim_timer_activate_time_usecs (UNIT *uptr);
t_bool sim_timer_is_active (UNIT *uptr);
t_bool sim_timer_cancel (UNIT *uptr);
t_stat sim_register_clock_unit (UNIT *uptr);
t_stat sim_register_clock_unit_tmr (UNIT *uptr, int32 tmr);
t_stat sim_clock_coschedule (UNIT *uptr, int32 interval);
t_stat sim_clock_coschedule_abs (UNIT *uptr, int32 interval);
t_stat sim_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 ticks);
t_stat sim_clock_coschedule_tmr_abs (UNIT *uptr, int32 tmr, int32 ticks);
double sim_timer_inst_per_sec (void);
int32 sim_rtcn_tick_size (int32 tmr);
int32 sim_rtcn_calibrated_tmr (void);
t_bool sim_timer_idle_capable (uint32 *host_ms_sleep_1, uint32 *host_tick_ms);
#define PRIORITY_BELOW_NORMAL  -1
#define PRIORITY_NORMAL         0
#define PRIORITY_ABOVE_NORMAL   1
t_stat sim_os_set_thread_priority (int below_normal_above);
uint32 sim_get_rom_delay_factor (void);
void sim_set_rom_delay_factor (uint32 delay);
int32 sim_rom_read_with_delay (int32 val);

extern t_bool sim_idle_enab;                        /* idle enabled flag */
extern volatile t_bool sim_idle_wait;               /* idle waiting flag */
extern t_bool sim_asynch_timer;
extern DEVICE sim_timer_dev;
extern UNIT * volatile sim_clock_cosched_queue[SIM_NTIMERS+1];
extern const t_bool rtc_avail;

#ifdef  __cplusplus
}
#endif

#endif
Added src/sim_tmxr.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
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
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
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_tmxr.c: Telnet terminal multiplexer library

   Copyright (c) 2001-2011, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   Based on the original DZ11 simulator by Thord Nilson, as updated by
   Arthur Krewat.

   12-Oct-12    MP      Revised serial port support to not require changes to 
                        any code in TMXR library using code.  Added support
                        for per line listener ports and outgoing tcp connections.
   02-Jun-11    MP      Fixed telnet option negotiation loop with some clients
                        Added Option Negotiation and Debugging Support
   17-Jan-11    MP      Added Buffered line capabilities
   16-Jan-11    MP      Made option negotiation more reliable
   20-Nov-08    RMS     Added three new standardized SHOW routines
   05-Nov-08    JDB     Moved logging call after connection check in tmxr_putc_ln
   03-Nov-08    JDB     Added TMXR null check to tmxr_find_ldsc
   07-Oct-08    JDB     Added initial serial port support
   30-Sep-08    JDB     Reverted tmxr_find_ldsc to original implementation
   27-May-08    JDB     Added line connection order to tmxr_poll_conn,
                        added tmxr_set_lnorder and tmxr_show_lnorder
   14-May-08    JDB     Print device and line to which connection was made
   11-Apr-07    JDB     Worked around Telnet negotiation problem with QCTerm
   16-Aug-05    RMS     Fixed C++ declaration and cast problems
   29-Jun-05    RMS     Extended tmxr_dscln to support unit array devices
                        Fixed bug in SET LOG/NOLOG
   04-Jan-04    RMS     Changed TMXR ldsc to be pointer to linedesc array
                        Added tmxr_linemsg, circular output pointers, logging
                        (from Mark Pizzolato)
   29-Dec-03    RMS     Added output stall support
   01-Nov-03    RMS     Cleaned up attach routine
   09-Mar-03    RMS     Fixed bug in SHOW CONN
   22-Dec-02    RMS     Fixed bugs in IAC+IAC receive and transmit sequences
                        Added support for received break (all from by Mark Pizzolato)
                        Fixed bug in attach
   31-Oct-02    RMS     Fixed bug in 8b (binary) support
   22-Aug-02    RMS     Added tmxr_open_master, tmxr_close_master
   30-Dec-01    RMS     Added tmxr_fstats, tmxr_dscln, renamed tmxr_fstatus
   03-Dec-01    RMS     Changed tmxr_fconns for extended SET/SHOW
   20-Oct-01    RMS     Fixed bugs in read logic (found by Thord Nilson).
                        Added tmxr_rqln, tmxr_tqln

   This library includes:

   tmxr_poll_conn -                     poll for connection
   tmxr_reset_ln -                      reset line (drops Telnet/tcp and serial connections)
   tmxr_detach_ln -                     reset line and close per line listener and outgoing destination
   tmxr_getc_ln -                       get character for line
   tmxr_get_packet_ln -                 get packet from line
   tmxr_get_packet_ln_ex -              get packet from line with separater byte
   tmxr_poll_rx -                       poll receive
   tmxr_putc_ln -                       put character for line
   tmxr_put_packet_ln -                 put packet on line
   tmxr_put_packet_ln_ex -              put packet on line with separator byte
   tmxr_poll_tx -                       poll transmit
   tmxr_send_buffered_data -            transmit buffered data
   tmxr_set_modem_control_passthru -    enable modem control on a multiplexer
   tmxr_clear_modem_control_passthru -  disable modem control on a multiplexer
   tmxr_set_get_modem_bits -            set and/or get a line modem bits
   tmxr_set_line_loopback -             enable or disable loopback mode on a line
   tmxr_get_line_loopback -             returns the current loopback status of a line
   tmxr_set_line_halfduplex -           enable or disable halfduplex mode on a line
   tmxr_get_line_halfduplex -           returns the current halfduplex status of a line
   tmxr_set_config_line -               set port speed, character size, parity and stop bits
   tmxr_open_master -                   open master connection
   tmxr_close_master -                  close master connection
   tmxr_attach  -                       attach terminal multiplexor to listening port
   tmxr_detach  -                       detach terminal multiplexor to listening port
   tmxr_attach_help  -                  help routine for attaching multiplexer devices
   tmxr_set_line_unit -                 set the unit which polls for input for a given line
   tmxr_ex      -                       (null) examine
   tmxr_dep     -                       (null) deposit
   tmxr_msg     -                       send message to socket
   tmxr_linemsg -                       send message to line
   tmxr_linemsgf -                      send formatted message to line
   tmxr_fconns  -                       output connection status
   tmxr_fstats  -                       output connection statistics
   tmxr_set_log -                       enable logging for line
   tmxr_set_nolog -                     disable logging for line
   tmxr_show_log -                      show logging status for line
   tmxr_dscln   -                       disconnect line (SET routine)
   tmxr_rqln    -                       number of available characters for line
   tmxr_tqln    -                       number of buffered characters for line
   tmxr_tpqln    -                      number of buffered packet characters for line
   tmxr_tpbusyln -                      transmit packet busy status for line
   tmxr_set_lnorder -                   set line connection order
   tmxr_show_lnorder -                  show line connection order
   tmxr_show_summ -                     show connection summary
   tmxr_show_cstat -                    show line connections or status
   tmxr_show_lines -                    show number of lines
   tmxr_show_open_devices -             show info about all open tmxr devices 

   All routines are OS-independent.


    This library supports the simulation of multiple-line terminal multiplexers.
    It may also be used to create single-line "multiplexers" to provide
    additional terminals beyond the simulation console.  It may also be used to 
    create single-line or multi-line simulated synchronous (BiSync) devices.
    Multiplexer lines may be connected to terminal emulators supporting the 
    Telnet protocol via sockets, or to hardware terminals via host serial
    ports.  Concurrent Telnet and serial connections may be mixed on a given 
    multiplexer.

    When connecting via sockets, the simulated multiplexer is attached to a
    listening port on the host system:

      sim> attach MUX 23
      Listening on port 23

    Once attached, the listening port must be polled for incoming connections.
    When a connection attempt is received, it will be associated with the next
    multiplexer line in the user-specified line order, or with the next line in
    sequence if no order has been specified.  Individual lines may be connected
    to serial ports or remote systems via TCP (telnet or not as desired), OR 
    they may have separate listening TCP ports.

    Logging of Multiplexer Line output:
    
    The traffic going out multiplexer lines can be logged to files.  A single
    line multiplexer can log it's traffic with the following command:

        sim> atta MUX 23,Log=LogFileName
        sim> atta MUX Connect=ser0,Log=LogFileName

    Specifying a Log value for a multi-line multiplexer is specifying a 
    template filename.  The actual file name used for each line will be
    the indicated filename with _n appended (n being the line number).

    Buffered Multiplexer Line:

    A Multiplexer Line Buffering has been implemented.  A Buffered Line will 
    have a copy of the last 'buffer size' bytes of output retained in a line
    specific buffer.  The contents of this buffer will be transmitted out any
    new connection on that line when a new telnet session is established.

    This capability is most useful for the Console Telnet session.  When a
    Console Telnet session is Buffered, a simulator will start (via BOOT CPU 
    or whatever is appropriate for a particular simulator) without needing to 
    have an active telnet connection.  When a Telnet connection comes along 
    for the telnet port, the contents of the saved buffer (which wraps on 
    overflow) are presented on the telnet session as output before session 
    traffic.  This allows the connecting telnet client to see what happened 
    before he connected since the likely reason he might be connecting to the 
    console of a background simulator is to troubleshoot unusual behavior, 
    the details of which may have already been sent to the console.

    Serial Port support:

    Serial ports may be specified as an operating system specific device names
    or using simh generic serial names.  simh generic names are of the form 
    serN, where N is from 0 thru one less than the maximum number of serial 
    ports on the local system.  The mapping of simh generic port names to OS 
    specific names can be displayed using the following command:

        sim> show serial
        Serial devices:
         ser0   COM1 (\Device\Serial0)
         ser1   COM3 (Winachcf0)        

        sim> attach MUX Line=2,Connect=ser0

    or equivalently

        sim> attach MUX Line=2,Connect=COM1

    An optional configuration string may be present after the port name.  If
    present, it must be separated from the port name with a semicolon and has
    this form:

       <rate>-<charsize><parity><stopbits>

    where:

      rate     = communication rate in bits per second
      charsize = character size in bits (5-8, including optional parity)
      parity   = parity designator (N/E/O/M/S for no/even/odd/mark/space parity)
      stopbits = number of stop bits (1, 1.5, or 2)

     As an example:

        9600-8n1

    The supported rates, sizes, and parity options are host-specific.  If 
    a configuration string is not supplied, then the default of 9600-8N1 
    is used.

    An attachment to a serial port with the '-V' switch will cause a 
    connection message to be output to the connected serial port.
    This will help to confirm the correct port has been connected and 
    that the port settings are reasonable for the connected device.
    This would be done as:
     
        sim> attach -V MUX Connect=SerN
        

    Line specific tcp listening ports are supported.  These are configured 
    using commands of the form:
     
        sim> attach MUX Line=2,port{;notelnet}

    Direct computer to computer connections (Virutal Null Modem cables) may 
    be established using the telnet protocol or via raw tcp sockets.
     
        sim> attach MUX Line=2,Connect=host:port{;notelnet}

    Computer to computer virtual connections can be one way (as illustrated 
    above) or symmetric.  A symmetric connection is configured by combining 
    a one way connection with a tcp listening port on the same line:

        sim> attach MUX Line=2,Connect=host:port,listenport

    When symmetric virtual connections are configured, incoming connections 
    on the specified listening port are checked to assure that they actually 
    come from the specified connection destination host system.



     The command syntax for a single line device (MX) is:

        sim> attach MX port{;notelnet}
        sim> attach MX Connect=serN{;config}
        sim> attach MX Connect=COM9{;config}
        sim> attach MX Connect=host:port{;notelnet}

     The command syntax for ANY multi-line device is:

        sim> attach MX port{;notelnet}              ; Defines the master listening port for the mux and optionally allows non-telnet (i.e. raw socket) operation for all lines.
        sim> attach MX Line=n,port{;notelnet}       ; Defines a line specific listen port for a particular line. Each line can have a separate listen port and the mux can have its own as well.  Optionally disable telnet wire protocol (i.e. raw socket)
        sim> attach MX Line=n,Connect=serN{;config} ; Connects line n to simh generic serial port N (port list visible with the sim> SHOW SERIAL command), the optional ";config" data specifies the speed, parity and stop bits for the connection
                                                    ; DTR (and RTS) will be raised at attach time and will drop at detach/disconnect time
        sim> attach MX Line=n,Connect=host:port{;notelnet} ; Causes a connection to be established to the designated host:port.  The actual connection will happen in a non-blocking fashion and will be completed and/or re-established by the normal tmxr_poll_conn activities
     
     All connections configured for any multiplexer device are unconfigured by:

        sim> detach MX                              ; detaches ALL connections/ports/sessions on the MUX.

    Console serial connections are achieved by:

        sim> set console serial=serN{;config}
    or
        sim> set console serial=COM2{;config}

    A line specific listening port (12366) can be specified by the following:

        sim> attach MUX Line=2,12366

    A line specific remote telnet (or raw tcp) destination can be specified 
    by the following:

        sim> attach MUX Line=2,Connect=remotehost:port

    If a connection to a remotehost:port wants a raw binary data channel 
    (instead of a telnet session) the following would be used:

        sim> attach MUX Line=2,Connect=remotehost:port;notelnet

    A single line multiplexor can indicate any of the above line options 
    without specifying a line number:

        sim> attach MUX Connect=ser0;9600-8N1
        sim> attach MUX 12366
        sim> attach MUX Connect=remotehost:port
        sim> attach MUX Connect=remotehost:port;notelnet

    A multiplexor can disconnect all (telnet, serial and outgoing) previous
    attachments with:

        sim> detach MUX

   A device emulation may choose to implement a command interface to 
   disconnect specific individual lines.  This would usually be done via
   a Unit Modifier table entry (MTAB) which dispatches the command 
   "SET dev DISCONNECT[=line]" to tmxr_dscln.  This will cause a telnet 
   connection to be closed, but a serial port will normally have DTR 
   dropped for 500ms and raised again (thus hanging up a modem on that 
   serial port).

     sim> set MUX disconnect=2

   A line which is connected to a serial port can be manually closed by
   adding the -C switch to a disconnect command.

     sim> set -C MUX disconnect=2

    Full Modem Control serial port support.

    This library supports devices which wish to emulate full modem 
    control/signalling for serial ports.  Any device emulation which wishes 
    to support this functionality for attached serial ports must call
    "tmxr_set_modem_control_passthru" before any call to tmxr_attach.  
    This disables automatic DTR (&RTS) manipulation by this library.
    Responsibility for manipulating DTR falls on the simulated operating 
    system.  Calling tmxr_set_modem_control_passthru would usually be in 
    a device reset routine.  It may also be called by a device attach
    routine based on user specified options.
    Once support for full modem control has been declared by a device 
    emulation for a particular TMXR device, this library will make no 
    direct effort to manipulate modem bits while connected to serial ports.
    The "tmxr_set_get_modem_bits" API exists to allow the device emulation 
    layer to query and control modem signals.  The "tmxr_set_config_line" 
    API exists to allow the device emulation layer to change port settings 
    (baud rate, parity and stop bits).  A modem_control enabled line 
    merely passes the VM's port status bits, data and settings through to 
    and from the serial port.  

    The "tmxr_set_get_modem_bits" and "tmxr_set_config_line" APIs will 
    ONLY work on a modem control enabled TMXR device.

*/

#define NOT_MUX_USING_CODE /* sim_tmxr library define */

#include "sim_defs.h"
#include "sim_serial.h"
#include "sim_sock.h"
#include "sim_timer.h"
#include "sim_tmxr.h"
#include "scp.h"

#include <ctype.h>
#include <math.h>

/* Telnet protocol constants - negatives are for init'ing signed char data */

/* Commands */
#define TN_IAC          0xFFu /* -1 */                  /* protocol delim */
#define TN_DONT         0xFEu /* -2 */                  /* dont */
#define TN_DO           0xFDu /* -3 */                  /* do */
#define TN_WONT         0xFCu /* -4 */                  /* wont */
#define TN_WILL         0xFBu /* -5 */                  /* will */
#define TN_SB           0xFAu /* -6 */                  /* sub-option negotiation */
#define TN_GA           0xF9u /* -7 */                  /* go ahead */
#define TN_EL           0xF8u /* -8 */                  /* erase line */
#define TN_EC           0xF7u /* -9 */                  /* erase character */
#define TN_AYT          0xF6u /* -10 */                 /* are you there */
#define TN_AO           0xF5u /* -11 */                 /* abort output */
#define TN_IP           0xF4u /* -12 */                 /* interrupt process */
#define TN_BRK          0xF3u /* -13 */                 /* break */
#define TN_DATAMK       0xF2u /* -14 */                 /* data mark */
#define TN_NOP          0xF1u /* -15 */                 /* no operation */
#define TN_SE           0xF0u /* -16 */                 /* end sub-option negot */

/* Options */

#define TN_BIN            0                             /* bin */
#define TN_ECHO           1                             /* echo */
#define TN_SGA            3                             /* sga */
#define TN_STATUS         5                             /* option status query */
#define TN_TIMING         6                             /* Timing Mark */
#define TN_NAOCRD        10                             /* Output Carriage-Return Disposition */
#define TN_NAOHTS        11                             /* Output Horizontal Tab Stops */
#define TN_NAOHTD        12                             /* Output Horizontal Tab Stop Disposition */
#define TN_NAOFFD        13                             /* Output Forfeed Disposition */
#define TN_NAOVTS        14                             /* Output Vertical Tab Stop */
#define TN_NAOVTD        15                             /* Output Vertical Tab Stop Disposition */
#define TN_NAOLFD        16                             /* Output Linefeed Disposition */
#define TN_EXTEND        17                             /* Extended Ascii */
#define TN_LOGOUT        18                             /* Logout */
#define TN_BM            19                             /* Byte Macro */
#define TN_DET           20                             /* Data Entry Terminal */
#define TN_SENDLO        23                             /* Send Location */
#define TN_TERMTY        24                             /* Terminal Type */
#define TN_ENDREC        25                             /* Terminal Type */
#define TN_TUID          26                             /* TACACS User Identification */
#define TN_OUTMRK        27                             /* Output Marking */
#define TN_TTYLOC        28                             /* Terminal Location Number */
#define TN_3270          29                             /* 3270 Regime */
#define TN_X3PAD         30                             /* X.3 PAD */
#define TN_NAWS          31                             /* Negotiate About Window Size */
#define TN_TERMSP        32                             /* Terminal Speed */
#define TN_TOGFLO        33                             /* Remote Flow Control */
#define TN_LINE          34                             /* line mode */
#define TN_XDISPL        35                             /* X Display Location */
#define TN_ENVIRO        36                             /* Environment */
#define TN_AUTH          37                             /* Authentication */
#define TN_ENCRYP        38                             /* Data Encryption */
#define TN_NEWENV        39                             /* New Environment */
#define TN_TN3270        40                             /* TN3270 Enhancements */
#define TN_CHARST        42                             /* CHARSET */
#define TN_COMPRT        44                             /* Com Port Control */
#define TN_KERMIT        47                             /* KERMIT */

#define TN_CR           015                             /* carriage return */
#define TN_LF           012                             /* line feed */
#define TN_NUL          000                             /* null */

/* Telnet line states */

#define TNS_NORM        000                             /* normal */
#define TNS_IAC         001                             /* IAC seen */
#define TNS_WILL        002                             /* WILL seen */
#define TNS_WONT        003                             /* WONT seen */
#define TNS_SKIP        004                             /* skip next cmd */
#define TNS_CRPAD       005                             /* CR padding */
#define TNS_DO          006                             /* DO request pending rejection */

/* Telnet Option Sent Flags */

#define TNOS_DONT       001                             /* Don't has been sent */
#define TNOS_WONT       002                             /* Won't has been sent */

static BITFIELD tmxr_modem_bits[] = {
  BIT(DTR),                                 /* Data Terminal Ready */
  BIT(RTS),                                 /* Request To Send     */
  BIT(DCD),                                 /* Data Carrier Detect */
  BIT(RNG),                                 /* Ring Indicator      */
  BIT(CTS),                                 /* Clear To Send       */
  BIT(DSR),                                 /* Data Set Ready      */
  ENDBITS
};

static u_char mantra[] = {                  /* Telnet Option Negotiation Mantra */
    TN_IAC, TN_WILL, TN_LINE,
    TN_IAC, TN_WILL, TN_SGA,
    TN_IAC, TN_WILL, TN_ECHO,
    TN_IAC, TN_WILL, TN_BIN,
    TN_IAC, TN_DO, TN_BIN
    };

#define TMXR_GUARD  ((int32)(lp->serport ? 1 : sizeof(mantra)))/* buffer guard */

/* Local routines */

static void tmxr_add_to_open_list (TMXR* mux);

/* Initialize the line state.

   Reset the line state to represent an idle line.  Note that we do not clear
   all of the line structure members, so a connected line remains connected
   after this call.

   Because a line break is represented by a flag in the "receive break status"
   array, we must zero that array in order to clear any pending break
   indications.
*/

static void tmxr_init_line (TMLN *lp)
{
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;
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);
    lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
    }
if (lp->loopback) {
    lp->lpbsz = lp->rxbsz;
    lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz);
    lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0;
    }
if (lp->rxpb) {
    lp->rxpboffset = lp->rxpbsize = 0;
    free (lp->rxpb);
    lp->rxpb = NULL;
    }
if (lp->txpb) {
    lp->txpbsize = lp->txppsize = lp->txppoffset = 0;
    free (lp->txpb);
    lp->txpb = NULL;
    }
memset (lp->rbr, 0, lp->rxbsz);                         /* clear break status array */
return;
}


/* Report a connection to a line.

   If the indicated line (lp) is speaking the telnet wire protocol, a 
   notification of the form:

      Connected to the <sim> simulator <dev> device, line <n>

   is sent to the newly connected line.  If the device has only one line, the
   "line <n>" part is omitted.  If the device has not been defined, the "<dev>
   device" part is omitted.

*/

static void tmxr_report_connection (TMXR *mp, TMLN *lp)
{
int32 unwritten, psave;
char cmsg[80];
char dmsg[80] = "";
char lmsg[80] = "";
char msgbuf[256] = "";

if ((!lp->notelnet) || (sim_switches & SWMASK ('V'))) {
    sprintf (cmsg, "\n\r\nConnected to the %s simulator ", sim_name);

    if (mp->dptr) {                                     /* device defined? */
        sprintf (dmsg, "%s device",                     /* report device name */
                       sim_dname (mp->dptr));

        if (mp->lines > 1)                              /* more than one line? */
            sprintf (lmsg, ", line %d", (int)(lp-mp->ldsc));/* report the line number */
        }

    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->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 */
tmxr_linemsg (lp, msgbuf);                              /* beginning of buffer */
lp->txbpi = psave;                                      /* restore insertion pointer */

unwritten = tmxr_send_buffered_data (lp);               /* send the message */

if (unwritten == 0)                                     /* buffer now empty? */
    lp->xmte = 1;                                       /* reenable transmission if paused */

lp->txcnt -= (int32)strlen (msgbuf);                    /* adjust statistics */
return;
}


/* Report a disconnection to a line.

   A notification of the form:

      Disconnected from the <sim> simulator

   is sent to the line about to be disconnected.  We do not flush the buffer
   here, because the disconnect routines will do that just after calling us.
*/

static void tmxr_report_disconnection (TMLN *lp)
{
if (lp->notelnet)
    return;
tmxr_linemsgf (lp, "\r\nDisconnected from the %s simulator\r\n\n", sim_name);/* report disconnection */
return;
}

static int32 loop_write_ex (TMLN *lp, char *buf, int32 length, t_bool prefix_datagram)
{
int32 written = 0;
int32 loopfree = lp->lpbsz - lp->lpbcnt;

if (lp->datagram && prefix_datagram) {
    if ((size_t)loopfree < (size_t)(length + sizeof(length)))
        return written;
    loop_write_ex (lp, (char *)&length, sizeof(length), FALSE);
    }
while (length) {
    int32 chunksize;

    loopfree = lp->lpbsz - lp->lpbcnt;
    if (loopfree == 0)
        break;
    if (loopfree < length)
        length = loopfree;
    if (lp->lpbpi >= lp->lpbpr)
        chunksize = lp->lpbsz - lp->lpbpi;
    else
        chunksize = lp->lpbpr - lp->lpbpi;
    if (chunksize > length)
        chunksize = length;
    memcpy (&lp->lpb[lp->lpbpi], buf, chunksize);
    buf += chunksize;
    length -= chunksize;
    written += chunksize;
    lp->lpbpi = (lp->lpbpi + chunksize) % lp->lpbsz;
    }
lp->lpbcnt += written;
return written;
}

static int32 loop_write (TMLN *lp, char *buf, int32 length)
{
return loop_write_ex (lp, buf, length, TRUE);
}

static int32 loop_read_ex (TMLN *lp, char *buf, int32 bufsize)
{
int32 bytesread = 0;

while (bufsize > 0) {
    int32 chunksize;
    int32 loopused = lp->lpbcnt;

    if (loopused < bufsize)
        bufsize = loopused;
    if (loopused == 0)
        break;
    if (lp->lpbpi > lp->lpbpr)
        chunksize = lp->lpbpi - lp->lpbpr;
    else
        chunksize = lp->lpbsz - lp->lpbpr;
    if (chunksize > bufsize)
        chunksize = bufsize;
    memcpy (buf, &lp->lpb[lp->lpbpr], chunksize);
    buf += chunksize;
    bufsize -= chunksize;
    bytesread += chunksize;
    lp->lpbpr = (lp->lpbpr + chunksize) % lp->lpbsz;
    }
lp->lpbcnt -= bytesread;
return bytesread;
}

static int32 loop_read (TMLN *lp, char *buf, int32 bufsize)
{
if (lp->datagram) {
    int32 pktsize;

    if (lp->lpbcnt < (int32)sizeof(pktsize))
        return 0;
    if ((sizeof(pktsize) != loop_read_ex (lp, (char *)&pktsize, sizeof(pktsize))) ||
        (pktsize > bufsize))
        return -1;
    bufsize = pktsize;
    }
return loop_read_ex (lp, buf, bufsize);
}

/* Read from a line.

   Up to "length" characters are read into the character buffer associated with
   line "lp".  The actual number of characters read is returned.  If no
   characters are available, 0 is returned.  If an error occurred while reading,
   -1 is returned.

   If a line break was detected on serial input, the associated receive break
   status flag will be set.  Line break indication for Telnet connections is
   embedded in the Telnet protocol and must be determined externally.
*/

static int32 tmxr_read (TMLN *lp, int32 length)
{
int32 i = lp->rxbpi;

if (lp->loopback)
    return loop_read (lp, &(lp->rxb[i]), length);
if (lp->serport)                                        /* serial port connection? */
    return sim_read_serial (lp->serport, &(lp->rxb[i]), length, &(lp->rbr[i]));
else                                                    /* Telnet connection */
    return sim_read_sock (lp->sock, &(lp->rxb[i]), length);
}


/* Write to a line.

   Up to "length" characters are written from the character buffer associated
   with "lp".  The actual number of characters written is returned.  If an error
   occurred while writing, -1 is returned.
*/

static int32 tmxr_write (TMLN *lp, int32 length)
{
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)
        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 ()));
    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)
            return written;                             /* ignore errors on datagram sockets */
        else
            return -1;                                  /* return error indication */
    else
        return written;
    }
}


/* Remove a character from the read buffer.

   The character at position "p" in the read buffer associated with line "lp" is
   removed by moving all of the following received characters down one position.
   The receive break status array is adjusted accordingly.
*/

static void tmxr_rmvrc (TMLN *lp, int32 p)
{
for ( ; p < lp->rxbpi; p++) {                           /* work from "p" through end of buffer */
    lp->rxb[p] = lp->rxb[p + 1];                        /* slide following character down */
    lp->rbr[p] = lp->rbr[p + 1];                        /* adjust break status too */
    }

lp->rbr[p] = 0;                                         /* clear potential break from vacated slot */
lp->rxbpi = lp->rxbpi - 1;                              /* drop buffer insert index */
return;
}


/* Find a line descriptor indicated by unit or number.

   If "uptr" is NULL, then the line descriptor is determined by the line number
   passed in "val".  If "uptr" is not NULL, then it must point to a unit
   associated with a line, and the line descriptor is determined by the unit
   number, which is derived by the position of the unit in the device's unit
   array.

   Note: This routine may be called with a UNIT that does not belong to the
   device indicated in the TMXR structure.  That is, the multiplexer lines may
   belong to a device other than the one attached to the socket (the HP 2100 MUX
   device is one example).  Therefore, we must look up the device from the unit
   at each call, rather than depending on the DEVICE pointer stored in the TMXR.
*/

static TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, const TMXR *mp)
{
if (mp == NULL)                                         /* invalid multiplexer descriptor? */
    return NULL;                                        /* programming error! */
if (uptr) {                                             /* called from SET? */
    DEVICE *dptr = find_dev_from_unit (uptr);           /* find device */
    if (dptr == NULL)                                   /* what?? */
        return NULL;
    val = (int32) (uptr - dptr->units);                 /* implicit line # */
    }
if ((val < 0) || (val >= mp->lines))                    /* invalid line? */
    return NULL;
return mp->ldsc + val;                                  /* line descriptor */
}


/* Get a line descriptor indicated by a string or unit.

   A pointer to the line descriptor associated with multiplexer "mp" and unit
   "uptr" or specified by string "cptr" is returned.  If "uptr" is non-null,
   then the unit number within its associated device implies the line number.
   If "uptr" is null, then the string "cptr" is parsed for a decimal line
   number.  If the line number is missing, malformed, or outside of the range of
   line numbers associated with "mp", then NULL is returned with status set to
   SCPE_ARG.

   Implementation note:

    1. A return status of SCPE_IERR implies a programming error (passing an
       invalid pointer or an invalid unit).
*/

static TMLN *tmxr_get_ldsc (UNIT *uptr, const char *cptr, TMXR *mp, t_stat *status)
{
t_value  ln;
TMLN    *lp = NULL;
t_stat   code = SCPE_OK;

if (mp == NULL)                                         /* missing mux descriptor? */
    code = SCPE_IERR;                                   /* programming error! */

else if (uptr) {                                        /* implied line form? */
    lp = tmxr_find_ldsc (uptr, mp->lines, mp);          /* determine line from unit */

    if (lp == NULL)                                     /* invalid line number? */
        code = SCPE_IERR;                               /* programming error! */
    }

else if (cptr == NULL)                                  /* named line form, parameter supplied? */
    code = SCPE_MISVAL;                                 /* no, so report missing */

else {
    ln = get_uint (cptr, 10, mp->lines - 1, &code);     /* get line number */

    if (code == SCPE_OK)                                /* line number OK? */
        lp = mp->ldsc + (int32) ln;                     /* use as index to determine line */
    }

if (status)                                             /* return value pointer supplied? */
    *status = code;                                     /* store return status value */

return lp;                                              /* return pointer to line descriptor */
}

/* Generate the Attach string which will fully configure the multiplexer

   Inputs:
        old     =       pointer to the original configuration string which will be replaced
        *mp     =       pointer to multiplexer

   Output:
        a complete attach string for the current state of the multiplexer

*/
static char *growstring(char **string, size_t growth)
{
*string = (char *)realloc (*string, 1 + (*string ? strlen (*string) : 0) + growth);
return *string + strlen(*string);
}

static char *tmxr_mux_attach_string(char *old, TMXR *mp)
{
char* tptr = NULL;
int32 i;
TMLN *lp;

free (old);
tptr = (char *) calloc (1, 1);

if (tptr == NULL)                                       /* no more mem? */
    return tptr;

if (mp->port)                                           /* copy port */
    sprintf (growstring(&tptr, 13 + strlen (mp->port)), "%s%s", mp->port, mp->notelnet ? ";notelnet" : "");
if (mp->logfiletmpl[0])                                 /* logfile info */
    sprintf (growstring(&tptr, 7 + strlen (mp->logfiletmpl)), ",Log=%s", mp->logfiletmpl);
while ((*tptr == ',') || (*tptr == ' '))
    memcpy (tptr, tptr+1, strlen(tptr+1)+1);
for (i=0; i<mp->lines; ++i) {
    char *lptr;
    lp = mp->ldsc + i;

    lptr = tmxr_line_attach_string(lp);
    if (lptr) {
        sprintf (growstring(&tptr, 10+strlen(lptr)), "%s%s", *tptr ? "," : "", lptr);
        free (lptr);
        }
    }
if (mp->lines == 1)
    while ((*tptr == ',') || (*tptr == ' '))
        memcpy (tptr, tptr+1, strlen(tptr+1)+1);
if (*tptr == '\0') {
    free (tptr);
    tptr = NULL;
    }
return tptr;
}



/* Global routines */


/* Return the Line specific attach setup currently configured for a given line

   Inputs:
        *lp     =       pointer to terminal line descriptor
   Outputs:
        a string which can be used to reconfigure the line, 
        NULL if the line isn't configured

   Note: The returned string is dynamically allocated memory and must be freed 
         when it is no longer needed by calling free

*/

char *tmxr_line_attach_string(TMLN *lp)
{
char* tptr = NULL;

tptr = (char *) calloc (1, 1);

if (tptr == NULL)                                       /* no more mem? */
    return tptr;

if (lp->destination || lp->port || lp->txlogname) {
    if ((lp->mp->lines > 1) || (lp->port))
        sprintf (growstring(&tptr, 32), "Line=%d", (int)(lp-lp->mp->ldsc));
    if (lp->modem_control != lp->mp->modem_control)
        sprintf (growstring(&tptr, 32), ",%s", lp->modem_control ? "Modem" : "NoModem");
    if (lp->txbfd && (lp->txbsz != lp->mp->buffered))
        sprintf (growstring(&tptr, 32), ",Buffered=%d", lp->txbsz);
    if (!lp->txbfd && (lp->mp->buffered > 0))
        sprintf (growstring(&tptr, 32), ",UnBuffered");
    if (lp->mp->datagram != lp->datagram)
        sprintf (growstring(&tptr, 8), ",%s", lp->datagram ? "UDP" : "TCP");
    if (lp->mp->packet != lp->packet)
        sprintf (growstring(&tptr, 8), ",Packet");
    if (lp->port)
        sprintf (growstring(&tptr, 12 + strlen (lp->port)), ",%s%s", lp->port, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : "");
    if (lp->destination) {
        if (lp->serport) {
            char portname[CBUFSIZE];

            get_glyph_nc (lp->destination, portname, ';');
            sprintf (growstring(&tptr, 25 + strlen (lp->destination)), ",Connect=%s%s%s", portname, strcmp("9600-8N1", lp->serconfig) ? ";" : "", strcmp("9600-8N1", lp->serconfig) ? lp->serconfig : "");
            }
        else
            sprintf (growstring(&tptr, 25 + strlen (lp->destination)), ",Connect=%s%s", lp->destination, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : "");
        }
    if (lp->txlogname)
        sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname);
    if (lp->loopback)
        sprintf (growstring(&tptr, 12 ), ",Loopback");
    }
if (*tptr == '\0') {
    free (tptr);
    tptr = NULL;
    }
return tptr;
}

/*

Set the connection polling interval

*/
t_stat tmxr_connection_poll_interval (TMXR *mp, uint32 seconds)
{
if (0 == seconds)
    return SCPE_ARG;
mp->poll_interval = seconds;
return SCPE_OK;
}

/* Poll for new connection

   Called from unit service routine to test for new connection

   Inputs:
        *mp     =       pointer to terminal multiplexer descriptor
   Outputs:
        line number activated, -1 if none

   If a connection order is defined for the descriptor, and the first value is
   not -1 (indicating default order), then the order array is used to find an
   open line.  Otherwise, a search is made of all lines in numerical sequence.

*/

int32 tmxr_poll_conn (TMXR *mp)
{
SOCKET newsock;
TMLN *lp;
int32 *op;
int32 i, j;
char *address;
char msg[512];
uint32 poll_time = sim_os_msec ();

if (mp->last_poll_time == 0) {                          /* first poll initializations */
    UNIT *uptr = mp->uptr;

    if (!uptr)                                          /* Attached ? */
        return -1;                                      /* No connections are possinle! */

    if (mp->poll_interval == 0)                         /* Assure reasonable polling interval */
        mp->poll_interval = TMXR_DEFAULT_CONNECT_POLL_INTERVAL;

    if (!(uptr->dynflags & TMUF_NOASYNCH)) {            /* if asynch not disabled */
        uptr->dynflags |= UNIT_TM_POLL;                 /* tag as polling unit */
        sim_cancel (uptr);
        }
    for (i=0; i < mp->lines; i++) {
        uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr;

        if (!(mp->uptr->dynflags & TMUF_NOASYNCH)) {    /* if asynch not disabled */
            uptr->dynflags |= UNIT_TM_POLL;             /* tag as polling unit */
            sim_cancel (uptr);
            }
        }
    }

if ((poll_time - mp->last_poll_time) < mp->poll_interval*1000)
    return -1;                                          /* too soon to try */

srand((unsigned int)poll_time);
tmxr_debug_trace (mp, "tmxr_poll_conn()");

mp->last_poll_time = poll_time;

/* Check for a pending Telnet/tcp connection */

if (mp->master) {
    if (mp->ring_sock != INVALID_SOCKET) {  /* Use currently 'ringing' socket if one is active */
        newsock = mp->ring_sock;
        mp->ring_sock = INVALID_SOCKET;
        address = mp->ring_ipad;
        mp->ring_ipad = NULL;
        }
    else
        newsock = sim_accept_conn_ex (mp->master, &address, (mp->packet ? SIM_SOCK_OPT_NODELAY : 0));/* poll connect */

    if (newsock != INVALID_SOCKET) {                    /* got a live one? */
        sprintf (msg, "tmxr_poll_conn() - Connection from %s", address);
        tmxr_debug_connect (mp, msg);
        op = mp->lnorder;                               /* get line connection order list pointer */
        i = mp->lines;                                  /* play it safe in case lines == 0 */
        ++mp->sessions;                                 /* count the new session */

        for (j = 0; j < mp->lines; j++, i++) {          /* find next avail line */
            if (op && (*op >= 0) && (*op < mp->lines))  /* order list present and valid? */
                i = *op++;                              /* get next line in list to try */
            else                                        /* no list or not used or range error */
                i = j;                                  /* get next sequential line */

            lp = mp->ldsc + i;                          /* get pointer to line descriptor */
            if ((lp->conn == FALSE) &&                  /* is the line available? */
                (lp->destination == NULL) &&
                (lp->master == 0) &&
                (lp->ser_connect_pending == FALSE) &&
                (lp->modem_control ? ((lp->modembits & TMXR_MDM_DTR) != 0) : TRUE))
                break;                                  /* yes, so stop search */
            }

        if (i >= mp->lines) {                           /* all busy? */
            int32 ringable_count = 0;

            for (j = 0; j < mp->lines; j++, i++) {      /* find next avail line */
                lp = mp->ldsc + j;                      /* get pointer to line descriptor */
                if ((lp->conn == FALSE) &&              /* is the line available? */
                    (lp->destination == NULL) &&
                    (lp->master == 0) &&
                    (lp->ser_connect_pending == FALSE) &&
                    ((lp->modembits & TMXR_MDM_DTR) == 0)) {
                    ++ringable_count;
                    tmxr_set_get_modem_bits (lp, TMXR_MDM_RNG, 0, NULL);
                    tmxr_debug_connect_line (lp, "tmxr_poll_conn() - Ringing line");
                    }
                }
            if (ringable_count > 0) {
                if (mp->ring_start_time == 0) {
                    mp->ring_start_time = poll_time;
                    mp->ring_sock = newsock;
                    mp->ring_ipad = address;
                    }
                else {
                    if ((poll_time - mp->ring_start_time) < TMXR_MODEM_RING_TIME*1000) {
                        mp->ring_sock = newsock;
                        mp->ring_ipad = address;
                        }
                    else {                                      /* Timeout waiting for DTR */
                        int ln;

                        /* turn off pending ring signals */
                        for (ln = 0; ln < lp->mp->lines; ln++) {
                            TMLN *tlp = lp->mp->ldsc + ln;
                            if (((tlp->destination == NULL) && (tlp->master == 0)) &&
                                (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE))
                                tlp->modembits &= ~TMXR_MDM_RNG;
                            }
                        mp->ring_start_time = 0;
                        tmxr_msg (newsock, "No answer on any connection\r\n");
                        tmxr_debug_connect (mp, "tmxr_poll_conn() - No Answer - All connections busy");
                        sim_close_sock (newsock);
                        free (address);
                        }
                    }
                }
            else {
                tmxr_msg (newsock, "All connections busy\r\n");
                tmxr_debug_connect (mp, "tmxr_poll_conn() - All connections busy");
                sim_close_sock (newsock);
                free (address);
                }
            }
        else {
            lp = mp->ldsc + i;                          /* get line desc */
            lp->conn = TRUE;                            /* record connection */
            lp->sock = newsock;                         /* save socket */
            lp->ipad = address;                         /* ip address */
            tmxr_init_line (lp);                        /* init line */
            lp->notelnet = mp->notelnet;                /* apply mux default telnet setting */
            if (!lp->notelnet) {
                sim_write_sock (newsock, (char *)mantra, sizeof(mantra));
                tmxr_debug (TMXR_DBG_XMT, lp, "Sending", (char *)mantra, sizeof(mantra));
                lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
                memset (lp->telnet_sent_opts, 0, 256);
                }
            tmxr_report_connection (mp, lp);
            lp->cnms = sim_os_msec ();                  /* time of connection */
            return i;
            }
        }                                               /* end if newsock */
    }

/* Look for per line listeners or outbound connecting sockets */
for (i = 0; i < mp->lines; i++) {                       /* check each line in sequence */
    int j, r = rand();
    lp = mp->ldsc + i;                                  /* get pointer to line descriptor */

    /* Check for pending serial port connection notification */
    
    if (lp->ser_connect_pending) {
        lp->ser_connect_pending = FALSE;
        lp->conn = TRUE;
        return i;
        }

    /* Don't service network connections for loopbacked lines */

    if (lp->loopback)
        continue;

    /* If two simulators are configured with symmetric virtual null modem 
       cables pointing at each other, there may be a problem establishing 
       a connection if both systems happen to be checking for the success
       of their connections in the exact same order.  They can each observe
       success in their respective outgoing connections, which haven't 
       actually been 'accept'ed on the peer end of the connection.  
       We address this issue by checking for the success of an outgoing
       connection and the arrival of an incoming one in a random order.
     */
    for (j=0; j<2; j++)
        switch ((j+r)&1) {
            case 0:
                if (lp->connecting) {                           /* connecting? */
                    char *sockname, *peername;

                    switch (sim_check_conn(lp->connecting, FALSE))
                        {
                        case 1:                                 /* successful connection */
                            lp->conn = TRUE;                    /* record connection */
                            lp->sock = lp->connecting;          /* it now looks normal */
                            lp->connecting = 0;
                            lp->ipad = (char *)realloc (lp->ipad, 1+strlen (lp->destination));
                            strcpy (lp->ipad, lp->destination);
                            lp->cnms = sim_os_msec ();
                            sim_getnames_sock (lp->sock, &sockname, &peername);
                            sprintf (msg, "tmxr_poll_conn() - Outgoing Line Connection to %s (%s->%s) established", lp->destination, sockname, peername);
                            tmxr_debug_connect_line (lp, msg);
                            free (sockname);
                            free (peername);
                            return i;
                        case -1:                                /* failed connection */
                            sprintf (msg, "tmxr_poll_conn() - Outgoing Line Connection to %s failed", lp->destination);
                            tmxr_debug_connect_line (lp, msg);
                            tmxr_reset_ln (lp);                 /* retry */
                            break;
                        }
                    }
                break;
            case 1:
                if (lp->master) {                                   /* Check for a pending Telnet/tcp connection */
                    while (INVALID_SOCKET != (newsock = sim_accept_conn_ex (lp->master, &address, (lp->packet ? SIM_SOCK_OPT_NODELAY : 0)))) {/* got a live one? */
                        char *sockname, *peername;

                        sim_getnames_sock (newsock, &sockname, &peername);
                        sprintf (msg, "tmxr_poll_conn() - Incoming Line Connection from %s (%s->%s)", address, peername, sockname);
                        tmxr_debug_connect_line (lp, msg);
                        free (sockname);
                        free (peername);
                        ++mp->sessions;                             /* count the new session */

                        if (lp->destination) {                      /* Virtual Null Modem Cable? */
                            char host[CBUFSIZE];

                            if (sim_parse_addr (lp->destination, host, sizeof(host), NULL, NULL, 0, NULL, address)) {
                                tmxr_msg (newsock, "Rejecting connection from unexpected source\r\n");
                                sprintf (msg, "tmxr_poll_conn() - Rejecting line connection from: %s, Expected: %s", address, host);
                                tmxr_debug_connect_line (lp, msg);
                                sim_close_sock (newsock);
                                free (address);
                                continue;                           /* Try for another connection */
                                }
                            if (lp->connecting) {
                                sprintf (msg, "tmxr_poll_conn() - aborting outgoing line connection attempt to: %s", lp->destination);
                                tmxr_debug_connect_line (lp, msg);
                                sim_close_sock (lp->connecting);    /* abort our as yet unconnnected socket */
                                lp->connecting = 0;
                                }
                            }
                        if (lp->conn == FALSE) {                    /* is the line available? */
                            if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) {
                                lp->conn = TRUE;                    /* record connection */
                                lp->sock = newsock;                 /* save socket */
                                lp->ipad = address;                 /* ip address */
                                tmxr_init_line (lp);                /* init line */
                                if (!lp->notelnet) {
                                    sim_write_sock (newsock, (char *)mantra, sizeof(mantra));
                                    tmxr_debug (TMXR_DBG_XMT, lp, "Sending", (char *)mantra, sizeof(mantra));
                                    lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
                                    memset (lp->telnet_sent_opts, 0, 256);
                                    }
                                tmxr_report_connection (mp, lp);
                                lp->cnms = sim_os_msec ();          /* time of connection */
                                return i;
                                }
                            else {
                                tmxr_msg (newsock, "Line connection not available\r\n");
                                tmxr_debug_connect_line (lp, "tmxr_poll_conn() - Line connection not available");
                                sim_close_sock (newsock);
                                free (address);
                                }
                            }
                        else {
                            tmxr_msg (newsock, "Line connection busy\r\n");
                            tmxr_debug_connect_line (lp, "tmxr_poll_conn() - Line connection busy");
                            sim_close_sock (newsock);
                            free (address);
                            }
                        }
                    }
                break;
            }

    /* Check for needed outgoing connection initiation */

    if (lp->destination && (!lp->sock) && (!lp->connecting) && (!lp->serport) && 
        (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR))) {
        sprintf (msg, "tmxr_poll_conn() - establishing outgoing connection to: %s", lp->destination);
        tmxr_debug_connect_line (lp, msg);
        lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (lp->mp->packet ? SIM_SOCK_OPT_NODELAY : 0));
        }

    }

return -1;                                              /* no new connections made */
}

/* Reset a line.

   The telnet/tcp or serial session associated with multiplexer descriptor "mp" and
   line descriptor "lp" is disconnected.  An associated tcp socket is
   closed; a serial port is closed if the closeserial parameter is true, otherwise
   for non modem control serial lines DTR is dropped and raised again after 500ms 
   to signal the attached serial device.  
*/

static t_stat tmxr_reset_ln_ex (TMLN *lp, t_bool closeserial)
{
char msg[512];

tmxr_debug_trace_line (lp, "tmxr_reset_ln_ex()");

if (lp->txlog)
    fflush (lp->txlog);                                 /* flush log */

tmxr_send_buffered_data (lp);                           /* send any buffered data */

sprintf (msg, "tmxr_reset_ln_ex(%s)", closeserial ? "TRUE" : "FALSE");
tmxr_debug_connect_line (lp, msg);

if (lp->serport) {
    if (closeserial) {
        sim_close_serial (lp->serport);
        lp->serport = 0;
        lp->ser_connect_pending = FALSE;
        free (lp->destination);
        lp->destination = NULL;
        free (lp->serconfig);
        lp->serconfig = NULL;
        lp->cnms = 0;
        lp->xmte = 1;
        }
    else
        if (!lp->modem_control) {                       /* serial connection? */
            sim_control_serial (lp->serport, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);/* drop DTR and RTS */
            sim_os_ms_sleep (TMXR_DTR_DROP_TIME);
            sim_control_serial (lp->serport, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL);/* raise DTR and RTS */
            }
    }
else                                                    /* Telnet connection */
    if (lp->sock) {
        sim_close_sock (lp->sock);                      /* close socket */
        free (lp->telnet_sent_opts);
        lp->telnet_sent_opts = NULL;
        lp->sock = 0;
        lp->conn = FALSE;
        lp->cnms = 0;
        lp->xmte = 1;
        }
free(lp->ipad);
lp->ipad = NULL;
if ((lp->destination) && (!lp->serport)) {
    if (lp->connecting) {
        sim_close_sock (lp->connecting);
        lp->connecting = 0;
        }
    if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) {
        sprintf (msg, "tmxr_reset_ln_ex() - connecting to %s", lp->destination);
        tmxr_debug_connect_line (lp, msg);
        lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (lp->mp->packet ? SIM_SOCK_OPT_NODELAY : 0));
        }
    }
tmxr_init_line (lp);                                /* initialize line state */
return SCPE_OK;
}

t_stat tmxr_close_ln (TMLN *lp)
{
tmxr_debug_trace_line (lp, "tmxr_close_ln()");
tmxr_debug_connect_line (lp, "tmxr_close_ln()");
return tmxr_reset_ln_ex (lp, TRUE);
}

t_stat tmxr_reset_ln (TMLN *lp)
{
tmxr_debug_trace_line (lp, "tmxr_reset_ln()");
return tmxr_reset_ln_ex (lp, FALSE);
}

/* Enable modem control pass thru

   Inputs:
        none
        
   Output:
        none

   Implementation note:

    1  Calling this API disables any actions on the part of this
       library to directly manipulate DTR (&RTS) on serial ports.

    2  Calling this API enables the tmxr_set_get_modem_bits and
       tmxr_set_config_line APIs.

*/
static t_stat tmxr_clear_modem_control_passthru_state (TMXR *mp, t_bool state)
{
int i;

if (mp->modem_control == state)
    return SCPE_OK;
if (mp->master)
    return SCPE_ALATT;
for (i=0; i<mp->lines; ++i) {
    TMLN *lp;

    lp = mp->ldsc + i;
    if ((lp->master)     || 
        (lp->sock)       || 
        (lp->connecting) ||
        (lp->serport))
        return SCPE_ALATT;
    }
mp->modem_control = state;
for (i=0; i<mp->lines; ++i)
    mp->ldsc[i].modem_control = state;
return SCPE_OK;
}

t_stat tmxr_set_modem_control_passthru (TMXR *mp)
{
return tmxr_clear_modem_control_passthru_state (mp, TRUE);
}

/* Disable modem control pass thru

   Inputs:
        none
        
   Output:
        none

   Implementation note:

    1  Calling this API enables this library's direct manipulation 
       of DTR (&RTS) on serial ports.

    2  Calling this API disables the tmxr_set_get_modem_bits and
       tmxr_set_config_line APIs.

    3  This API will only change the state of the modem control processing
       of this library if there are no listening ports, serial ports or 
       outgoing connecctions associated with the specified multiplexer

*/
t_stat tmxr_clear_modem_control_passthru (TMXR *mp)
{
return tmxr_clear_modem_control_passthru_state (mp, FALSE);
}

/* Manipulate the modem control bits of a specific line

   Inputs:
        *lp     =       pointer to terminal line descriptor
        bits_to_set     TMXR_MDM_DTR and/or TMXR_MDM_RTS as desired
        bits_to_clear   TMXR_MDM_DTR and/or TMXR_MDM_RTS as desired
        
   Output:
        incoming_bits   if non NULL, returns the current stat of DCD, 
                        RNG, CTS and DSR along with the current state
                        of DTR and RTS

   Implementation note:

       If a line is connected to a serial port, then these values affect 
       and reflect the state of the serial port.  If the line is connected
       to a network socket (or could be) then the network session state is
       set, cleared and/or returned.
*/
t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
{
int32 before_modem_bits, incoming_state;
DEVICE *dptr;

tmxr_debug_trace_line (lp, "tmxr_set_get_modem_bits()");

if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) ||         /* Assure only settable bits */
    (bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
    (bits_to_set & bits_to_clear))                  /* and can't set and clear the same bits */
    return SCPE_ARG;
before_modem_bits = lp->modembits;
lp->modembits |= bits_to_set;
lp->modembits &= ~(bits_to_clear | TMXR_MDM_INCOMING);
if ((lp->sock) || (lp->serport) || (lp->loopback)) {
    if (lp->modembits & TMXR_MDM_DTR) {
        incoming_state = TMXR_MDM_DSR;
        if (lp->modembits & TMXR_MDM_RTS)
            incoming_state |= TMXR_MDM_CTS;
        if (lp->halfduplex) {
            if (incoming_state & TMXR_MDM_CTS)
                incoming_state |= TMXR_MDM_DCD;
            }
        else
            incoming_state |= TMXR_MDM_DCD;
        }
    else
        incoming_state = TMXR_MDM_DCD | TMXR_MDM_DSR | ((lp->modembits & TMXR_MDM_DTR) ? 0 : TMXR_MDM_RNG);
    }
else {
    if (((before_modem_bits & TMXR_MDM_DTR) == 0) &&    /* Upward transition of DTR? */
        ((lp->modembits & TMXR_MDM_DTR) != 0)     &&
        (lp->conn == FALSE)                       &&    /* Not connected */ 
        (lp->modembits & TMXR_MDM_RNG)) {               /* and Ring Signal Present */
        if ((lp->destination == NULL) && 
            (lp->master == 0) &&
            (lp->mp && (lp->mp->ring_sock))) {
            int ln;
            
            lp->conn = TRUE;                            /* record connection */
            lp->sock = lp->mp->ring_sock;               /* save socket */
            lp->mp->ring_sock = INVALID_SOCKET;
            lp->ipad = lp->mp->ring_ipad;               /* ip address */
            lp->mp->ring_ipad = NULL;
            lp->mp->ring_start_time = 0;
            tmxr_init_line (lp);                        /* init line */
            lp->notelnet = lp->mp->notelnet;            /* apply mux default telnet setting */
            if (!lp->notelnet) {
                sim_write_sock (lp->sock, (char *)mantra, sizeof(mantra));
                tmxr_debug (TMXR_DBG_XMT, lp, "Sending", (char *)mantra, sizeof(mantra));
                lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
                memset (lp->telnet_sent_opts, 0, 256);
                }
            tmxr_report_connection (lp->mp, lp);
            lp->cnms = sim_os_msec ();                  /* time of connection */
            lp->modembits &= ~TMXR_MDM_RNG;             /* turn off ring on this line*/
            /* turn off other pending ring signals */
            for (ln = 0; ln < lp->mp->lines; ln++) {
                TMLN *tlp = lp->mp->ldsc + ln;
                if (((tlp->destination == NULL) && (tlp->master == 0)) &&
                    (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE))
                    tlp->modembits &= ~TMXR_MDM_RNG;
                }
            }
        }
    if ((lp->master) || (lp->mp && lp->mp->master) ||
        (lp->port && lp->destination))
        incoming_state = TMXR_MDM_DSR;
    else
        incoming_state = 0;
    }
lp->modembits |= incoming_state;
dptr = (lp->dptr ? lp->dptr : (lp->mp ? lp->mp->dptr : NULL));
if ((lp->modembits != before_modem_bits) && (sim_deb && lp->mp && dptr)) {
    sim_debug_bits (TMXR_DBG_MDM, dptr, tmxr_modem_bits, before_modem_bits, lp->modembits, FALSE);
    sim_debug (TMXR_DBG_MDM, dptr, " - Line %d - %p\n", (int)(lp-lp->mp->ldsc), lp->txb);
    }
if (incoming_bits)
    *incoming_bits = lp->modembits;
if (lp->mp && lp->modem_control) {                  /* This API ONLY works on modem_control enabled multiplexer lines */
    if (bits_to_set | bits_to_clear) {              /* Anything to do? */
        if (lp->loopback) {
            if ((lp->modembits ^ before_modem_bits) & TMXR_MDM_DTR) { /* DTR changed? */
                lp->ser_connect_pending = (lp->modembits & TMXR_MDM_DTR);
                lp->conn = !(lp->modembits & TMXR_MDM_DTR);
                }
            return SCPE_OK;
            }
        if (lp->serport)
            return sim_control_serial (lp->serport, bits_to_set, bits_to_clear, incoming_bits);
        if ((lp->sock) || (lp->connecting)) {
            if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) { /* drop DTR? */
                if (lp->sock)
                    tmxr_report_disconnection (lp);     /* report closure */
                tmxr_reset_ln (lp);
                }
            }
        else {
            if ((lp->destination) &&                    /* Virtual Null Modem Cable */
                (bits_to_set & ~before_modem_bits &     /* and DTR being Raised */
                 TMXR_MDM_DTR)) {
                char msg[512];

                sprintf (msg, "tmxr_set_get_modem_bits() - establishing outgoing connection to: %s", lp->destination);
                tmxr_debug_connect_line (lp, msg);
                lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (lp->mp->packet ? SIM_SOCK_OPT_NODELAY : 0));
                }
            }
        }
    return SCPE_OK;
    }
if ((lp->sock) || (lp->connecting)) {
    if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) { /* drop DTR? */
        if (lp->sock)
            tmxr_report_disconnection (lp);     /* report closure */
        tmxr_reset_ln (lp);
        }
    }
if ((lp->serport) && (!lp->loopback))
    sim_control_serial (lp->serport, 0, 0, incoming_bits);
return SCPE_INCOMP;
}

/* Enable or Disable loopback mode on a line

   Inputs:
        lp -                the line to change
        enable_loopback -   enable or disable flag

   Output:
        none

   Implementation note:

    1) When enabling loopback mode, this API will disconnect any currently 
       connected TCP or Serial session.
    2) When disabling loopback mode, prior network connections and/or 
       serial port connections will be restored.

*/
t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback)
{
if (lp->loopback == (enable_loopback != FALSE))
    return SCPE_OK;                 /* Nothing to do */
lp->loopback = (enable_loopback != FALSE);
if (lp->loopback) {
    lp->lpbsz = lp->rxbsz;
    lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz);
    lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0;
    if (!lp->conn)
        lp->ser_connect_pending = TRUE;
    }
else {
    free (lp->lpb);
    lp->lpb = NULL;
    lp->lpbsz = 0;
    }
return SCPE_OK;
}

t_bool tmxr_get_line_loopback (TMLN *lp)
{
return (lp->loopback != FALSE);
}

/* Enable or Disable halfduplex mode on a line

   Inputs:
        lp -                the line to change
        enable_halfduplex - enable or disable flag

   Output:
        none

   When a network connected line is in halfduplex mode, DCD modem signal
   track with CTS.  When not in halfduplex mode the DCD modem signal for
   network connected lines tracks with DSR.

*/
t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_halfduplex)
{
if (lp->halfduplex == (enable_halfduplex != FALSE))
    return SCPE_OK;                 /* Nothing to do */
lp->halfduplex = (enable_halfduplex != FALSE);
return SCPE_OK;
}

t_bool tmxr_get_line_halfduplex (TMLN *lp)
{
return (lp->halfduplex != FALSE);
}

t_stat tmxr_set_config_line (TMLN *lp, CONST char *config)
{
t_stat r;

tmxr_debug_trace_line (lp, "tmxr_set_config_line()");
if (lp->serport)
    r = sim_config_serial (lp->serport, config);
else {
    lp->serconfig = (char *)realloc (lp->serconfig, 1 + strlen (config));
    strcpy (lp->serconfig, config);
    r = tmxr_set_line_speed (lp, lp->serconfig);;
    if (r != SCPE_OK) {
        free (lp->serconfig);
        lp->serconfig = NULL;
        }
    }
if ((r == SCPE_OK) && (lp->mp) && (lp->mp->uptr))   /* Record port state for proper restore */
    lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
return r;
}


/* Get character from specific line

   Inputs:
        *lp     =       pointer to terminal line descriptor
   Output:
        valid + char, 0 if line

   Implementation note:

    1. If a line break was detected coincident with the current character, the
       receive break status associated with the character is cleared, and
       SCPE_BREAK is ORed into the return value.
*/

int32 tmxr_input_pending_ln (TMLN *lp)
{
return (lp->rxbpi - lp->rxbpr);
}

int32 tmxr_getc_ln (TMLN *lp)
{
int32 j; 
t_stat val = 0;
uint32 tmp;

tmxr_debug_trace_line (lp, "tmxr_getc_ln()");
if ((lp->conn && lp->rcve) &&                           /* conn & enb & */
    ((!lp->rxbps) ||                                    /* (!rate limited || enough time passed)? */
     (sim_gtime () >= lp->rxnexttime))) {
    if (!sim_send_poll_data (&lp->send, &val)) {        /* injected input characters available? */
        j = lp->rxbpi - lp->rxbpr;                      /* # input chrs */
        if (j) {                                        /* any? */
            tmp = lp->rxb[lp->rxbpr];                   /* get char */
            val = TMXR_VALID | (tmp & 0377);            /* valid + chr */
            if (lp->rbr[lp->rxbpr]) {                   /* break? */
                lp->rbr[lp->rxbpr] = 0;                 /* clear status */
                val = val | SCPE_BREAK;                 /* indicate to caller */
                }
            lp->rxbpr = lp->rxbpr + 1;                  /* adv pointer */
            }
        }
    }                                                   /* end if conn */
if (lp->rxbpi == lp->rxbpr)                             /* empty? zero ptrs */
    lp->rxbpi = lp->rxbpr = 0;
if (lp->rxbps) {
    if (val)
        lp->rxnexttime = floor (sim_gtime () + ((lp->rxdelta * sim_timer_inst_per_sec ())/lp->rxbpsfactor));
    }
tmxr_debug_return(lp, val);
return val;
}

/* Get packet from specific line

   Inputs:
        *lp     =       pointer to terminal line descriptor
        **pbuf  =       pointer to pointer of packet contents
        *psize  =       pointer to packet size
        frame_byte -    byte which separates packets in the tcp stream
                        (0 means no separation character)

   Output:
        SCPE_LOST       link state lost
        SCPE_OK         Packet returned OR no packet available

   Implementation notes:

    1. If a packet is not yet available, then the pbuf address returned is
       NULL, but success (SCPE_OK) is returned
*/

t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize)
{
return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0);
}

t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte)
{
int32 c;
size_t pktsize;
size_t fc_size = (frame_byte ? 1 : 0);

while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
    if (lp->rxpboffset + 3 > lp->rxpbsize) {
        lp->rxpbsize += 512;
        lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize);
        }
    if ((lp->rxpboffset == 0) && (fc_size) && (c != frame_byte)) {
        tmxr_debug (TMXR_DBG_PRCV, lp, "Received Unexpected Framing Byte", (char *)&lp->rxpb[lp->rxpboffset], 1);
        continue;
        }
    if ((lp->datagram) && (lp->rxpboffset == fc_size)) {
        /* Datagram packet length is provided as a part of the natural datagram 
           delivery, for TCP lines, we read the packet length from the data stream.
           So, here we stuff packet size into head of packet buffer so it looks like
           it was delivered by TCP and the below return logic doesn't have to worry */
        lp->rxpb[lp->rxpboffset++] = (uint8)(((1 + lp->rxbpi - lp->rxbpr) >> 8) & 0xFF);
        lp->rxpb[lp->rxpboffset++] = (uint8)((1 + lp->rxbpi - lp->rxbpr) & 0xFF);
        }
    lp->rxpb[lp->rxpboffset++] = c & 0xFF;
    if (lp->rxpboffset >= (2 + fc_size)) {
        pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size];
        if (pktsize == (lp->rxpboffset - 2)) {
            ++lp->rxpcnt;
            *pbuf = &lp->rxpb[2+fc_size];
            *psize = pktsize;
            lp->rxpboffset = 0;
            tmxr_debug (TMXR_DBG_PRCV, lp, "Received Packet", (char *)&lp->rxpb[2+fc_size], pktsize);
            return SCPE_OK;
            }
        }
    }
*pbuf = NULL;
*psize = 0;
if (lp->conn)
    return SCPE_OK;
return SCPE_LOST;
}

/* Poll for input

   Inputs:
        *mp     =       pointer to terminal multiplexer descriptor
   Outputs:     none
*/

void tmxr_poll_rx (TMXR *mp)
{
int32 i, nbytes, j;
TMLN *lp;

tmxr_debug_trace (mp, "tmxr_poll_rx()");
for (i = 0; i < mp->lines; i++) {                       /* loop thru lines */
    lp = mp->ldsc + i;                                  /* get line desc */
    if (!(lp->sock || lp->serport || lp->loopback) || 
        !(lp->rcve))                                    /* skip if not connected */
        continue;

    nbytes = 0;
    if (lp->rxbpi == 0)                                 /* need input? */
        nbytes = tmxr_read (lp,                         /* yes, read */
            lp->rxbsz - TMXR_GUARD);                    /* leave spc for Telnet cruft */
    else if (lp->tsta)                                  /* in Telnet seq? */
        nbytes = tmxr_read (lp,                         /* yes, read to end */
            lp->rxbsz - lp->rxbpi);

    if (nbytes < 0) {                                   /* line error? */
        if (!lp->datagram) {                            /* ignore errors reading UDP sockets */
            if (!lp->txbfd || lp->notelnet) 
                lp->txbpi = lp->txbpr = 0;              /* Drop the data we already know we can't send */
            tmxr_close_ln (lp);                         /* disconnect line */
            }
        }

    else if (nbytes > 0) {                              /* if data rcvd */

        tmxr_debug (TMXR_DBG_RCV, lp, "Received", &(lp->rxb[lp->rxbpi]), nbytes);

        j = lp->rxbpi;                                  /* start of data */
        lp->rxbpi = lp->rxbpi + nbytes;                 /* adv pointers */
        lp->rxcnt = lp->rxcnt + nbytes;

/* Examine new data, remove TELNET cruft before making input available */

        if (!lp->notelnet) {                            /* Are we looking for telnet interpretation? */
            for (; j < lp->rxbpi; ) {                   /* loop thru char */
                u_char tmp = (u_char)lp->rxb[j];        /* get char */
                switch (lp->tsta) {                     /* case tlnt state */

                case TNS_NORM:                          /* normal */
                    if (tmp == TN_IAC) {                /* IAC? */
                        lp->tsta = TNS_IAC;             /* change state */
                        tmxr_rmvrc (lp, j);             /* remove char */
                        break;
                        }
                    if ((tmp == TN_CR) && lp->dstb)     /* CR, no bin */
                        lp->tsta = TNS_CRPAD;           /* skip pad char */
                    j = j + 1;                          /* advance j */
                    break;

                case TNS_IAC:                           /* IAC prev */
                    if (tmp == TN_IAC) {                /* IAC + IAC */
                        lp->tsta = TNS_NORM;            /* treat as normal */
                        j = j + 1;                      /* advance j */
                        break;                          /* keep IAC */
                        }
                    if (tmp == TN_BRK) {                /* IAC + BRK? */
                        lp->tsta = TNS_NORM;            /* treat as normal */
                        lp->rxb[j] = 0;                 /* char is null */
                        lp->rbr[j] = 1;                 /* flag break */
                        j = j + 1;                      /* advance j */
                        break;
                        }
                    switch (tmp) {
                    case TN_WILL:                       /* IAC + WILL? */
                        lp->tsta = TNS_WILL;
                        break;
                    case TN_WONT:                       /* IAC + WONT? */
                        lp->tsta = TNS_WONT;
                        break;
                    case TN_DO:                         /* IAC + DO? */
                        lp->tsta = TNS_DO;
                        break;
                    case TN_DONT:                       /* IAC + DONT? */
                        lp->tsta = TNS_SKIP;            /* IAC + other */
                        break;
                    case TN_GA: case TN_EL:             /* IAC + other 2 byte types */
                    case TN_EC: case TN_AYT:    
                    case TN_AO: case TN_IP:
                    case TN_NOP: 
                        lp->tsta = TNS_NORM;            /* ignore */
                        break;
                    case TN_SB:                         /* IAC + SB sub-opt negotiation */
                    case TN_DATAMK:                     /* IAC + data mark */
                    case TN_SE:                         /* IAC + SE sub-opt end */
                        lp->tsta = TNS_NORM;            /* ignore */
                        break;
                        }
                    tmxr_rmvrc (lp, j);                 /* remove char */
                    break;

                case TNS_WILL:                          /* IAC+WILL prev */
                    if ((tmp == TN_STATUS) || 
                        (tmp == TN_TIMING) || 
                        (tmp == TN_NAOCRD) || 
                        (tmp == TN_NAOHTS) || 
                        (tmp == TN_NAOHTD) || 
                        (tmp == TN_NAOFFD) || 
                        (tmp == TN_NAOVTS) || 
                        (tmp == TN_NAOVTD) || 
                        (tmp == TN_NAOLFD) || 
                        (tmp == TN_EXTEND) || 
                        (tmp == TN_LOGOUT) || 
                        (tmp == TN_BM)     || 
                        (tmp == TN_DET)    || 
                        (tmp == TN_SENDLO) || 
                        (tmp == TN_TERMTY) || 
                        (tmp == TN_ENDREC) || 
                        (tmp == TN_TUID)   || 
                        (tmp == TN_OUTMRK) || 
                        (tmp == TN_TTYLOC) || 
                        (tmp == TN_3270)   || 
                        (tmp == TN_X3PAD)  || 
                        (tmp == TN_NAWS)   || 
                        (tmp == TN_TERMSP) || 
                        (tmp == TN_TOGFLO) || 
                        (tmp == TN_XDISPL) || 
                        (tmp == TN_ENVIRO) || 
                        (tmp == TN_AUTH)   || 
                        (tmp == TN_ENCRYP) || 
                        (tmp == TN_NEWENV) || 
                        (tmp == TN_TN3270) || 
                        (tmp == TN_CHARST) || 
                        (tmp == TN_COMPRT) || 
                        (tmp == TN_KERMIT)) {
                        /* Reject (DONT) these 'uninteresting' options only one time to avoid loops */
                        if (0 == (lp->telnet_sent_opts[tmp] & TNOS_DONT)) {
                            lp->notelnet = TRUE;                /* Temporarily disable so */
                            tmxr_putc_ln (lp, TN_IAC);          /* IAC gets injected bare */
                            lp->notelnet = FALSE;
                            tmxr_putc_ln (lp, TN_DONT); 
                            tmxr_putc_ln (lp, tmp); 
                            lp->telnet_sent_opts[tmp] |= TNOS_DONT;/* Record DONT sent */
                            }
                        }
                case TNS_WONT:           /* IAC+WILL/WONT prev */
                    if (tmp == TN_BIN) {                /* BIN? */
                        if (lp->tsta == TNS_WILL) {
                            lp->dstb = 0;
                            }
                        else {
                            lp->dstb = 1;
                            }
                        }
                    tmxr_rmvrc (lp, j);                 /* remove it */
                    lp->tsta = TNS_NORM;                /* next normal */
                    break;

                /* Negotiation with the HP terminal emulator "QCTerm" is not working.
                   QCTerm says "WONT BIN" but sends bare CRs.  RFC 854 says:

                     Note that "CR LF" or "CR NUL" is required in both directions
                     (in the default ASCII mode), to preserve the symmetry of the
                     NVT model.  ...The protocol requires that a NUL be inserted
                     following a CR not followed by a LF in the data stream.

                   Until full negotiation is implemented, we work around the problem
                   by checking the character following the CR in non-BIN mode and
                   strip it only if it is LF or NUL.  This should not affect
                   conforming clients.
                */

                case TNS_CRPAD:                         /* only LF or NUL should follow CR */
                    lp->tsta = TNS_NORM;                /* next normal */
                    if ((tmp == TN_LF) ||               /* CR + LF ? */
                        (tmp == TN_NUL))                /* CR + NUL? */
                        tmxr_rmvrc (lp, j);             /* remove it */
                    break;

                case TNS_DO:                            /* pending DO request */
                    if ((tmp == TN_STATUS) || 
                        (tmp == TN_TIMING) || 
                        (tmp == TN_NAOCRD) || 
                        (tmp == TN_NAOHTS) || 
                        (tmp == TN_NAOHTD) || 
                        (tmp == TN_NAOFFD) || 
                        (tmp == TN_NAOVTS) || 
                        (tmp == TN_NAOVTD) || 
                        (tmp == TN_NAOLFD) || 
                        (tmp == TN_EXTEND) || 
                        (tmp == TN_LOGOUT) || 
                        (tmp == TN_BM)     || 
                        (tmp == TN_DET)    || 
                        (tmp == TN_SENDLO) || 
                        (tmp == TN_TERMTY) || 
                        (tmp == TN_ENDREC) || 
                        (tmp == TN_TUID)   || 
                        (tmp == TN_OUTMRK) || 
                        (tmp == TN_TTYLOC) || 
                        (tmp == TN_3270)   || 
                        (tmp == TN_X3PAD)  || 
                        (tmp == TN_NAWS)   || 
                        (tmp == TN_TERMSP) || 
                        (tmp == TN_TOGFLO) || 
                        (tmp == TN_XDISPL) || 
                        (tmp == TN_ENVIRO) || 
                        (tmp == TN_AUTH)   || 
                        (tmp == TN_ENCRYP) || 
                        (tmp == TN_NEWENV) || 
                        (tmp == TN_TN3270) || 
                        (tmp == TN_CHARST) || 
                        (tmp == TN_COMPRT) || 
                        (tmp == TN_KERMIT)) {
                        /* Reject (WONT) these 'uninteresting' options only one time to avoid loops */
                        if (0 == (lp->telnet_sent_opts[tmp] & TNOS_WONT)) {
                            lp->notelnet = TRUE;                /* Temporarily disable so */
                            tmxr_putc_ln (lp, TN_IAC);          /* IAC gets injected bare */
                            lp->notelnet = FALSE;
                            tmxr_putc_ln (lp, TN_WONT); 
                            tmxr_putc_ln (lp, tmp); 
                            lp->telnet_sent_opts[tmp] |= TNOS_WONT;/* Record WONT sent */
                            }
                        }
                case TNS_SKIP: default:                 /* skip char */
                    tmxr_rmvrc (lp, j);                 /* remove char */
                    lp->tsta = TNS_NORM;                /* next normal */
                    break;
                    }                                   /* end case state */
                }                                       /* end for char */
            if (nbytes != (lp->rxbpi-lp->rxbpr)) {
                tmxr_debug (TMXR_DBG_RCV, lp, "Remaining", &(lp->rxb[lp->rxbpr]), lp->rxbpi-lp->rxbpr);
                }
            }
        }                                               /* end else nbytes */
    }                                                   /* end for lines */
for (i = 0; i < mp->lines; i++) {                       /* loop thru lines */
    lp = mp->ldsc + i;                                  /* get line desc */
    if (lp->rxbpi == lp->rxbpr)                         /* if buf empty, */
        lp->rxbpi = lp->rxbpr = 0;                      /* reset pointers */
    }                                                   /* end for */
return;
}


/* Return count of available characters for line */

int32 tmxr_rqln_bare (const TMLN *lp, t_bool speed)
{
if ((speed) && (lp->rxbps)) {                   /* consider speed and rate limiting? */
    if (sim_gtime () < lp->rxnexttime)          /* too soon? */
        return 0;
    else
        return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz : 0)) ? 1 : 0;
    }
return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz: 0));
}

int32 tmxr_rqln (const TMLN *lp)
{
return tmxr_rqln_bare (lp, TRUE);
}


/* Store character in line buffer

   Inputs:
        *lp     =       pointer to line descriptor
        chr     =       character
   Outputs:
        status  =       ok, connection lost, or stall

   Implementation note:

    1. If the line is not connected, SCPE_LOST is returned.
*/

t_stat tmxr_putc_ln (TMLN *lp, int32 chr)
{
if ((lp->conn == FALSE) &&                              /* no conn & not buffered telnet? */
    (!lp->txbfd || lp->notelnet)) {
    ++lp->txdrp;                                        /* lost */
    return SCPE_LOST;
    }
tmxr_debug_trace_line (lp, "tmxr_putc_ln()");
#define TXBUF_AVAIL(lp) ((lp->serport ? 2: lp->txbsz) - tmxr_tqln (lp))
#define TXBUF_CHAR(lp, c) {                               \
    lp->txb[lp->txbpi++] = (char)(c);                     \
    lp->txbpi %= lp->txbsz;                               \
    if (lp->txbpi == lp->txbpr)                           \
        lp->txbpr = (1+lp->txbpr)%lp->txbsz, ++lp->txdrp; \
    }
if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {/* room for char (+ IAC)? */
    if ((TN_IAC == (u_char) chr) && (!lp->notelnet))    /* char == IAC in telnet session? */
        TXBUF_CHAR (lp, TN_IAC);                        /* stuff extra IAC char */
    TXBUF_CHAR (lp, chr);                               /* buffer char & adv pointer */
    if ((!lp->txbfd) && (TXBUF_AVAIL (lp) <= TMXR_GUARD))/* near full? */
        lp->xmte = 0;                                   /* disable line */
    if (lp->txlog) {                                    /* log if available */
        extern TMLN *sim_oline;                         /* Make sure to avoid recursion */
        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 */
    return SCPE_OK;                                     /* char sent */
    }
++lp->txdrp; lp->xmte = 0;                              /* no room, dsbl line */
return SCPE_STALL;                                      /* char not sent */
}

/* Store packet in line buffer

   Inputs:
        *lp     =       pointer to line descriptor
        *buf    =       pointer to packet data
        size    =       size of packet
        frame_char =    inter-packet franing character (0 means no frame character)

   Outputs:
        status  =       ok, connection lost, or stall

   Implementation notea:

    1. If the line is not connected, SCPE_LOST is returned.
    2. If prior packet transmission still in progress, SCPE_STALL is 
       returned and no packet data is stored.  The caller must retry later.
*/
t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size)
{
return tmxr_put_packet_ln_ex (lp, buf, size, 0);
}

t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte)
{
t_stat r;
size_t fc_size = (frame_byte ? 1 : 0);
size_t pktlen_size = (lp->datagram ? 0 : 2);

if ((!lp->conn) && (!lp->loopback))
    return SCPE_LOST;
if (lp->txppoffset < lp->txppsize) {
    tmxr_debug (TMXR_DBG_PXMT, lp, "Skipped Sending Packet - Transmit Busy", (char *)&lp->txpb[3], size);
    return SCPE_STALL;
    }
if (lp->txpbsize < size + pktlen_size + fc_size) {
    lp->txpbsize = size + pktlen_size + fc_size;
    lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize);
    }
lp->txpb[0] = frame_byte;
if (!lp->datagram) {
    lp->txpb[0+fc_size] = (size >> 8) & 0xFF;
    lp->txpb[1+fc_size] = size & 0xFF;
    }
memcpy (lp->txpb + pktlen_size + fc_size, buf, size);
lp->txppsize = size + pktlen_size + fc_size;
lp->txppoffset = 0;
tmxr_debug (TMXR_DBG_PXMT, lp, "Sending Packet", (char *)&lp->txpb[pktlen_size+fc_size], size);
++lp->txpcnt;
while ((lp->txppoffset < lp->txppsize) && 
       (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
   ++lp->txppoffset;
tmxr_send_buffered_data (lp);
return (lp->conn || lp->loopback) ? SCPE_OK : SCPE_LOST;
}

/* Poll for output

   Inputs:
        *mp     =       pointer to terminal multiplexer descriptor
   Outputs:
        none
*/

void tmxr_poll_tx (TMXR *mp)
{
int32 i, nbytes;
TMLN *lp;

tmxr_debug_trace (mp, "tmxr_poll_tx()");
for (i = 0; i < mp->lines; i++) {                       /* loop thru lines */
    lp = mp->ldsc + i;                                  /* get line desc */
    if (!lp->conn)                                      /* skip if !conn */
        continue;
    nbytes = tmxr_send_buffered_data (lp);              /* buffered bytes */
    if (nbytes == 0) {                                  /* buf empty? enab line */
#if defined(SIM_ASYNCH_MUX)
        UNIT *ruptr = lp->uptr ? lp->uptr : lp->mp->uptr;
        if ((ruptr->dynflags & UNIT_TM_POLL) &&
            sim_asynch_enabled &&
            tmxr_rqln (lp))
            _sim_activate (ruptr, 0);
#endif
        lp->xmte = 1;                                   /* enable line transmit */
        }
    }                                                   /* end for */
return;
}


/* Send buffered data across network

   Inputs:
        *lp     =       pointer to line descriptor
   Outputs:
        returns number of bytes still buffered
*/

int32 tmxr_send_buffered_data (TMLN *lp)
{
int32 nbytes, sbytes;
t_stat r;

tmxr_debug_trace_line (lp, "tmxr_send_buffered_data()");
nbytes = tmxr_tqln(lp);                                 /* avail bytes */
if (nbytes) {                                           /* >0? write */
    if (lp->txbpr < lp->txbpi)                          /* no wrap? */
        sbytes = tmxr_write (lp, nbytes);               /* write all data */
    else
        sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);/* write to end buf */
    if (sbytes >= 0) {                                  /* ok? */
        tmxr_debug (TMXR_DBG_XMT, lp, "Sent", &(lp->txb[lp->txbpr]), sbytes);
        lp->txbpr = (lp->txbpr + sbytes);               /* update remove ptr */
        if (lp->txbpr >= lp->txbsz)                     /* wrap? */
            lp->txbpr = 0;
        lp->txcnt = lp->txcnt + sbytes;                 /* update counts */
        nbytes = nbytes - sbytes;
        if ((nbytes == 0) && (lp->datagram))            /* if Empty buffer on datagram line */
            lp->txbpi = lp->txbpr = 0;                  /* Start next packet at beginning of buffer */
        }
    if (sbytes < 0) {                                   /* I/O Error? */
        lp->txbpi = lp->txbpr = 0;                      /* Drop the data we already know we can't send */
        lp->rxpboffset = lp->txppoffset = lp->txppsize = 0;/* Drop the data we already know we can't send */
        tmxr_close_ln (lp);                             /*  close line/port on error */
        return nbytes;                                  /*  done now. */
        }
    if (nbytes && (lp->txbpr == 0))     {               /* more data and wrap? */
        sbytes = tmxr_write (lp, nbytes);
        if (sbytes > 0) {                               /* ok */
            tmxr_debug (TMXR_DBG_XMT, lp, "Sent", lp->txb, sbytes);
            lp->txbpr = (lp->txbpr + sbytes);           /* update remove ptr */
            if (lp->txbpr >= lp->txbsz)                 /* wrap? */
                lp->txbpr = 0;
            lp->txcnt = lp->txcnt + sbytes;             /* update counts */
            nbytes = nbytes - sbytes;
            }
        }
    }                                                   /* end if nbytes */
while ((lp->txppoffset < lp->txppsize) &&               /* buffered packet data? */
       (lp->txbsz > nbytes) &&                          /* and room in xmt buffer */
       (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
   ++lp->txppoffset;
if ((nbytes == 0) && (tmxr_tqln(lp) > 0))
    return tmxr_send_buffered_data (lp);
return tmxr_tqln(lp) + tmxr_tpqln(lp);
}


/* Return count of buffered characters for line */

int32 tmxr_tqln (const TMLN *lp)
{
return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? lp->txbsz: 0));
}

/* Return count of buffered packet characters for line */

int32 tmxr_tpqln (const TMLN *lp)
{
return (lp->txppsize - lp->txppoffset);
}

/* Return transmit packet busy status for line */

t_bool tmxr_tpbusyln (const TMLN *lp)
{
return (0 != (lp->txppsize - lp->txppoffset));
}

static void _mux_detach_line (TMLN *lp, t_bool close_listener, t_bool close_connecting)
{
if (close_listener && lp->master) {
    sim_close_sock (lp->master);
    lp->master = 0;
    free (lp->port);
    lp->port = NULL;
    }
if (lp->sock) {                             /* if existing tcp, drop it */
    tmxr_report_disconnection (lp);         /* report disconnection */
    tmxr_reset_ln (lp);
    }
if (close_connecting) {
    free (lp->destination);
    lp->destination = NULL;
    if (lp->connecting) {                   /* if existing outgoing tcp, drop it */
        lp->sock = lp->connecting;
        lp->connecting = 0;
        tmxr_reset_ln (lp);
        }
    }
if (lp->serport) {                          /* close current serial connection */
    tmxr_reset_ln (lp);
    sim_control_serial (lp->serport, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);/* drop DTR and RTS */
    sim_close_serial (lp->serport);
    lp->serport = 0;
    free (lp->serconfig);
    lp->serconfig = NULL;
    free (lp->destination);
    lp->destination = NULL;
    }
tmxr_set_line_loopback (lp, FALSE);
}

t_stat tmxr_detach_ln (TMLN *lp)
{
UNIT *uptr = NULL;

tmxr_debug_trace_line (lp, "tmxr_detach_ln()");
_mux_detach_line (lp, TRUE, TRUE);
if (lp->mp) {
    if (lp->uptr)
        uptr = lp->uptr;
    else
        uptr = lp->mp->uptr;
    }
if (uptr && uptr->filename) {
    /* Revise the unit's connect string to reflect the current attachments */
    uptr->filename = tmxr_mux_attach_string (uptr->filename, lp->mp);
    /* No connections or listeners exist, then we're equivalent to being fully detached.  We should reflect that */
    if (uptr->filename == NULL)
        tmxr_detach (lp->mp, uptr);
    }
return SCPE_OK;
}

static int32 _tmln_speed_delta (CONST char *cptr)
{
struct {
    const char *bps;
    int32 delta;
    } *spd, speeds[] = {
    {"50",      TMLN_SPD_50_BPS},
    {"75",      TMLN_SPD_75_BPS},
    {"110",     TMLN_SPD_110_BPS},
    {"134",     TMLN_SPD_134_BPS},
    {"150",     TMLN_SPD_150_BPS},
    {"300",     TMLN_SPD_300_BPS},
    {"600",     TMLN_SPD_600_BPS},
    {"1200",    TMLN_SPD_1200_BPS},
    {"1800",    TMLN_SPD_1800_BPS},
    {"2000",    TMLN_SPD_2000_BPS},
    {"2400",    TMLN_SPD_2400_BPS},
    {"3600",    TMLN_SPD_3600_BPS},
    {"4800",    TMLN_SPD_4800_BPS},
    {"7200",    TMLN_SPD_7200_BPS},
    {"9600",    TMLN_SPD_9600_BPS},
    {"19200",   TMLN_SPD_19200_BPS},
    {"38400",   TMLN_SPD_38400_BPS},
    {"57600",   TMLN_SPD_57600_BPS},
    {"76800",   TMLN_SPD_76800_BPS},
    {"115200",  TMLN_SPD_115200_BPS},
    {"0",       0}};                    /* End of List, last valid value */
int nspeed;
char speed[24];

nspeed = (uint32)strtotv (cptr, &cptr, 10);
if ((*cptr != '\0') && (*cptr != '-') && (*cptr != '*'))
    return -1;
sprintf (speed, "%d", nspeed);

spd = speeds;
while (1) {
    if (0 == strcmp(spd->bps, speed))
        return spd->delta;
    if (spd->delta == 0)
        break;
    ++spd;
    }
return -1;
}

t_stat tmxr_set_line_speed (TMLN *lp, CONST char *speed)
{
UNIT *uptr;
CONST char *cptr;
t_stat r;

if (!speed || !*speed)
    return SCPE_2FARG;
if (_tmln_speed_delta (speed) < 0)
    return SCPE_ARG;
lp->rxbps = (uint32)strtotv (speed, &cptr, 10);
if (*cptr == '*') {
    uint32 rxbpsfactor = (uint32) get_uint (cptr+1, 10, 32, &r);
    if (r != SCPE_OK)
        return r;
    lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE * rxbpsfactor;
    }
lp->rxdelta = _tmln_speed_delta (speed);
lp->rxnexttime = 0.0;
uptr = lp->uptr;
if ((!uptr) && (lp->mp))
    uptr = lp->mp->uptr;
if (uptr)
    uptr->wait = lp->rxdelta;
if (lp->rxbpsfactor == 0.0)
    lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE;
lp->txbps = lp->rxbps;
lp->txdelta = lp->rxdelta;
lp->txnexttime = lp->rxnexttime;
return SCPE_OK;
}


/* Open a master listening socket (and all of the other variances of connections).

   A listening socket for the port number described by "cptr" is opened for the
   multiplexer associated with descriptor "mp".  If the open is successful, all
   lines not currently otherwise connected (via serial, outgoing or direct 
   listener) are initialized for Telnet connections.

   Initialization for all connection styles (MUX wide listener, per line serial, 
   listener, outgoing, logging, buffering) are handled by this routine.

*/

t_stat tmxr_open_master (TMXR *mp, CONST char *cptr)
{
int32 i, line, nextline = -1;
char tbuf[CBUFSIZE], listen[CBUFSIZE], destination[CBUFSIZE], 
     logfiletmpl[CBUFSIZE], buffered[CBUFSIZE], hostport[CBUFSIZE], 
     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;
    if (lp->rxbpsfactor == 0.0)
        lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE;
    }
mp->ring_sock = INVALID_SOCKET;
free (mp->ring_ipad);
mp->ring_ipad = NULL;
mp->ring_start_time = 0;
tmxr_debug_trace (mp, "tmxr_open_master()");
while (*tptr) {
    line = nextline;
    memset(logfiletmpl, '\0', sizeof(logfiletmpl));
    memset(listen,      '\0', sizeof(listen));
    memset(destination, '\0', sizeof(destination));
    memset(buffered,    '\0', sizeof(buffered));
    memset(port,        '\0', sizeof(port));
    memset(option,      '\0', sizeof(option));
    memset(speed,       '\0', sizeof(speed));
    nolog = notelnet = listennotelnet = loopback = FALSE;
    datagram = mp->datagram;
    packet = mp->packet;
    if (mp->buffered)
        sprintf(buffered, "%d", mp->buffered);
    if (line != -1)
        notelnet = listennotelnet = mp->notelnet;
    modem_control = mp->modem_control;
    while (*tptr) {
        tptr = get_glyph_nc (tptr, tbuf, ',');
        if (!tbuf[0])
            break;
        cptr = tbuf;
        if (!isdigit(*cptr)) {
            char gbuf[CBUFSIZE];
            CONST char *init_cptr = cptr;

            cptr = get_glyph (cptr, gbuf, '=');
            if (0 == MATCH_CMD (gbuf, "LINE")) {
                if ((NULL == cptr) || ('\0' == *cptr))
                    return sim_messagef (SCPE_2FARG, "Missing Line Specifier\n");
                nextline = (int32) get_uint (cptr, 10, mp->lines-1, &r);
                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);
                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;
                }
           if ((0 == MATCH_CMD (gbuf, "NOBUFFERED")) || 
                (0 == MATCH_CMD (gbuf, "UNBUFFERED"))) {
                if ((NULL != cptr) && ('\0' != *cptr))
                    return sim_messagef (SCPE_2MARG, "Unexpected Unbuffered Specifier: %s\n", cptr);
                buffered[0] = '\0';
                continue;
                }
            if (0 == MATCH_CMD (gbuf, "BUFFERED")) {
                if ((NULL == cptr) || ('\0' == *cptr))
                    strcpy (buffered, "32768");
                else {
                    i = (int32) get_uint (cptr, 10, 1024*1024, &r);
                    if (r || (i == 0))
                        return sim_messagef (SCPE_ARG, "Invalid Buffered Specifier: %s\n", cptr);
                    sprintf(buffered, "%d", i);
                    }
                continue;
                }
            if (0 == MATCH_CMD (gbuf, "NOLOG")) {
                if ((NULL != cptr) && ('\0' != *cptr))
                    return sim_messagef (SCPE_2MARG, "Unexpected NoLog Specifier: %s\n", cptr);
                nolog = TRUE;
                continue;
                }
            if (0 == MATCH_CMD (gbuf, "NOMODEM")) {
                if ((NULL != cptr) && ('\0' != *cptr))
                    return sim_messagef (SCPE_2MARG, "Unexpected NoModem Specifier: %s\n", cptr);
                modem_control = FALSE;
                continue;
                }
            if (0 == MATCH_CMD (gbuf, "MODEM")) {
                if ((NULL != cptr) && ('\0' != *cptr))
                    return sim_messagef (SCPE_2MARG, "Unexpected Modem Specifier: %s\n", cptr);
                modem_control = TRUE;
                continue;
                }
            if ((0 == MATCH_CMD (gbuf, "DATAGRAM")) || (0 == MATCH_CMD (gbuf, "UDP"))) {
                if ((NULL != cptr) && ('\0' != *cptr))
                    return sim_messagef (SCPE_2MARG, "Unexpected Datagram Specifier: %s\n", cptr);
                notelnet = datagram = TRUE;
                continue;
                }
            if (0 == MATCH_CMD (gbuf, "PACKET")) {
                if ((NULL != cptr) && ('\0' != *cptr))
                    return sim_messagef (SCPE_2MARG, "Unexpected Packet Specifier: %s\n", cptr);
                packet = TRUE;
                continue;
                }
            if ((0 == MATCH_CMD (gbuf, "STREAM")) || (0 == MATCH_CMD (gbuf, "TCP"))) {
                if ((NULL != cptr) && ('\0' != *cptr))
                    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");
                strcpy (destination, cptr);
                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 : ""));
                strcpy (speed, cptr);
                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);
                get_glyph (cptr, tptr, 0);                  /* upcase this string */
                if (0 == MATCH_CMD (cptr, "NOTELNET"))
                    listennotelnet = TRUE;
                else
                    if (0 == MATCH_CMD (cptr, "TELNET"))
                        listennotelnet = FALSE;
                    else
                        return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr);
                }
            cptr = init_cptr;
            }
        cptr = get_glyph_nc (cptr, port, ';');
        sock = sim_master_sock (port, &r);                      /* make master socket to validate port */
        if (r)
            return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port);
        if (sock == INVALID_SOCKET)                             /* open error */
            return sim_messagef (SCPE_OPENERR, "Can't open network port: %s\n", port);
        sim_close_sock (sock);
        sim_os_ms_sleep (2);                                    /* let the close finish (required on some platforms) */
        strcpy (listen, port);
        cptr = get_glyph (cptr, option, ';');
        if (option[0]) {
            if (0 == MATCH_CMD (option, "NOTELNET"))
                listennotelnet = TRUE;
            else
                if (0 == MATCH_CMD (option, "TELNET"))
                    listennotelnet = FALSE;
                else
                    return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", option);
            }
        }
    if (destination[0]) {
        /* Validate destination */
        serport = sim_open_serial (destination, NULL, &r);
        if (serport != INVALID_HANDLE) {
            sim_close_serial (serport);
            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);
            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
                    if (0 == MATCH_CMD (eptr, "TELNET"))
                        if (datagram)
                            return sim_messagef (SCPE_ARG, "Telnet invalid on Datagram socket\n");
                        else
                            notelnet = FALSE;
                    else
                        return sim_messagef (SCPE_ARG, "Unexpected specifier: %s\n", eptr);
                }
            sock = sim_connect_sock_ex (NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (packet ? SIM_SOCK_OPT_NODELAY : 0));
            if (sock != INVALID_SOCKET)
                sim_close_sock (sock);
            else
                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);
            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);
                if (mp->lines > 1)
                    sprintf(lp->txlogname, "%s_%d", mp->logfiletmpl, i);
                else
                    strcpy (lp->txlogname, mp->logfiletmpl);
                r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref);
                if (r != SCPE_OK) {
                    free (lp->txlogname);
                    lp->txlogname = NULL;
                    break;
                    }
                }
            }
        mp->buffered = atoi(buffered);
        for (i = 0; i < mp->lines; i++) { /* initialize line buffers */
            lp = mp->ldsc + i;
            if (mp->buffered) {
                lp->txbsz = mp->buffered;
                lp->txbfd = 1;
                lp->rxbsz = mp->buffered;
                }
            else {
                lp->txbsz = TMXR_MAXBUF;
                lp->txbfd = 0;
                lp->rxbsz = TMXR_MAXBUF;
                }
            lp->txbpi = lp->txbpr = 0;
            lp->txb = (char *)realloc(lp->txb, lp->txbsz);
            lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
            lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
            }
        if (nolog) {
            mp->logfiletmpl[0] = '\0';
            for (i = 0; i < mp->lines; i++) { /* close line logs */
                lp = mp->ldsc + i;
                free(lp->txlogname);
                lp->txlogname = NULL;
                if (lp->txlog) {
                    sim_close_logfile (&lp->txlogref);
                    lp->txlog = NULL;
                    }
                }
            }
        if ((listen[0]) && (!datagram)) {
            sock = sim_master_sock (listen, &r);            /* make master socket */
            if (r)
                return sim_messagef (SCPE_ARG, "Invalid network listen port: %s\n", listen);
            if (sock == INVALID_SOCKET)                     /* open error */
                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);
            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;
            mp->notelnet = listennotelnet;                  /* save desired telnet behavior flag */
            for (i = 0; i < mp->lines; i++) {               /* initialize lines */
                lp = mp->ldsc + i;
                lp->mp = mp;                                /* set the back pointer */
                lp->packet = mp->packet;

                if (lp->serport) {                          /* serial port attached? */
                    tmxr_reset_ln (lp);                     /* close current serial connection */
                    sim_control_serial (lp->serport, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);/* drop DTR and RTS */
                    sim_close_serial (lp->serport);
                    lp->serport = 0;
                    free (lp->serconfig);
                    lp->serconfig = NULL;
                    }
                else {
                    if (speed[0])
                        tmxr_set_line_speed (lp, speed);
                    }
                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");
            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);
                }
            }
        if (destination[0]) {
            if (mp->lines > 1)
                return sim_messagef (SCPE_ARG, "Ambiguous Destination specification\n");
            lp = &mp->ldsc[0];
            serport = sim_open_serial (destination, lp, &r);
            if (serport != INVALID_HANDLE) {
                _mux_detach_line (lp, TRUE, TRUE);
                if (lp->mp && lp->mp->master) {             /* if existing listener, close it */
                    sim_close_sock (lp->mp->master);
                    lp->mp->master = 0;
                    free (lp->mp->port);
                    lp->mp->port = NULL;
                    }
                lp->destination = (char *)malloc(1+strlen(destination));
                strcpy (lp->destination, destination);
                lp->mp = mp;
                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 */
                    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 */
                        }
                    else
                        return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n");
                    }
                lp->packet = packet;
                sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (packet ? SIM_SOCK_OPT_NODELAY : 0));
                if (sock != INVALID_SOCKET) {
                    _mux_detach_line (lp, FALSE, TRUE);
                    lp->destination = (char *)malloc(1+strlen(hostport));
                    strcpy (lp->destination, hostport);
                    lp->mp = mp;
                    if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
                        lp->connecting = sock;
                        lp->ipad = (char *)malloc (1 + strlen (lp->destination));
                        strcpy (lp->ipad, lp->destination);
                        }
                    else
                        sim_close_sock (sock);
                    lp->notelnet = notelnet;
                    tmxr_init_line (lp);                    /* init the line state */
                    if (speed[0] && (!datagram))
                        tmxr_set_line_speed (lp, speed);
                    return SCPE_OK;
                    }
                else
                    return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n", datagram ? "Datagram" : "Stream", datagram ? listen : "", datagram ? "<->" : "", hostport);
                }
            }
        }
    else {                                                  /* line specific attach */
        lp = &mp->ldsc[line];
        lp->mp = mp;
        if (logfiletmpl[0]) {
            sim_close_logfile (&lp->txlogref);
            lp->txlog = NULL;
            lp->txlogname = (char *)realloc (lp->txlogname, 1 + strlen (logfiletmpl));
            strcpy (lp->txlogname, logfiletmpl);
            r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref);
            if (r == SCPE_OK)
                setvbuf(lp->txlog, NULL, _IOFBF, 65536);
            else {
                free (lp->txlogname);
                lp->txlogname = NULL;
                return sim_messagef (r, "Can't open log file: %s\n", logfiletmpl);
                }
            }
        if (buffered[0] == '\0') {
            lp->rxbsz = lp->txbsz = TMXR_MAXBUF;
            lp->txbfd = 0;
            }
        else {
            lp->rxbsz = lp->txbsz = atoi(buffered);
            lp->txbfd = 1;
            }
        lp->txbpi = lp->txbpr = 0;
        lp->txb = (char *)realloc (lp->txb, lp->txbsz);
        lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
        lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
        lp->packet = packet;
        if (nolog) {
            free(lp->txlogname);
            lp->txlogname = NULL;
            if (lp->txlog) {
                sim_close_logfile (&lp->txlogref);
                lp->txlog = NULL;
                }
            }
        if ((listen[0]) && (!datagram)) {
            if ((mp->lines == 1) && (mp->master))
                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);
            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;
            }
        if (destination[0]) {
            serport = sim_open_serial (destination, lp, &r);
            if (serport != INVALID_HANDLE) {
                _mux_detach_line (lp, TRUE, TRUE);
                lp->destination = (char *)malloc(1+strlen(destination));
                strcpy (lp->destination, destination);
                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 */
                    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 */
                        }
                    else
                        return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n");
                    }
                sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (packet ? SIM_SOCK_OPT_NODELAY : 0));
                if (sock != INVALID_SOCKET) {
                    _mux_detach_line (lp, FALSE, TRUE);
                    lp->destination = (char *)malloc(1+strlen(hostport));
                    strcpy (lp->destination, hostport);
                    if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
                        lp->connecting = sock;
                        lp->ipad = (char *)malloc (1 + strlen (lp->destination));
                        strcpy (lp->ipad, lp->destination);
                        }
                    else
                        sim_close_sock (sock);
                    lp->notelnet = notelnet;
                    tmxr_init_line (lp);                    /* init the line state */
                    }
                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);
            }
        lp->modem_control = modem_control;
        if (speed[0] && (!datagram) && (!lp->serport))
            tmxr_set_line_speed (lp, speed);
        r = SCPE_OK;
        }
    }
if (r == SCPE_OK)
    tmxr_add_to_open_list (mp);
return r;
}


/* Declare which unit polls for input 

   Inputs:
        *mp     =       the mux
        line    =       the line number
        *uptr_poll =    the unit which polls

   Outputs:
        none

   Implementation note:

        Only devices which poll on a unit different from the unit provided
        at MUX attach time need call this function.  Calling this API is
        necessary for asynchronous multiplexer support and unnecessary 
        otherwise.

*/

t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll)
{
if ((line < 0) || (line >= mp->lines))
    return SCPE_ARG;
mp->ldsc[line].uptr = uptr_poll;
return SCPE_OK;
}

/* Declare which unit polls for output 

   Inputs:
        *mp     =       the mux
        line    =       the line number
        *uptr_poll =    the unit which polls for output

   Outputs:
        none

   Implementation note:

        Only devices which poll on a unit different from the unit provided
        at MUX attach time need call this function ABD different from the
        unit which polls for input.  Calling this API is necessary for 
        asynchronous multiplexer support and unnecessary otherwise.

*/

t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll)
{
if ((line < 0) || (line >= mp->lines))
    return SCPE_ARG;
mp->ldsc[line].o_uptr = uptr_poll;
return SCPE_OK;
}

/* Declare which units are the console input and out devices

   Inputs:
        *rxuptr =    the console input unit
        *txuptr =    the console output unit

   Outputs:
        none

   Implementation note:

        This routine is exported by the tmxr library so that it gets 
        defined to code which uses it by including sim_tmxr.h.  Including
        sim_tmxr.h is necessary so that sim_activate is properly defined
        in the caller's code to actually call tmxr_activate.

*/

t_stat tmxr_set_console_units (UNIT *rxuptr, UNIT *txuptr)
{
extern TMXR sim_con_tmxr;

tmxr_set_line_unit (&sim_con_tmxr, 0, rxuptr);
tmxr_set_line_output_unit (&sim_con_tmxr, 0, txuptr);
return SCPE_OK;
}


static TMXR **tmxr_open_devices = NULL;
static int tmxr_open_device_count = 0;

#if defined(SIM_ASYNCH_MUX)
pthread_t           sim_tmxr_poll_thread;          /* Polling Thread Id */
#if defined(_WIN32) || defined(VMS)
pthread_t           sim_tmxr_serial_poll_thread;   /* Serial Polling Thread Id */
pthread_cond_t      sim_tmxr_serial_startup_cond;
#endif
pthread_mutex_t     sim_tmxr_poll_lock;
pthread_cond_t      sim_tmxr_poll_cond;
pthread_cond_t      sim_tmxr_startup_cond;
int32               sim_tmxr_poll_count = 0;
t_bool              sim_tmxr_poll_running = FALSE;

static void *
_tmxr_poll(void *arg)
{
struct timeval timeout;
int timeout_usec;
DEVICE *dptr = tmxr_open_devices[0]->dptr;
UNIT **units = NULL;
UNIT **activated = NULL;
SOCKET *sockets = NULL;
int wait_count = 0;

/* Boost Priority for this I/O thread vs the CPU instruction execution 
   thread which, in general, won't be readily yielding the processor when 
   this thread needs to run */
sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);

sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - starting\n");

units = (UNIT **)calloc(FD_SETSIZE, sizeof(*units));
activated = (UNIT **)calloc(FD_SETSIZE, sizeof(*activated));
sockets = (SOCKET *)calloc(FD_SETSIZE, sizeof(*sockets));
timeout_usec = 1000000;
pthread_mutex_lock (&sim_tmxr_poll_lock);
pthread_cond_signal (&sim_tmxr_startup_cond);   /* Signal we're ready to go */
while (sim_asynch_enabled) {
    int i, j, status, select_errno;
    fd_set readfds, errorfds;
    int socket_count;
    SOCKET max_socket_fd;
    TMXR *mp;
    DEVICE *d;

    if ((tmxr_open_device_count == 0) || (!sim_is_running)) {
        for (j=0; j<wait_count; ++j) {
            d = find_dev_from_unit(activated[j]);
            sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Removing interest in %s. Other interest: %d\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count);
            --activated[j]->a_poll_waiter_count;
            --sim_tmxr_poll_count;
            }
        break;
        }
    /* If we started something we should wait for, let it finish before polling again */
    if (wait_count) {
        sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - waiting for %d units\n", wait_count);
        pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock);
        sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - continuing with timeout of %dms\n", timeout_usec/1000);
        }
    FD_ZERO (&readfds);
    FD_ZERO (&errorfds);
    for (i=max_socket_fd=socket_count=0; i<tmxr_open_device_count; ++i) {
        mp = tmxr_open_devices[i];
        if ((mp->master) && (mp->uptr->dynflags&UNIT_TM_POLL)) {
            units[socket_count] = mp->uptr;
            sockets[socket_count] = mp->master;
            FD_SET (mp->master, &readfds);
            FD_SET (mp->master, &errorfds);
            if (mp->master > max_socket_fd)
                max_socket_fd = mp->master;
            ++socket_count;
            }
        for (j=0; j<mp->lines; ++j) {
            if (mp->ldsc[j].sock) {
                units[socket_count] = mp->ldsc[j].uptr;
                if (units[socket_count] == NULL)
                    units[socket_count] = mp->uptr;
                sockets[socket_count] = mp->ldsc[j].sock;
                FD_SET (mp->ldsc[j].sock, &readfds);
                FD_SET (mp->ldsc[j].sock, &errorfds);
                if (mp->ldsc[j].sock > max_socket_fd)
                    max_socket_fd = mp->ldsc[j].sock;
                ++socket_count;
                }
#if !defined(_WIN32) && !defined(VMS)
            if (mp->ldsc[j].serport) {
                units[socket_count] = mp->ldsc[j].uptr;
                if (units[socket_count] == NULL)
                    units[socket_count] = mp->uptr;
                sockets[socket_count] = mp->ldsc[j].serport;
                FD_SET (mp->ldsc[j].serport, &readfds);
                FD_SET (mp->ldsc[j].serport, &errorfds);
                if (mp->ldsc[j].serport > max_socket_fd)
                    max_socket_fd = mp->ldsc[j].serport;
                ++socket_count;
                }
#endif
            if (mp->ldsc[j].connecting) {
                units[socket_count] = mp->uptr;
                sockets[socket_count] = mp->ldsc[j].connecting;
                FD_SET (mp->ldsc[j].connecting, &readfds);
                FD_SET (mp->ldsc[j].connecting, &errorfds);
                if (mp->ldsc[j].connecting > max_socket_fd)
                    max_socket_fd = mp->ldsc[j].connecting;
                ++socket_count;
                }
            if (mp->ldsc[j].master) {
                units[socket_count] = mp->uptr;
                sockets[socket_count] = mp->ldsc[j].master;
                FD_SET (mp->ldsc[j].master, &readfds);
                FD_SET (mp->ldsc[j].master, &errorfds);
                if (mp->ldsc[j].master > max_socket_fd)
                    max_socket_fd = mp->ldsc[j].master;
                ++socket_count;
                }
            }
        }
    pthread_mutex_unlock (&sim_tmxr_poll_lock);
    if (timeout_usec > 1000000)
        timeout_usec = 1000000;
    timeout.tv_sec = timeout_usec/1000000;
    timeout.tv_usec = timeout_usec%1000000;
    select_errno = 0;
    if (socket_count == 0) {
        sim_os_ms_sleep (timeout_usec/1000);
        status = 0;
        }
    else
        status = select (1+(int)max_socket_fd, &readfds, NULL, &errorfds, &timeout);
    select_errno = errno;
    wait_count=0;
    pthread_mutex_lock (&sim_tmxr_poll_lock);
    switch (status) {
        case 0:     /* timeout */
            for (i=max_socket_fd=socket_count=0; i<tmxr_open_device_count; ++i) {
                mp = tmxr_open_devices[i];
                if (mp->master) {
                    if (!mp->uptr->a_polling_now) {
                        mp->uptr->a_polling_now = TRUE;
                        mp->uptr->a_poll_waiter_count = 0;
                        d = find_dev_from_unit(mp->uptr);
                        sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating %s to poll connect\n", sim_uname(mp->uptr));
                        pthread_mutex_unlock (&sim_tmxr_poll_lock);
                        _sim_activate (mp->uptr, 0);
                        pthread_mutex_lock (&sim_tmxr_poll_lock);
                        }
                    if (mp->txcount) {
                        timeout_usec = 10000; /* Wait 10ms next time (this gets doubled below) */
                        mp->txcount = 0;
                        }
                    }
                for (j=0; j<mp->lines; ++j) {
                    if ((mp->ldsc[j].conn) && (mp->ldsc[j].uptr)) {
                        if (tmxr_tqln(&mp->ldsc[j]) || tmxr_rqln (&mp->ldsc[j])) {
                            timeout_usec = 10000; /* Wait 10ms next time (this gets doubled below) */
                            /* More than one socket can be associated with the 
                               same unit.  Make sure to only activate it one time */
                            if (!mp->ldsc[j].uptr->a_polling_now) {
                                mp->ldsc[j].uptr->a_polling_now = TRUE;
                                mp->ldsc[j].uptr->a_poll_waiter_count = 0;
                                d = find_dev_from_unit(mp->ldsc[j].uptr);
                                sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Line %d Activating %s to poll data: %d/%d\n", 
                                    j, sim_uname(mp->ldsc[j].uptr), tmxr_tqln(&mp->ldsc[j]), tmxr_rqln (&mp->ldsc[j]));
                                pthread_mutex_unlock (&sim_tmxr_poll_lock);
                                _sim_activate (mp->ldsc[j].uptr, 0);
                                pthread_mutex_lock (&sim_tmxr_poll_lock);
                                }
                            }
                        }
                    }
                }
            sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - Poll Timeout - %dms\n", timeout_usec/1000);
            timeout_usec *= 2;     /* Double timeout time */  
            break;
        case SOCKET_ERROR:
            wait_count = 0;
            if (select_errno == EINTR)
                break;
            sim_printf ("select() returned -1, errno=%d - %s\r\n", select_errno, strerror(select_errno));
            abort();
            break;
        default:
            wait_count = 0;
            for (i=0; i<socket_count; ++i) {
                if (FD_ISSET(sockets[i], &readfds) || 
                    FD_ISSET(sockets[i], &errorfds)) {
                    /* More than one socket can be associated with the 
                       same unit.  Only activate one time */
                    for (j=0; j<wait_count; ++j)
                        if (activated[j] == units[i])
                            break;
                    if (j == wait_count) {
                        activated[j] = units[i];
                        ++wait_count;
                        if (!activated[j]->a_polling_now) {
                            activated[j]->a_polling_now = TRUE;
                            activated[j]->a_poll_waiter_count = 1;
                            d = find_dev_from_unit(activated[j]);
                            sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating for data %s\n", sim_uname(activated[j]));
                            pthread_mutex_unlock (&sim_tmxr_poll_lock);
                            _sim_activate (activated[j], 0);
                            pthread_mutex_lock (&sim_tmxr_poll_lock);
                            }
                        else {
                            d = find_dev_from_unit(activated[j]);
                            sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Already Activated %s%d %d times\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count);
                            ++activated[j]->a_poll_waiter_count;
                            }
                        }
                    }
                }
            if (wait_count)
                timeout_usec = 10000; /* Wait 10ms next time */
            break;
        }
    sim_tmxr_poll_count += wait_count;
    }
pthread_mutex_unlock (&sim_tmxr_poll_lock);
free(units);
free(activated);
free(sockets);

sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - exiting\n");

return NULL;
}

#if defined(_WIN32)
static void *
_tmxr_serial_poll(void *arg)
{
int timeout_usec;
DEVICE *dptr = tmxr_open_devices[0]->dptr;
UNIT **units = NULL;
UNIT **activated = NULL;
SERHANDLE *serports = NULL;
int wait_count = 0;

/* Boost Priority for this I/O thread vs the CPU instruction execution 
   thread which, in general, won't be readily yielding the processor when 
   this thread needs to run */
sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);

sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - starting\n");

units = (UNIT **)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*units));
activated = (UNIT **)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*activated));
serports = (SERHANDLE *)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*serports));
timeout_usec = 1000000;
pthread_mutex_lock (&sim_tmxr_poll_lock);
pthread_cond_signal (&sim_tmxr_serial_startup_cond);   /* Signal we're ready to go */
while (sim_asynch_enabled) {
    int i, j;
    DWORD status;
    int serport_count;
    TMXR *mp;
    DEVICE *d;

    if ((tmxr_open_device_count == 0) || (!sim_is_running)) {
        for (j=0; j<wait_count; ++j) {
            d = find_dev_from_unit(activated[j]);
            sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_poll() - Removing interest in %s. Other interest: %d\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count);
            --activated[j]->a_poll_waiter_count;
            --sim_tmxr_poll_count;
            }
        break;
        }
    /* If we started something we should wait for, let it finish before polling again */
    if (wait_count) {
        sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - waiting for %d units\n", wait_count);
        pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock);
        sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - continuing with timeout of %dms\n", timeout_usec/1000);
        }
    for (i=serport_count=0; i<tmxr_open_device_count; ++i) {
        mp = tmxr_open_devices[i];
        for (j=0; j<mp->lines; ++j) {
            if (mp->ldsc[j].serport) {
                units[serport_count] = mp->ldsc[j].uptr;
                if (units[serport_count] == NULL)
                    units[serport_count] = mp->uptr;
                serports[serport_count] = mp->ldsc[j].serport;
                ++serport_count;
                }
            }
        }
    if (serport_count == 0)                                 /* No open serial ports? */
        break;                                              /* We're done */
    pthread_mutex_unlock (&sim_tmxr_poll_lock);
    if (timeout_usec > 1000000)
        timeout_usec = 1000000;
    status = WaitForMultipleObjects (serport_count, serports, FALSE, timeout_usec/1000);
    wait_count=0;
    pthread_mutex_lock (&sim_tmxr_poll_lock);
    switch (status) {
        case WAIT_FAILED:
            sim_printf ("WaitForMultipleObjects() Failed, LastError=%d\r\n", GetLastError());
            abort();
            break;
        case WAIT_TIMEOUT:
            sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - Poll Timeout - %dms\n", timeout_usec/1000);
            timeout_usec *= 2;     /* Double timeout time */  
            break;
        default:
            i = status - WAIT_OBJECT_0;
            wait_count = 0;
            j = wait_count;
            activated[j] = units[i];
            ++wait_count;
            if (!activated[j]->a_polling_now) {
                activated[j]->a_polling_now = TRUE;
                activated[j]->a_poll_waiter_count = 1;
                d = find_dev_from_unit(activated[j]);
                sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_poll() - Activating for data %s\n", sim_uname(activated[j]));
                pthread_mutex_unlock (&sim_tmxr_poll_lock);
                _sim_activate (activated[j], 0);
                pthread_mutex_lock (&sim_tmxr_poll_lock);
                }
            else {
                d = find_dev_from_unit(activated[j]);
                sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_poll() - Already Activated %s%d %d times\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count);
                ++activated[j]->a_poll_waiter_count;
                }
            if (wait_count)
                timeout_usec = 10000; /* Wait 10ms next time */
            break;
        }
    sim_tmxr_poll_count += wait_count;
    }
pthread_mutex_unlock (&sim_tmxr_poll_lock);
free(units);
free(activated);
free(serports);

sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - exiting\n");

return NULL;
}
#endif /* _WIN32 */

#if defined(VMS)

#include <descrip.h>
#include <ttdef.h>
#include <tt2def.h>
#include <iodef.h>
#include <ssdef.h>
#include <starlet.h>
#include <unistd.h>

typedef struct {
    unsigned short status;
    unsigned short count;
    unsigned int dev_status; } IOSB;

#define MAXIMUM_WAIT_OBJECTS 64             /* Number of possible concurrently opened serial ports */

pthread_cond_t      sim_serial_line_startup_cond;


static void *
_tmxr_serial_line_poll(void *arg)
{
TMLN *lp = (TMLN *)arg;
DEVICE *dptr = tmxr_open_devices[0]->dptr;
UNIT *uptr = (lp->uptr ? lp->uptr : lp->mp->uptr);
DEVICE *d = find_dev_from_unit(uptr);
int wait_count = 0;

/* Boost Priority for this I/O thread vs the CPU instruction execution 
   thread which, in general, won't be readily yielding the processor when 
   this thread needs to run */
sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);

sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_line_poll() - starting\n");

pthread_mutex_lock (&sim_tmxr_poll_lock);
pthread_cond_signal (&sim_serial_line_startup_cond);   /* Signal we're ready to go */
while (sim_asynch_enabled) {
    int i, j;
    int serport_count;
    TMXR *mp = lp->mp;
    unsigned int status, term[2];
    unsigned char buf[4];
    IOSB iosb;

    if ((tmxr_open_device_count == 0) || (!sim_is_running)) {
        if (wait_count) {
            sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_line_poll() - Removing interest in %s. Other interest: %d\n", sim_uname(uptr), uptr->a_poll_waiter_count);
            --uptr->a_poll_waiter_count;
            --sim_tmxr_poll_count;
            }
        break;
        }
    /* If we started something we should wait for, let it finish before polling again */
    if (wait_count) {
        sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_line_poll() - waiting for %d units\n", wait_count);
        pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock);
        sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_line_poll() - continuing with timeout of 1 sec\n");
        }
    lp->a_active = TRUE;
    pthread_mutex_unlock (&sim_tmxr_poll_lock);
    term[0] = term[1] = 0;
    status = sys$qiow (0, lp->serport, 
                       IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO,
                       &iosb, 0, 0, buf, 1, 1, term, 0, 0);
    if (status != SS$_NORMAL) {
        sim_printf ("_tmxr_serial_line_poll() - QIO Failed, Status=%d\r\n", status);
        abort();
        }
    wait_count = 0;
    sys$synch (0, &iosb);
    pthread_mutex_lock (&sim_tmxr_poll_lock);
    lp->a_active = FALSE;
    if (iosb.count == 1) {
        lp->a_buffered_character = buf[0] | SCPE_KFLAG;
        wait_count = 1;
        if (!uptr->a_polling_now) {
            uptr->a_polling_now = TRUE;
            uptr->a_poll_waiter_count = 1;
            sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_line_poll() - Activating for data %s\n", sim_uname(uptr));
            pthread_mutex_unlock (&sim_tmxr_poll_lock);
            _sim_activate (uptr, 0);
            pthread_mutex_lock (&sim_tmxr_poll_lock);
            }
        else {
            sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_line_poll() - Already Activated %s%d %d times\n", sim_uname(uptr), uptr->a_poll_waiter_count);
            ++uptr->a_poll_waiter_count;
            }
        }
    sim_tmxr_poll_count += wait_count;
    }
pthread_mutex_unlock (&sim_tmxr_poll_lock);

sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_line_poll() - exiting\n");

return NULL;
}

static void *
_tmxr_serial_poll(void *arg)
{
int timeout_usec;
DEVICE *dptr = tmxr_open_devices[0]->dptr;
TMLN **lines = NULL;
pthread_t *threads = NULL;

/* Boost Priority for this I/O thread vs the CPU instruction execution 
   thread which, in general, won't be readily yielding the processor when 
   this thread needs to run */

sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - starting\n");

lines = (TMLN **)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*lines));
threads = (pthread_t *)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*threads));
pthread_mutex_lock (&sim_tmxr_poll_lock);
pthread_cond_signal (&sim_tmxr_serial_startup_cond);   /* Signal we're ready to go */
pthread_cond_init (&sim_serial_line_startup_cond, NULL);
while (sim_asynch_enabled) {
    pthread_attr_t attr;
    int i, j;
    int serport_count;
    TMXR *mp;
    DEVICE *d;

    if ((tmxr_open_device_count == 0) || (!sim_is_running))
        break;
    pthread_attr_init (&attr);
    pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
    for (i=serport_count=0; i<tmxr_open_device_count; ++i) {
        mp = tmxr_open_devices[i];
        for (j=0; j<mp->lines; ++j) {
            if (mp->ldsc[j].serport) {
                lines[serport_count] = &mp->ldsc[j];
                pthread_create (&threads[serport_count], &attr, _tmxr_serial_line_poll, (void *)&mp->ldsc[j]);
                pthread_cond_wait (&sim_serial_line_startup_cond, &sim_tmxr_poll_lock); /* Wait for thread to stabilize */
                ++serport_count;
                }
            }
        }
    pthread_attr_destroy( &attr);
    if (serport_count == 0)                                 /* No open serial ports? */
        break;                                              /* We're done */
    pthread_mutex_unlock (&sim_tmxr_poll_lock);
    for (i=0; i<serport_count; i++)
        pthread_join (threads[i], NULL);
    pthread_mutex_lock (&sim_tmxr_poll_lock);
    }
pthread_mutex_unlock (&sim_tmxr_poll_lock);
pthread_cond_destroy (&sim_serial_line_startup_cond);
free(lines);
free(threads);

sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - exiting\n");

return NULL;
}
#endif /* VMS */

#endif /* defined(SIM_ASYNCH_MUX) */

t_stat tmxr_start_poll (void)
{
#if defined(SIM_ASYNCH_MUX)
pthread_mutex_lock (&sim_tmxr_poll_lock);
if ((tmxr_open_device_count > 0) && 
    sim_asynch_enabled           && 
    sim_is_running               && 
    !sim_tmxr_poll_running) {
    pthread_attr_t attr;

    pthread_cond_init (&sim_tmxr_startup_cond, NULL);
    pthread_attr_init (&attr);
    pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
    pthread_create (&sim_tmxr_poll_thread, &attr, _tmxr_poll, NULL);
    pthread_attr_destroy( &attr);
    pthread_cond_wait (&sim_tmxr_startup_cond, &sim_tmxr_poll_lock); /* Wait for thread to stabilize */
    pthread_cond_destroy (&sim_tmxr_startup_cond);
    sim_tmxr_poll_running = TRUE;
    }
pthread_mutex_unlock (&sim_tmxr_poll_lock);
#endif
return SCPE_OK;
}

t_stat tmxr_stop_poll (void)
{
#if defined(SIM_ASYNCH_MUX)
pthread_mutex_lock (&sim_tmxr_poll_lock);
if (sim_tmxr_poll_running) {
    pthread_cond_signal (&sim_tmxr_poll_cond);
    pthread_mutex_unlock (&sim_tmxr_poll_lock);
    pthread_join (sim_tmxr_poll_thread, NULL);
    sim_tmxr_poll_running = FALSE;
    /* Transitioning from asynch mode so kick all polling units onto the event queue */
    if (tmxr_open_device_count) {
        int i, j;

        for (i=0; i<tmxr_open_device_count; ++i) {
            TMXR *mp = tmxr_open_devices[i];

            if (mp->uptr)
                _sim_activate (mp->uptr, 0);
            for (j = 0; j < mp->lines; ++j)
                if (mp->ldsc[j].uptr)
                    _sim_activate (mp->ldsc[j].uptr, 0);
            }
        }
    }
else
    pthread_mutex_unlock (&sim_tmxr_poll_lock);
#endif
return SCPE_OK;
}

static void tmxr_add_to_open_list (TMXR* mux)
{
int i;
t_bool found = FALSE;

#if defined(SIM_ASYNCH_MUX)
pthread_mutex_lock (&sim_tmxr_poll_lock);
#endif
for (i=0; i<tmxr_open_device_count; ++i)
    if (tmxr_open_devices[i] == mux) {
        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.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
}

static void _tmxr_remove_from_open_list (TMXR* mux)
{
int i, j;

#if defined(SIM_ASYNCH_MUX)
tmxr_stop_poll ();
pthread_mutex_lock (&sim_tmxr_poll_lock);
#endif
for (i=0; i<tmxr_open_device_count; ++i)
    if (tmxr_open_devices[i] == mux) {
        for (j=i+1; j<tmxr_open_device_count; ++j)
            tmxr_open_devices[j-1] = tmxr_open_devices[j];
        --tmxr_open_device_count;
        break;
        }
#if defined(SIM_ASYNCH_MUX)
pthread_mutex_unlock (&sim_tmxr_poll_lock);
#endif
}

static t_stat _tmxr_locate_line_send_expect (const char *cptr, SEND **snd, EXPECT **exp)
{
char gbuf[CBUFSIZE];
DEVICE *dptr;
int i;
t_stat r;

if (snd)
    *snd = NULL;
if (exp)
    *exp = NULL;
cptr = get_glyph(cptr, gbuf, ':');
dptr = find_dev (gbuf);                 /* device match? */
if (!dptr)
    return SCPE_ARG;

for (i=0; i<tmxr_open_device_count; ++i)
    if (tmxr_open_devices[i]->dptr == dptr) {
        int line = (int)get_uint (cptr, 10, tmxr_open_devices[i]->lines, &r);
        if (r != SCPE_OK)
            return r;
        if (snd)
            *snd = &tmxr_open_devices[i]->ldsc[line].send;
        if (exp)
            *exp = &tmxr_open_devices[i]->ldsc[line].expect;
        return SCPE_OK;
        }
return SCPE_ARG;
}

t_stat tmxr_locate_line_send (const char *cptr, SEND **snd)
{
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);
}

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;

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;
        }
    }
tmxr_add_to_open_list (mp);
return SCPE_OK;
}


t_stat tmxr_startup (void)
{
return SCPE_OK;
}

t_stat tmxr_shutdown (void)
{
if (tmxr_open_device_count)
    return SCPE_IERR;
return SCPE_OK;
}

t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc)
{
int i, j;

if (0 == tmxr_open_device_count)
    fprintf(st, "No Attached Multiplexer Devices\n");
else {
    for (i=0; i<tmxr_open_device_count; ++i) {
        TMXR *mp = tmxr_open_devices[i];
        TMLN *lp;
        char *attach;

        fprintf(st, "Multiplexer device: %s", (mp->dptr ? sim_dname (mp->dptr) : ""));
        if (mp->lines > 1) {
            fprintf(st, ", ");
            tmxr_show_lines(st, NULL, 0, mp);
            }
        if (mp->packet)
            fprintf(st, ", Packet");
        if (mp->datagram)
            fprintf(st, ", UDP");
        if (mp->notelnet)
            fprintf(st, ", Telnet=disabled");
        if (mp->modem_control)
            fprintf(st, ", ModemControl=enabled");
        if (mp->buffered)
            fprintf(st, ", Buffered=%d", mp->buffered);
        attach = tmxr_mux_attach_string (NULL, mp);
        if (attach)
            fprintf(st, ",\n    attached to %s, ", attach);
        free (attach);
        tmxr_show_summ(st, NULL, 0, mp);
        fprintf(st, ", sessions=%d", mp->sessions);
        if (mp->lines == 1) {
            if (mp->ldsc->rxbps) {
                fprintf(st, ", Speed=%d", mp->ldsc->rxbps);
                if (mp->ldsc->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
                    fprintf(st, "*%.0f", mp->ldsc->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
                fprintf(st, " bps");
                }
            }
        fprintf(st, "\n");
        if (mp->ring_start_time) {
            fprintf (st, "    incoming Connection from: %s ringing for %d milliseconds\n", mp->ring_ipad, sim_os_msec () - mp->ring_start_time);
            }
        for (j = 0; j < mp->lines; j++) {
            lp = mp->ldsc + j;
            if (mp->lines > 1) {
                if (lp->dptr && (mp->dptr != lp->dptr))
                    fprintf (st, "Device: %s ", sim_dname(lp->dptr));
                fprintf (st, "Line: %d", j);
                if (mp->notelnet != lp->notelnet)
                    fprintf (st, " - %stelnet", lp->notelnet ? "no" : "");
                if (lp->uptr && (lp->uptr != lp->mp->uptr))
                    fprintf (st, " - Unit: %s", sim_uname (lp->uptr));
                if (mp->modem_control != lp->modem_control)
                    fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled");
                if (lp->loopback)
                    fprintf(st, ", Loopback");
                if (lp->rxbps) {
                    fprintf(st, ", Speed=%d", lp->rxbps);
                    if (lp->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
                        fprintf(st, "*%.0f", lp->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
                    fprintf(st, " bps");
                    }
                fprintf (st, "\n");
                }
            if ((!lp->sock) && (!lp->connecting) && (!lp->serport) && (!lp->master)) {
                if (lp->modem_control)
                    tmxr_fconns (st, lp, -1);
                continue;
                }
            tmxr_fconns (st, lp, -1);
            tmxr_fstats (st, lp, -1);
            }
        }
    }
return SCPE_OK;
}


/* Close a master listening socket.

   The listening socket associated with multiplexer descriptor "mp" is closed
   and deallocated.  In addition, all current Telnet sessions are disconnected.
   Serial and outgoing sessions are also disconnected.
*/

t_stat tmxr_close_master (TMXR *mp)
{
int32 i;
TMLN *lp;

for (i = 0; i < mp->lines; i++) {  /* loop thru conn */
    lp = mp->ldsc + i;

    if (!lp->destination && lp->sock) {                 /* not serial and is connected? */
        tmxr_report_disconnection (lp);                 /* report disconnection */
        tmxr_reset_ln (lp);                             /* disconnect line */
        }
    else {
        if (lp->sock) {
            tmxr_report_disconnection (lp);             /* report disconnection */
            tmxr_reset_ln (lp);
            }
        if (lp->serport) {
            sim_control_serial (lp->serport, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);/* drop DTR and RTS */
            tmxr_close_ln (lp);
            }
        free (lp->destination);
        lp->destination = NULL;
        if (lp->connecting) {
            lp->sock = lp->connecting;
            lp->connecting = 0;
            tmxr_reset_ln (lp);
            }
        lp->conn = FALSE;
        }
    if (lp->master) {
        sim_close_sock (lp->master);                    /* close master socket */
        lp->master = 0;
        free (lp->port);
        lp->port = NULL;
        }
    lp->txbfd = 0;
    free (lp->txb);
    lp->txb = NULL;
    free (lp->rxb);
    lp->rxb = NULL;
    free (lp->rbr);
    lp->rbr = NULL;
    lp->modembits = 0;
    }

if (mp->master)
    sim_close_sock (mp->master);                        /* close master socket */
mp->master = 0;
free (mp->port);
mp->port = NULL;
if (mp->ring_sock != INVALID_SOCKET) {
    sim_close_sock (mp->ring_sock);
    mp->ring_sock = INVALID_SOCKET;
    free (mp->ring_ipad);
    mp->ring_ipad = NULL;
    mp->ring_start_time = 0;
    }
_tmxr_remove_from_open_list (mp);
return SCPE_OK;
}


/* Detach unit from master socket and close all active network connections 
   and/or serial ports.

   Note that we return SCPE_OK, regardless of whether a listening socket was
   attached.  
*/

t_stat tmxr_detach (TMXR *mp, UNIT *uptr)
{
int32 i;

if (!(uptr->flags & UNIT_ATT))                          /* attached? */
    return SCPE_OK;
tmxr_close_master (mp);                                 /* close master socket */
free (uptr->filename);                                  /* free setup string */
uptr->filename = NULL;
uptr->tmxr = NULL;
mp->last_poll_time = 0;
for (i=0; i < mp->lines; i++) {
    UNIT *uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr;
    UNIT *o_uptr = mp->ldsc[i].o_uptr ? mp->ldsc[i].o_uptr : mp->uptr;

    uptr->dynflags &= ~UNIT_TM_POLL;                    /* no polling */
    o_uptr->dynflags &= ~UNIT_TM_POLL;                  /* no polling */
    }
uptr->flags &= ~(UNIT_ATT);                             /* not attached */
uptr->dynflags &= ~(UNIT_TM_POLL|TMUF_NOASYNCH);        /* no polling, not asynch disabled  */
return SCPE_OK;
}


t_stat tmxr_activate (UNIT *uptr, int32 interval)
{
if (uptr->dynflags & UNIT_TMR_UNIT)
    return sim_timer_activate (uptr, interval);
#if defined(SIM_ASYNCH_MUX)
if ((!(uptr->dynflags & UNIT_TM_POLL)) || 
    (!sim_asynch_enabled)) {
    return _sim_activate (uptr, interval);
    }
return SCPE_OK;
#else
return _sim_activate (uptr, interval);
#endif
}

t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime)
{
#if defined(SIM_ASYNCH_MUX)
if ((!(uptr->dynflags & UNIT_TM_POLL)) || 
    (!sim_asynch_enabled)) {
    return _sim_activate_after (uptr, (double)usecs_walltime);
    }
return SCPE_OK;
#else
return _sim_activate_after (uptr, (double)usecs_walltime);
#endif
}

t_stat tmxr_activate_after_abs (UNIT *uptr, uint32 usecs_walltime)
{
#if defined(SIM_ASYNCH_MUX)
if ((!(uptr->dynflags & UNIT_TM_POLL)) || 
    (!sim_asynch_enabled)) {
    return _sim_activate_after_abs (uptr, (double)usecs_walltime);
    }
return SCPE_OK;
#else
return _sim_activate_after_abs (uptr, (double)usecs_walltime);
#endif
}


t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval)
{
int32 tmr = sim_rtcn_calibrated_tmr ();
int32 ticks = (interval + (sim_rtcn_tick_size (tmr)/2))/sim_rtcn_tick_size (tmr);/* Convert to ticks */

return tmxr_clock_coschedule_tmr (uptr, tmr, ticks);
}

t_stat tmxr_clock_coschedule_abs (UNIT *uptr, int32 interval)
{
sim_cancel (uptr);
return tmxr_clock_coschedule (uptr, interval);
}

#define MIN(a,b) (((a) < (b)) ? (a) : (b))

t_stat tmxr_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 ticks)
{
TMXR *mp = (TMXR *)uptr->tmxr;
int32 interval = ticks * sim_rtcn_tick_size (tmr);

#if defined(SIM_ASYNCH_MUX)
if ((!(uptr->dynflags & UNIT_TM_POLL)) || 
    (!sim_asynch_enabled)) {
    return sim_clock_coschedule (uptr, tmr, ticks);
    }
return SCPE_OK;
#else
if (mp) {
    int32 i, soon = interval;
    double sim_gtime_now = sim_gtime ();

    for (i = 0; i < mp->lines; i++) {
        TMLN *lp = &mp->ldsc[i];

        if (tmxr_rqln_bare (lp, FALSE)) {
            int32 due;

            if (lp->rxbps)
                if (lp->rxnexttime > sim_gtime_now)
                    due = (int32)(lp->rxnexttime - sim_gtime_now);
                else
                    due = sim_processing_event ? 1 : 0;     /* avoid potential infinite loop if called from service routine */
            else
                due = (int32)((uptr->wait * sim_timer_inst_per_sec ())/TMXR_RX_BPS_UNIT_SCALE);
            soon = MIN(soon, due);
            }
        }
    if (soon != interval) {
        sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "scheduling %s after %d instructions\n", sim_uname (uptr), soon);
        return _sim_activate (uptr, soon);
        }
    }
sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "coscheduling %s after interval %d ticks\n", sim_uname (uptr), ticks);
return sim_clock_coschedule_tmr (uptr, tmr, ticks);
#endif
}

t_stat tmxr_clock_coschedule_tmr_abs (UNIT *uptr, int32 tmr, int32 ticks)
{
sim_cancel (uptr);
return tmxr_clock_coschedule_tmr (uptr, tmr, ticks);
}

/* Generic Multiplexer attach help */

t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
TMXR *mux = (TMXR *)dptr->help_ctx;
t_bool single_line = FALSE;               /* default to Multi-Line help */

if (mux)
   single_line = (mux->lines == 1);

if (!flag)
    fprintf (st, "%s Multiplexer Attach Help\n\n", dptr->name);
if (single_line) {          /* Single Line Multiplexer */
    fprintf (st, "The %s multiplexer may be connected to terminal emulators supporting the\n", dptr->name);
    fprintf (st, "Telnet protocol via sockets, or to hardware terminals via host serial\n");
    fprintf (st, "ports.\n\n");
    if (mux->modem_control) {
        fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name);
        fprintf (st, "passing port configuration information and modem signals.\n");
        }
    fprintf (st, "A Telnet listening port can be configured with:\n\n");
    fprintf (st, "   sim> ATTACH %s {interface:}port\n\n", dptr->name);
    fprintf (st, "Line buffering can be enabled for the %s device with:\n\n", dptr->name);
    fprintf (st, "   sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name);
    fprintf (st, "Line buffering can be disabled for the %s device with:\n\n", dptr->name);
    fprintf (st, "   sim> ATTACH %s NoBuffer\n\n", dptr->name);
    fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n");
    fprintf (st, "The outbound traffic the %s device can be logged to a file with:\n", dptr->name);
    fprintf (st, "   sim> ATTACH %s Log=LogFileName\n\n", dptr->name);
    fprintf (st, "File logging can be disabled for the %s device with:\n\n", dptr->name);
    fprintf (st, "   sim> ATTACH %s NoLog\n\n", dptr->name);
    fprintf (st, "The %s device may be connected to a serial port on the host system.\n", dptr->name);
    }
else {
    fprintf (st, "%s multiplexer lines may be connected to terminal emulators supporting the\n", dptr->name);
    fprintf (st, "Telnet protocol via sockets, or to hardware terminals via host serial\n");
    fprintf (st, "ports.  Concurrent Telnet and serial connections may be mixed on a given\n");
    fprintf (st, "multiplexer.\n\n");
    if (mux && mux->modem_control) {
        fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name);
        fprintf (st, "passing port configuration information and modem signals on all lines.\n");
        }
    fprintf (st, "Modem Control signalling behaviors can be enabled/disabled on a specific\n");
    fprintf (st, "multiplexer line with:\n\n");
    fprintf (st, "   sim> ATTACH %s Line=n,Modem\n", dptr->name);
    fprintf (st, "   sim> ATTACH %s Line=n,NoModem\n\n", dptr->name);
    fprintf (st, "A Telnet listening port can be configured with:\n\n");
    fprintf (st, "   sim> ATTACH %s {interface:}port\n\n", dptr->name);
    if (mux)
        fprintf (st, "Line buffering for all %d lines on the %s device can be configured with:\n\n", mux->lines, dptr->name);
    else
        fprintf (st, "Line buffering for all lines on the %s device can be configured with:\n\n", dptr->name);
    fprintf (st, "   sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name);
    if (mux)
        fprintf (st, "Line buffering for all %d lines on the %s device can be disabled with:\n\n", mux->lines, dptr->name);
    else
        fprintf (st, "Line buffering for all lines on the %s device can be disabled with:\n\n", dptr->name);
    fprintf (st, "   sim> ATTACH %s NoBuffer\n\n", dptr->name);
    fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n");
    fprintf (st, "The outbound traffic for the lines of the %s device can be logged to files\n", dptr->name);
    fprintf (st, "with:\n\n");
    fprintf (st, "   sim> ATTACH %s Log=LogFileName\n\n", dptr->name);
    fprintf (st, "The log file name for each line uses the above LogFileName as a template\n");
    fprintf (st, "for the actual file name which will be LogFileName_n where n is the line\n");
    fprintf (st, "number.\n\n");
    fprintf (st, "Multiplexer lines may be connected to serial ports on the host system.\n");
    }
fprintf (st, "Serial ports may be specified as an operating system specific device names\n");
fprintf (st, "or using simh generic serial names.  simh generic names are of the form\n");
fprintf (st, "serN, where N is from 0 thru one less than the maximum number of serial\n");
fprintf (st, "ports on the local system.  The mapping of simh generic port names to OS \n");
fprintf (st, "specific names can be displayed using the following command:\n\n");
fprintf (st, "   sim> SHOW SERIAL\n");
fprintf (st, "   Serial devices:\n");
fprintf (st, "    ser0   COM1 (\\Device\\Serial0)\n");
fprintf (st, "    ser1   COM3 (Winachcf0)\n\n");
if (single_line) {          /* Single Line Multiplexer */
    fprintf (st, "   sim> ATTACH %s Connect=ser0\n\n", dptr->name);
    fprintf (st, "or equivalently:\n\n");
    fprintf (st, "   sim> ATTACH %s Connect=COM1\n\n", dptr->name);
    }
else {
    fprintf (st, "   sim> ATTACH %s Line=n,Connect=ser0\n\n", dptr->name);
    fprintf (st, "or equivalently:\n\n");
    fprintf (st, "   sim> ATTACH %s Line=n,Connect=COM1\n\n", dptr->name);
    if (mux)
        fprintf (st, "Valid line numbers are from 0 thru %d\n\n", mux->lines-1);
    }
if (single_line) {          /* Single Line Multiplexer */
    fprintf (st, "The input data rate for the %s device can be controlled by\n", dptr->name);
    fprintf (st, "specifying SPEED=nnn{*fac} on the the ATTACH command.\n");
    }
else {
    fprintf (st, "The input data rate for all lines or a particular line of a the %s\n", dptr->name);
    fprintf (st, "device can be controlled by specifying SPEED=nnn{*fac} on the ATTACH command.\n");
    }
fprintf (st, "SPEED values can be any one of:\n\n");
fprintf (st, "    0 50 75 110 134 150 300 600 1200 1800 2000 2400\n");
fprintf (st, "    3600 4800 7200 9600 19200 38400 57600 76800 115200\n\n");
fprintf (st, "A SPEED value of 0 causes input data to be delivered to the simulated\n");
fprintf (st, "port as fast as it arrives.\n\n");
fprintf (st, "If a simulated multiplexor devices can programmatically set a serial\n");
fprintf (st, "port line speed, the programmatically specified speed will take precidence\n");
fprintf (st, "over any input speed specified on an attach command.\n");
fprintf (st, "Some simulated systems run very much faster than the original system\n");
fprintf (st, "which is being simulated.  To accommodate this, the speed specified may\n");
fprintf (st, "include a factor which will increase the input data delivery rate by\n");
fprintf (st, "the specified factor.  A factor is specified with a speed value of the\n");
fprintf (st, "form \"speed*factor\".  Factor values can range from 1 thru 32.\n");
fprintf (st, "Example:\n\n");
fprintf (st, "   sim> ATTACH %s 1234,SPEED=2400\n", dptr->name);
fprintf (st, "   sim> ATTACH %s 1234,SPEED=9600*8\n", dptr->name);
if (!single_line)
    fprintf (st, "   sim> ATTACH %s Line=2,SPEED=2400\n", dptr->name);
fprintf (st, "\n");
fprintf (st, "The SPEED parameter only influences the rate at which data is deliverd\n");
fprintf (st, "into the simulated multiplexor port.  Output data rates are unaffected\n");
fprintf (st, "If an attach command specifies a speed multiply factor, that value will\n");
fprintf (st, "persist independent of any programatic action by the simulated system to\n");
fprintf (st, "change the port speed.\n\n");
fprintf (st, "An optional serial port configuration string may be present after the port\n");
fprintf (st, "name.  If present, it must be separated from the port name with a semicolon\n");
fprintf (st, "and has this form:\n\n");
fprintf (st, "   <rate>-<charsize><parity><stopbits>\n\n");
fprintf (st, "where:\n");
fprintf (st, "   rate     = communication rate in bits per second\n");
fprintf (st, "   charsize = character size in bits (5-8, including optional parity)\n");
fprintf (st, "   parity   = parity designator (N/E/O/M/S for no/even/odd/mark/space parity)\n");
fprintf (st, "   stopbits = number of stop bits (1, 1.5, or 2)\n\n");
fprintf (st, "As an example:\n\n");
fprintf (st, "   9600-8n1\n\n");
fprintf (st, "The supported rates, sizes, and parity options are host-specific.  If\n");
fprintf (st, "a configuration string is not supplied, then the default of 9600-8N1\n");
fprintf (st, "is used.\n");
fprintf (st, "Note: The serial port configuration option is only available on multiplexer\n");
fprintf (st, "      lines which are not operating with full modem control behaviors enabled.\n");
fprintf (st, "      Lines with full modem control behaviors enabled have all of their\n");
fprintf (st, "      configuration managed by the Operating System running within the\n");
fprintf (st, "      simulator.\n\n");
fprintf (st, "An attachment to a serial port with the '-V' switch will cause a\n");
fprintf (st, "connection message to be output to the connected serial port.\n");
fprintf (st, "This will help to confirm the correct port has been connected and\n");
fprintf (st, "that the port settings are reasonable for the connected device.\n");
fprintf (st, "This would be done as:\n\n");
if (single_line)            /* Single Line Multiplexer */
    fprintf (st, "   sim> ATTACH -V %s Connect=SerN\n", dptr->name);
else {
    fprintf (st, "   sim> ATTACH -V %s Line=n,Connect=SerN\n\n", dptr->name);
    fprintf (st, "Line specific tcp listening ports are supported.  These are configured\n");
    fprintf (st, "using commands of the form:\n\n");
    fprintf (st, "   sim> ATTACH %s Line=n,{interface:}port{;notelnet}\n\n", dptr->name);
    }
fprintf (st, "Direct computer to computer connections (Virutal Null Modem cables) may\n");
fprintf (st, "be established using the telnet protocol or via raw tcp sockets.\n\n");
fprintf (st, "   sim> ATTACH %s Line=n,Connect=host:port{;notelnet}\n\n", dptr->name);
fprintf (st, "Computer to computer virtual connections can be one way (as illustrated\n");
fprintf (st, "above) or symmetric.  A symmetric connection is configured by combining\n"); 
if (single_line) {          /* Single Line Multiplexer */
    fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n");
    fprintf (st, "   sim> ATTACH %s listenport,Connect=host:port\n\n", dptr->name);
    }
else {
    fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n");
    fprintf (st, "   sim> ATTACH %s Line=n,listenport,Connect=host:port\n\n", dptr->name);
    }
fprintf (st, "When symmetric virtual connections are configured, incoming connections\n");
fprintf (st, "on the specified listening port are checked to assure that they actually\n");
fprintf (st, "come from the specified connection destination host system.\n\n");
if (single_line) {          /* Single Line Multiplexer */
    fprintf (st, "The %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
    fprintf (st, "   sim> ATTACH %s Loopback\n\n", dptr->name);
    }
else {
    fprintf (st, "A line on the %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
    fprintf (st, "   sim> ATTACH %s Line=n,Loopback\n\n", dptr->name);
    }
fprintf (st, "When operating in LOOPBACK mode, all outgoing data arrives as input and\n");
fprintf (st, "outgoing modem signals (if enabled) (DTR and RTS) are reflected in the\n");
fprintf (st, "incoming modem signals (DTR->(DCD and DSR), RTS->CTS)\n\n");
if (single_line)            /* Single Line Multiplexer */
    fprintf (st, "The connection configured for the %s device is unconfigured by:\n\n", dptr->name);
else
    fprintf (st, "All connections configured for the %s device are unconfigured by:\n\n", dptr->name);
fprintf (st, "   sim> DETACH %s\n\n", dptr->name);
if (dptr->modifiers) {
    MTAB *mptr;

    for (mptr = dptr->modifiers; mptr->mask != 0; mptr++)
        if (mptr->valid == &tmxr_dscln) {
            fprintf (st, "A specific line on the %s device can be disconnected with:\n\n", dptr->name);
            fprintf (st, "   sim> SET %s %s=n\n\n", dptr->name, mptr->mstring);
            fprintf (st, "This will cause a telnet connection to be closed, but a serial port will\n");
            fprintf (st, "normally have DTR dropped for 500ms and raised again (thus hanging up a\n");
            fprintf (st, "modem on that serial port).\n\n");
            fprintf (st, "A line which is connected to a serial port can be manually closed by\n");
            fprintf (st, "adding the -C switch to a %s command.\n\n", mptr->mstring);
            fprintf (st, "   sim> SET -C %s %s=n\n\n", dptr->name, mptr->mstring);
            }
    }
return SCPE_OK;
}

/* Stub examine and deposit */

t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
return SCPE_NOFNC;
}

t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
return SCPE_NOFNC;
}


/* Write a message directly to a socket */

void tmxr_msg (SOCKET sock, const char *msg)
{
if ((sock) && (sock != INVALID_SOCKET))
    sim_write_sock (sock, msg, (int32)strlen (msg));
return;
}


/* Write a message to a line */

void tmxr_linemsg (TMLN *lp, const char *msg)
{
while (*msg) {
    while (SCPE_STALL == tmxr_putc_ln (lp, (int32)(*msg)))
        if (lp->txbsz == tmxr_send_buffered_data (lp))
            sim_os_ms_sleep (10);
    ++msg;
    }
return;
}


/* Write a formatted message to a line */

void tmxr_linemsgf (TMLN *lp, const char *fmt, ...)
{
va_list arglist;

va_start (arglist, fmt);
tmxr_linemsgvf (lp, fmt, arglist);
va_end (arglist);
}

void tmxr_linemsgvf (TMLN *lp, const char *fmt, va_list arglist)
{
char stackbuf[STACKBUFSIZE];
int32 bufsize = sizeof(stackbuf);
char *buf = stackbuf;
int32 i, len;

buf[bufsize-1] = '\0';
while (1) {                                         /* format passed string, args */
#if defined(NO_vsnprintf)
    len = vsprintf (buf, fmt, arglist);
#else                                               /* !defined(NO_vsnprintf) */
    len = vsnprintf (buf, bufsize-1, fmt, arglist);
#endif                                              /* NO_vsnprintf */

/* If the formatted result didn't fit into the buffer, then grow the buffer and try again */

    if ((len < 0) || (len >= bufsize-1)) {
        if (buf != stackbuf)
            free (buf);
        bufsize = bufsize * 2;
        if (bufsize < len + 2)
            bufsize = len + 2;
        buf = (char *) malloc (bufsize);
        if (buf == NULL)                            /* out of memory */
            return;
        buf[bufsize-1] = '\0';
        continue;
        }
    break;
    }

/* Output the formatted data expanding newlines where they exist */

for (i = 0; i < len; ++i) {
    if (('\n' == buf[i]) && ((i == 0) || ('\r' != buf[i-1]))) {
        while (SCPE_STALL == tmxr_putc_ln (lp, '\r'))
            if (lp->txbsz == tmxr_send_buffered_data (lp))
                sim_os_ms_sleep (10);
        }
    while (SCPE_STALL == tmxr_putc_ln (lp, buf[i]))
        if (lp->txbsz == tmxr_send_buffered_data (lp))
            sim_os_ms_sleep (10);
    }
if (buf != stackbuf)
    free (buf);
return;
}


/* Print connections - used only in named SHOW command */

void tmxr_fconns (FILE *st, const TMLN *lp, int32 ln)
{
int32 hr, mn, sc;
uint32 ctime;

if (ln >= 0)
    fprintf (st, "line %d: ", ln);

if ((lp->sock) || (lp->connecting)) {                   /* tcp connection? */
    if (lp->destination)                                /* remote connection? */
        if (lp->datagram)
            fprintf (st, "Datagram Connection from %s to remote port %s\n", lp->port, lp->destination);/* print port name */
        else
            fprintf (st, "Connection to remote port %s\n", lp->destination);/* print port name */
    else                                                /* incoming connection */
        fprintf (st, "Connection from IP address %s\n", lp->ipad);
    }
else
    if (lp->destination)                                /* remote connection? */
        fprintf (st, "Connecting to remote port %s\n", lp->destination);/* print port name */
if (lp->sock) {
    char *sockname, *peername;

    sim_getnames_sock (lp->sock, &sockname, &peername);
    fprintf (st, "Connection %s->%s\n", sockname, peername);
    free (sockname);
    free (peername);
    }

if ((lp->port) && (!lp->datagram))
    fprintf (st, "Listening on port %s\n", lp->port);   /* print port name */

if (lp->serport)                                        /* serial connection? */
    fprintf (st, "Connected to serial port %s\n", lp->destination);  /* print port name */

if (lp->cnms) {
    ctime = (sim_os_msec () - lp->cnms) / 1000;
    hr = ctime / 3600;
    mn = (ctime / 60) % 60;
    sc = ctime % 60;
    if (ctime)
        fprintf (st, " %s %02d:%02d:%02d\n", lp->connecting ? "Connecting for" : "Connected", hr, mn, sc);
    }
else
    fprintf (st, " Line disconnected\n");

if (lp->modem_control) {
    fprintf (st, " Modem Bits: %s%s%s%s%s%s\n", (lp->modembits & TMXR_MDM_DTR) ? "DTR " : "",
                                                (lp->modembits & TMXR_MDM_RTS) ? "RTS " : "",
                                                (lp->modembits & TMXR_MDM_DCD) ? "DCD " : "",
                                                (lp->modembits & TMXR_MDM_RNG) ? "RNG " : "",
                                                (lp->modembits & TMXR_MDM_CTS) ? "CTS " : "",
                                                (lp->modembits & TMXR_MDM_DSR) ? "DSR " : "");
    }

if ((lp->serport == 0) && (lp->sock) && (!lp->datagram))
    fprintf (st, " %s\n", (lp->notelnet) ? "Telnet disabled (RAW data)" : "Telnet protocol");
if (lp->send.buffer)
    sim_show_send_input (st, &lp->send);
if (lp->expect.buf)
    sim_exp_showall (st, &lp->expect);
if (lp->txlog)
    fprintf (st, " Logging to %s\n", lp->txlogname);
return;
}


/* Print statistics - used only in named SHOW command */

void tmxr_fstats (FILE *st, const TMLN *lp, int32 ln)
{
static const char *enab = "on";
static const char *dsab = "off";

if (ln >= 0)
    fprintf (st, "Line %d:", ln);
if ((!lp->sock) && (!lp->connecting) && (!lp->serport))
    fprintf (st, " not connected\n");
else {
    if (ln >= 0)
        fprintf (st, "\n");
    fprintf (st, "  input (%s)", (lp->rcve? enab: dsab));
    if (lp->rxcnt)
        fprintf (st, " queued/total = %d/%d", tmxr_rqln (lp), lp->rxcnt);
    if (lp->rxpcnt)
        fprintf (st, " packets = %d", lp->rxpcnt);
    fprintf (st, "\n  output (%s)", (lp->xmte? enab: dsab));
    if (lp->txcnt || lp->txbpi)
        fprintf (st, " queued/total = %d/%d", tmxr_tqln (lp), lp->txcnt);
    if (lp->txpcnt || tmxr_tpqln (lp))
        fprintf (st, " packet data queued/packets sent = %d/%d",
            tmxr_tpqln (lp), lp->txpcnt);
    fprintf (st, "\n");
    }
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);
return;
}


/* Disconnect a line.

   Disconnect a line of the multiplexer associated with descriptor "desc" from a
   tcp session or a serial port.  Two calling sequences are supported:

    1. If "val" is zero, then "uptr" is implicitly associated with the line
       number corresponding to the position of the unit in the zero-based array
       of units belonging to the associated device, and "cptr" is ignored.  For
       example, if "uptr" points to unit 3 in a given device, then line 3 will
       be disconnected.

    2. If "val" is non-zero, then "cptr" points to a string that is parsed for
       an explicit line number, and "uptr" is ignored.  For example, if "cptr"
       points to the string "3", then line 3 will be disconnected.

   If the line was connected to a tcp session, the socket associated with the
   line will be closed.  If the line was connected to a serial port, the port
   will NOT be closed, but DTR will be dropped.  After a 500ms delay DTR will
   be raised again.  If the sim_switches -C flag is set, then a serial port 
   connection will be closed.

   Implementation notes:

    1. This function is usually called as an MTAB processing routine.
*/

t_stat tmxr_dscln (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
TMXR *mp = (TMXR *) desc;
TMLN *lp;
t_stat status;

if (val)                                                        /* explicit line? */
    uptr = NULL;                                                /* indicate to get routine */

tmxr_debug_trace (mp, "tmxr_dscln()");

lp = tmxr_get_ldsc (uptr, cptr, mp, &status);                   /* get referenced line */

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 */
    }

return SCPE_OK;
}


/* Enable logging for line */

t_stat tmxr_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
TMXR *mp = (TMXR *) desc;
TMLN *lp;

if (cptr == NULL)                                       /* no file name? */
    return SCPE_2FARG;
lp = tmxr_find_ldsc (uptr, val, mp);                    /* find line desc */
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 */
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);
return SCPE_OK;
}


/* Disable logging for line */

t_stat tmxr_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
TMXR *mp = (TMXR *) desc;
TMLN *lp;

if (cptr)                                               /* no arguments */
    return SCPE_2MARG;
lp = tmxr_find_ldsc (uptr, val, mp);                    /* find line desc */
if (lp == NULL)
    return SCPE_IERR;
if (lp->txlog) {                                        /* logging? */
    sim_close_logfile (&lp->txlogref);                  /* close log */
    free (lp->txlogname);                               /* free namebuf */
    lp->txlog = NULL;
    lp->txlogname = NULL;
    }
if (mp->uptr)
    lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
return SCPE_OK;
}


/* Show logging status for line */

t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
const TMXR *mp = (const TMXR *) desc;
TMLN *lp;

lp = tmxr_find_ldsc (uptr, val, mp);                    /* find line desc */
if (lp == NULL)
    return SCPE_IERR;
if (lp->txlog)
    fprintf (st, "logging to %s", lp->txlogname);
else fprintf (st, "no logging");
return SCPE_OK;
}


/* Set the line connection order.

   Example command for eight-line multiplexer:

      SET <dev> LINEORDER=1;5;2-4;7

   Resulting connection order: 1,5,2,3,4,7,0,6.

   Parameters:
    - uptr = (not used)
    - val  = (not used)
    - cptr = pointer to first character of range specification
    - desc = pointer to multiplexer's TMXR structure

   On entry, cptr points to the value portion of the command string, which may
   be either a semicolon-separated list of line ranges or the keyword ALL.

   If a line connection order array is not defined in the multiplexer
   descriptor, the command is rejected.  If the specified range encompasses all
   of the lines, the first value of the connection order array is set to -1 to
   indicate sequential connection order.  Otherwise, the line values in the
   array are set to the order specified by the command string.  All values are
   populated, first with those explicitly specified in the command string, and
   then in ascending sequence with those not specified.

   If an error occurs, the original line order is not disturbed.
*/

t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *carg, void *desc)
{
TMXR *mp = (TMXR *) desc;
char *tbuf;
char *tptr;
CONST char *cptr;
t_addr low, high, max = (t_addr) mp->lines - 1;
int32 *list;
t_bool *set;
uint32 line, idx = 0;
t_stat result = SCPE_OK;

if (mp->lnorder == NULL)                                /* line connection order undefined? */
    return SCPE_NXPAR;                                  /* "Non-existent parameter" error */

else if ((carg == NULL) || (*carg == '\0'))             /* line range not supplied? */
    return SCPE_MISVAL;                                 /* "Missing value" error */

list = (int32 *) calloc (mp->lines, sizeof (int32));    /* allocate new line order array */

if (list == NULL)                                       /* allocation failed? */
    return SCPE_MEM;                                    /* report it */

set = (t_bool *) calloc (mp->lines, sizeof (t_bool));   /* allocate line set tracking array */

if (set == NULL) {                                      /* allocation failed? */
    free (list);                                        /* free successful list allocation */
    return SCPE_MEM;                                    /* report it */
    }

tbuf = (char *) calloc (strlen(carg)+2, sizeof(*carg));
strcpy (tbuf, carg);
tptr = tbuf + strlen (tbuf);                            /* append a semicolon */
*tptr++ = ';';                                          /*   to the command string */
*tptr = '\0';                                           /*   to make parsing easier for get_range */
cptr = tbuf;

while (*cptr) {                                         /* parse command string */
    cptr = get_range (NULL, cptr, &low, &high, 10, max, ';');/* get a line range */

    if (cptr == NULL) {                                 /* parsing error? */
        result = SCPE_ARG;                              /* "Invalid argument" error */
        break;
        }

    else if ((low > max) || (high > max)) {             /* line out of range? */
        result = SCPE_SUB;                              /* "Subscript out of range" error */
        break;
        }

    else if ((low == 0) && (high == max)) {             /* entire line range specified? */
        list [0] = -1;                                  /* set sequential order flag */
        idx = (uint32) max + 1;                         /* indicate no fill-in needed */
        break;
        }

    else
        for (line = (uint32) low; line <= (uint32) high; line++) /* see if previously specified */
            if (set [line] == FALSE) {                  /* not already specified? */
                set [line] = TRUE;                      /* now it is */
                list [idx] = line;                      /* add line to connection order */
                idx = idx + 1;                          /* bump "specified" count */
                }
    }

if (result == SCPE_OK) {                                /* assignment successful? */
    if (idx <= max)                                     /* any lines not specified? */
        for (line = 0; line <= max; line++)             /* fill them in sequentially */
            if (set [line] == FALSE) {                  /* specified? */
                list [idx] = line;                      /* no, so add it */
                idx = idx + 1;
                }

    memcpy (mp->lnorder, list, mp->lines * sizeof (int32)); /* copy working array to connection array */
    }

free (list);                                            /* free list allocation */
free (set);                                             /* free set allocation */
free (tbuf);                                            /* free arg copy with ; */

return result;
}


/* Show line connection order.

   Parameters:
    - st   = stream on which output is to be written
    - uptr = (not used)
    - val  = (not used)
    - desc = pointer to multiplexer's TMXR structure

   If a connection order array is not defined in the multiplexer descriptor, the
   command is rejected.  If the first value of the connection order array is set
   to -1, then the connection order is sequential.  Otherwise, the line values
   in the array are printed as a semicolon-separated list.  Ranges are printed
   where possible to shorten the output.
*/

t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 i, j, low, last;
const TMXR *mp = (const TMXR *) desc;
int32 *iptr = mp->lnorder;
t_bool first = TRUE;

if (iptr == NULL)                                       /* connection order undefined? */
    return SCPE_NXPAR;                                  /* "Non-existent parameter" error */

if (*iptr < 0)                                          /* sequential order indicated? */
    fprintf (st, "Order=0-%d\n", mp->lines - 1);        /* print full line range */

else {
    low = last = *iptr++;                               /* set first line value */

    for (j = 1; j <= mp->lines; j++) {                  /* print remaining lines in order list */
        if (j < mp->lines)                              /* more lines to process? */
            i = *iptr++;                                /* get next line in list */
        else                                            /* final iteration */
            i = -1;                                     /* get "tie-off" value */

        if (i != last + 1) {                            /* end of a range? */
            if (first) {                                /* first line to print? */
                fputs ("Order=", st);                   /* print header */
                first = FALSE;
                }

            else                                        /* not first line printed */
                fputc (';', st);                        /* print separator */

            if (low == last)                            /* range null? */
                fprintf (st, "%d", last);               /* print single line value */

            else                                        /* range established */
                fprintf (st, "%d-%d", low, last);       /* print start and end line */

            low = i;                                    /* start new range */
            }

        last = i;                                       /* note value for range check */
        }
    }

if (first == FALSE)                                     /* sanity check for lines == 0 */
    fputc ('\n', st);

return SCPE_OK;
}

/* Show summary processor */

t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
const TMXR *mp = (const TMXR *) desc;
int32 i, t;

if (mp == NULL)
    return SCPE_IERR;
for (i = t = 0; i < mp->lines; i++)
    if ((mp->ldsc[i].sock != 0) || (mp->ldsc[i].serport != 0))
        t = t + 1;
if (mp->lines > 1)
    fprintf (st, "%d current connection%s", t, (t != 1) ? "s" : "");
else
    fprintf (st, "%s", (t == 1) ? "connected" : "disconnected");
return SCPE_OK;
}

/* Show conn/stat processor */

t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
const TMXR *mp = (const TMXR *) desc;
int32 i, any;

if (mp == NULL)
    return SCPE_IERR;
for (i = any = 0; i < mp->lines; i++) {
    if ((mp->ldsc[i].sock != 0) || 
        (mp->ldsc[i].serport != 0) || mp->ldsc[i].modem_control) {
        if ((mp->ldsc[i].sock != 0) || (mp->ldsc[i].serport != 0))
            any++;
        if (val)
            tmxr_fconns (st, &mp->ldsc[i], i);
        else
            if ((mp->ldsc[i].sock != 0) || (mp->ldsc[i].serport != 0))
                tmxr_fstats (st, &mp->ldsc[i], i);
        }
    }
if (any == 0)
    fprintf (st, (mp->lines == 1? "disconnected\n": "all disconnected\n"));
return SCPE_OK;
}

/* Show number of lines */

t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
const TMXR *mp = (const TMXR *) desc;

if (mp == NULL)
    return SCPE_IERR;
fprintf (st, "lines=%d", mp->lines);
return SCPE_OK;
}


static struct {
    u_char value;
    const char *name;
    } tn_chars[] =
    {
        {TN_IAC,    "TN_IAC"},                /* protocol delim */
        {TN_DONT,   "TN_DONT"},               /* dont */
        {TN_DO,     "TN_DO"},                 /* do */
        {TN_WONT,   "TN_WONT"},               /* wont */
        {TN_WILL,   "TN_WILL"},               /* will */
        {TN_SB,     "TN_SB"},                 /* sub-option negotiation */
        {TN_GA,     "TN_SG"},                 /* go ahead */
        {TN_EL,     "TN_EL"},                 /* erase line */
        {TN_EC,     "TN_EC"},                 /* erase character */
        {TN_AYT,    "TN_AYT"},                /* are you there */
        {TN_AO,     "TN_AO"},                 /* abort output */
        {TN_IP,     "TN_IP"},                 /* interrupt process */
        {TN_BRK,    "TN_BRK"},                /* break */
        {TN_DATAMK, "TN_DATAMK"},             /* data mark */
        {TN_NOP,    "TN_NOP"},                /* no operation */
        {TN_SE,     "TN_SE"},                 /* end sub-option negot */
        /* Options */
        {TN_BIN,    "TN_BIN"},                /* bin */
        {TN_ECHO,   "TN_ECHO"},               /* echo */
        {TN_SGA,    "TN_SGA"},                /* sga */
        {TN_STATUS, "TN_STATUS"},             /* option status query */
        {TN_TIMING, "TN_TIMING"},             /* Timing Mark */
        {TN_NAOCRD, "TN_NAOCRD"},             /* Output Carriage-Return Disposition */
        {TN_NAOHTS, "TN_NAOHTS"},             /* Output Horizontal Tab Stops */
        {TN_NAOHTD, "TN_NAOHTD"},             /* Output Horizontal Tab Stop Disposition */
        {TN_NAOFFD, "TN_NAOFFD"},             /* Output Forfeed Disposition */
        {TN_NAOVTS, "TN_NAOVTS"},             /* Output Vertical Tab Stop */
        {TN_NAOVTD, "TN_NAOVTD"},             /* Output Vertical Tab Stop Disposition */
        {TN_NAOLFD, "TN_NAOLFD"},             /* Output Linefeed Disposition */
        {TN_EXTEND, "TN_EXTEND"},             /* Extended Ascii */
        {TN_LOGOUT, "TN_LOGOUT"},             /* Logout */
        {TN_BM,     "TN_BM"},                 /* Byte Macro */
        {TN_DET,    "TN_DET"},                /* Data Entry Terminal */
        {TN_SENDLO, "TN_SENDLO"},             /* Send Location */
        {TN_TERMTY, "TN_TERMTY"},             /* Terminal Type */
        {TN_ENDREC, "TN_ENDREC"},             /* Terminal Type */
        {TN_TUID,   "TN_TUID"},               /* TACACS User Identification */
        {TN_OUTMRK, "TN_OUTMRK"},             /* Output Marking */
        {TN_TTYLOC, "TN_TTYLOC"},             /* Terminal Location Number */
        {TN_3270,   "TN_3270"},               /* 3270 Regime */
        {TN_X3PAD,  "TN_X3PAD"},              /* X.3 PAD */
        {TN_NAWS,   "TN_NAWS"},               /* Negotiate About Window Size */
        {TN_TERMSP, "TN_TERMSP"},             /* Terminal Speed */
        {TN_TOGFLO, "TN_TOGFLO"},             /* Remote Flow Control */
        {TN_LINE,   "TN_LINE"},               /* line mode */
        {TN_XDISPL, "TN_XDISPL"},             /* X Display Location */
        {TN_ENVIRO, "TN_ENVIRO"},             /* Environment */
        {TN_AUTH,   "TN_AUTH"},               /* Authentication */
        {TN_ENCRYP, "TN_ENCRYP"},             /* Data Encryption */
        {TN_NEWENV, "TN_NEWENV"},             /* New Environment */
        {TN_TN3270, "TN_TN3270"},             /* TN3270 Enhancements */
        {TN_CHARST, "TN_CHARST"},             /* CHARSET */
        {TN_COMPRT, "TN_COMPRT"},             /* Com Port Control */
        {TN_KERMIT, "TN_KERMIT"},             /* KERMIT */
        {0, NULL}};

static char *tmxr_debug_buf = NULL;
static size_t tmxr_debug_buf_used = 0;
static size_t tmxr_debug_buf_size = 0;

static void tmxr_buf_debug_char (char value)
{
if (tmxr_debug_buf_used+2 > tmxr_debug_buf_size) {
    tmxr_debug_buf_size += 1024;
    tmxr_debug_buf = (char *)realloc (tmxr_debug_buf, tmxr_debug_buf_size);
    }
tmxr_debug_buf[tmxr_debug_buf_used++] = value;
tmxr_debug_buf[tmxr_debug_buf_used] = '\0';
}

static void tmxr_buf_debug_string (const char *string)
{
while (*string)
    tmxr_buf_debug_char (*string++);
}

static void tmxr_buf_debug_telnet_option (u_char chr)
{
int j;

for (j=0; 1; ++j) {
    if (NULL == tn_chars[j].name) {
        if (isprint(chr))
            tmxr_buf_debug_char (chr);
        else {
            tmxr_buf_debug_char ('_');
            if ((chr >= 1) && (chr <= 26)) {
                tmxr_buf_debug_char ('^');
                tmxr_buf_debug_char ('A' + chr - 1);
                }
            else {
                char octal[8];

                sprintf(octal, "\\%03o", (u_char)chr);
                tmxr_buf_debug_string (octal);
                }
            tmxr_buf_debug_char ('_');
            }
        break;
        }
    if ((u_char)chr == tn_chars[j].value) {
        tmxr_buf_debug_char ('_');
        tmxr_buf_debug_string (tn_chars[j].name);
        tmxr_buf_debug_char ('_');
        break;
        }
    }
}

static int tmxr_buf_debug_telnet_options (u_char *buf, int bufsize)
{
int optsize = 2;

tmxr_buf_debug_telnet_option ((u_char)buf[0]);
tmxr_buf_debug_telnet_option ((u_char)buf[1]);
switch ((u_char)buf[1]) {
    case TN_IAC:
    default:
        return optsize;
        break;
    case TN_WILL:
    case TN_WONT:
    case TN_DO:
    case TN_DONT:
        ++optsize;
        tmxr_buf_debug_telnet_option ((u_char)buf[2]);
        break;
    }
return optsize;
}

void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize)
{
DEVICE *dptr = (lp->dptr ? lp->dptr : (lp->mp ? lp->mp->dptr : NULL));

if ((dptr) && (dbits & dptr->dctrl)) {
    int i;

    tmxr_debug_buf_used = 0;
    if (tmxr_debug_buf)
        tmxr_debug_buf[tmxr_debug_buf_used] = '\0';

    if (lp->notelnet) {
        int same, group, sidx, oidx;
        char outbuf[80], strbuf[18];
        static char hex[] = "0123456789ABCDEF";

        for (i=same=0; i<bufsize; i += 16) {
            if ((i > 0) && (0 == memcmp(&buf[i], &buf[i-16], 16))) {
                ++same;
                continue;
                }
            if (same > 0) {
                if (lp->mp->lines > 1)
                    sim_debug (dbits, dptr, "Line:%d %04X thru %04X same as above\n", (int)(lp-lp->mp->ldsc), i-(16*same), i-1);
                else
                    sim_debug (dbits, dptr, "%04X thru %04X same as above\n", i-(16*same), i-1);
                same = 0;
                }
            group = (((bufsize - i) > 16) ? 16 : (bufsize - i));
            for (sidx=oidx=0; sidx<group; ++sidx) {
                outbuf[oidx++] = ' ';
                outbuf[oidx++] = hex[(buf[i+sidx]>>4)&0xf];
                outbuf[oidx++] = hex[buf[i+sidx]&0xf];
                if (isprint((u_char)buf[i+sidx]))
                    strbuf[sidx] = buf[i+sidx];
                else
                    strbuf[sidx] = '.';
                }
            outbuf[oidx] = '\0';
            strbuf[sidx] = '\0';
            if (lp->mp->lines > 1)
                sim_debug (dbits, dptr, "Line:%d %04X%-48s %s\n", (int)(lp-lp->mp->ldsc), i, outbuf, strbuf);
            else
                sim_debug (dbits, dptr, "%04X%-48s %s\n", i, outbuf, strbuf);
            }
        if (same > 0) {
            if (lp->mp->lines > 1)
                sim_debug (dbits, dptr, "Line:%d %04X thru %04X same as above\n", (int)(lp-lp->mp->ldsc), i-(16*same), bufsize-1);
            else
                sim_debug (dbits, dptr, "%04X thru %04X same as above\n", i-(16*same), bufsize-1);
            }
        }
    else {
        tmxr_debug_buf_used = 0;
        if (tmxr_debug_buf)
            tmxr_debug_buf[tmxr_debug_buf_used] = '\0';
        for (i=0; i<bufsize; ++i) {
            switch ((u_char)buf[i]) {
                case TN_CR:
                    tmxr_buf_debug_string ("_TN_CR_");
                    break;
                case TN_LF:
                    tmxr_buf_debug_string ("_TN_LF_");
                    break;
                case TN_IAC:
                    if (!lp->notelnet) {
                        i += (tmxr_buf_debug_telnet_options ((u_char *)(&buf[i]), bufsize-i) - 1);
                        break;
                        }
                default:
                    if (isprint((u_char)buf[i]))
                        tmxr_buf_debug_char (buf[i]);
                    else {
                        tmxr_buf_debug_char ('_');
                        if ((buf[i] >= 1) && (buf[i] <= 26)) {
                            tmxr_buf_debug_char ('^');
                            tmxr_buf_debug_char ('A' + buf[i] - 1);
                            }
                        else {
                            char octal[8];

                            sprintf(octal, "\\%03o", (u_char)buf[i]);
                            tmxr_buf_debug_string (octal);
                            }
                        tmxr_buf_debug_char ('_');
                        }
                    break;
                }
            }
        if (lp->mp->lines > 1)
            sim_debug (dbits, dptr, "Line:%d %s %d bytes '%s'\n", (int)(lp-lp->mp->ldsc), msg, bufsize, tmxr_debug_buf);
        else
            sim_debug (dbits, dptr, "%s %d bytes '%s'\n", msg, bufsize, tmxr_debug_buf);
        }
    }
}
Added src/sim_tmxr.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* sim_tmxr.h: terminal multiplexer definitions

   Copyright (c) 2001-2008, Robert M Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   Based on the original DZ11 simulator by Thord Nilson, as updated by
   Arthur Krewat.

   10-Oct-12    MP      Added extended attach support for serial, per line 
                        listener and outgoing connections
   17-Jan-11    MP      Added buffered line capabilities
   20-Nov-08    RMS     Added three new standardized SHOW routines
   07-Oct-08    JDB     Added serial port support to TMXR, TMLN
   27-May-08    JDB     Added lnorder to TMXR structure,
                        added tmxr_set_lnorder and tmxr_set_lnorder
   14-May-08    JDB     Added dptr to TMXR structure
   04-Jan-04    RMS     Changed TMXR ldsc to be pointer to linedesc array
                        Added tmxr_linemsg, logging (from Mark Pizzolato)
   29-Dec-03    RMS     Added output stall support, increased buffer size
   22-Dec-02    RMS     Added break support (from Mark Pizzolato)
   20-Aug-02    RMS     Added tmxr_open_master, tmxr_close_master, tmxr.port
   30-Dec-01    RMS     Renamed tmxr_fstatus, added tmxr_fstats
   20-Oct-01    RMS     Removed tmxr_getchar, formalized buffer guard,
                        added tmxr_rqln, tmxr_tqln
*/

#ifndef SIM_TMXR_H_
#define SIM_TMXR_H_    0

#ifdef  __cplusplus
extern "C" {
#endif

#ifndef SIMH_SERHANDLE_DEFINED
#define SIMH_SERHANDLE_DEFINED 0
typedef struct SERPORT *SERHANDLE;
#endif

#include "sim_sock.h"

#define TMXR_V_VALID    15
#define TMXR_VALID      (1 << TMXR_V_VALID)
#define TMXR_MAXBUF     256                             /* buffer size */

#define TMXR_DTR_DROP_TIME 500                          /* milliseconds to drop DTR for 'pseudo' modem control */
#define TMXR_MODEM_RING_TIME 3                          /* seconds to wait for DTR for incoming connections */
#define TMXR_DEFAULT_CONNECT_POLL_INTERVAL 1            /* seconds between connection polls */

#define TMXR_DBG_XMT    0x0010000                        /* Debug Transmit Data */
#define TMXR_DBG_RCV    0x0020000                        /* Debug Received Data */
#define TMXR_DBG_RET    0x0040000                        /* Debug Returned Received Data */
#define TMXR_DBG_MDM    0x0080000                        /* Debug Modem Signals */
#define TMXR_DBG_CON    0x0100000                        /* Debug Connection Activities */
#define TMXR_DBG_ASY    0x0200000                        /* Debug Asynchronous Activities */
#define TMXR_DBG_TRC    0x0400000                        /* Debug trace routine calls */
#define TMXR_DBG_PXMT   0x0800000                        /* Debug Transmit Packet Data */
#define TMXR_DBG_PRCV   0x1000000                        /* Debug Received Packet Data */
#define TMXR_DBG_EXP    0x2000000                        /* Debug Expect Activities */
#define TMXR_DBG_SEND   0x4000000                        /* Debug Send Activities */

/* Modem Control Bits */

#define TMXR_MDM_DTR        0x01    /* Data Terminal Ready */
#define TMXR_MDM_RTS        0x02    /* Request To Send     */
#define TMXR_MDM_DCD        0x04    /* Data Carrier Detect */
#define TMXR_MDM_RNG        0x08    /* Ring Indicator      */
#define TMXR_MDM_CTS        0x10    /* Clear To Send       */
#define TMXR_MDM_DSR        0x20    /* Data Set Ready      */
#define TMXR_MDM_INCOMING   (TMXR_MDM_DCD|TMXR_MDM_RNG|TMXR_MDM_CTS|TMXR_MDM_DSR)  /* Settable Modem Bits */
#define TMXR_MDM_OUTGOING   (TMXR_MDM_DTR|TMXR_MDM_RTS)  /* Settable Modem Bits */

/* Unit flags */

#define TMUF_V_NOASYNCH   (UNIT_V_UF + 12)              /* Asynch Disabled unit */
#define TMUF_NOASYNCH     (1u << TMUF_V_NOASYNCH)       /* This flag can be defined */
                                                        /* statically in a unit's flag field */
                                                        /* This will disable the unit from */
                                                        /* supporting asynchronmous mux behaviors */
/* Receive line speed limits */

#define TMLN_SPD_50_BPS     200000 /* usec per character */
#define TMLN_SPD_75_BPS     133333 /* usec per character */
#define TMLN_SPD_110_BPS     90909 /* usec per character */
#define TMLN_SPD_134_BPS     74626 /* usec per character */
#define TMLN_SPD_150_BPS     66666 /* usec per character */
#define TMLN_SPD_300_BPS     33333 /* usec per character */
#define TMLN_SPD_600_BPS     16666 /* usec per character */
#define TMLN_SPD_1200_BPS     8333 /* usec per character */
#define TMLN_SPD_1800_BPS     5555 /* usec per character */
#define TMLN_SPD_2000_BPS     5000 /* usec per character */
#define TMLN_SPD_2400_BPS     4166 /* usec per character */
#define TMLN_SPD_3600_BPS     2777 /* usec per character */
#define TMLN_SPD_4800_BPS     2083 /* usec per character */
#define TMLN_SPD_7200_BPS     1388 /* usec per character */
#define TMLN_SPD_9600_BPS     1041 /* usec per character */
#define TMLN_SPD_19200_BPS     520 /* usec per character */
#define TMLN_SPD_38400_BPS     260 /* usec per character */
#define TMLN_SPD_57600_BPS     173 /* usec per character */
#define TMLN_SPD_76800_BPS     130 /* usec per character */
#define TMLN_SPD_115200_BPS     86 /* usec per character */



typedef struct tmln TMLN;
typedef struct tmxr TMXR;
struct loopbuf {
    int32               bpr;                          /* xmt buf remove */
    int32               bpi;                          /* xmt buf insert */
    int32               size;
    };

struct tmln {
    int                 conn;                           /* line connected flag */
    SOCKET              sock;                           /* connection socket */
    char                *ipad;                          /* IP address */
    SOCKET              master;                         /* line specific master socket */
    char                *port;                          /* line specific listening port */
    int32               sessions;                       /* count of tcp connections received */
    uint32              cnms;                           /* conn time */
    int32               tsta;                           /* Telnet state */
    int32               rcve;                           /* rcv enable */
    int32               xmte;                           /* xmt enable */
    int32               dstb;                           /* disable Telnet binary mode */
    t_bool              notelnet;                       /* raw binary data (no telnet interpretation) */
    uint8               *telnet_sent_opts;              /* Telnet Options which we have sent a DON'T/WON'T */
    int32               rxbpr;                          /* rcv buf remove */
    int32               rxbpi;                          /* rcv buf insert */
    int32               rxbsz;                          /* rcv buffer size */
    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               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 */
    char                *rxb;                           /* rcv buffer */
    char                *rbr;                           /* rcv break */
    char                *txb;                           /* xmt buffer */
    uint8               *rxpb;                          /* rcv packet buffer */
    uint32              rxpbsize;                       /* rcv packet buffer size */
    uint32              rxpboffset;                     /* rcv packet buffer offset */
    uint32              rxbps;                          /* rcv bps speed (0 - unlimited) */
    double              rxbpsfactor;                    /* receive speed factor (scaled to usecs) */
#define TMXR_RX_BPS_UNIT_SCALE 1000000.0
    uint32              rxdelta;                        /* rcv inter character min time (usecs) */
    double              rxnexttime;                     /* min time for next receive character */
    uint32              txbps;                          /* xmt bps speed (0 - unlimited) */
    uint32              txdelta;                        /* xmt inter character min time (usecs) */
    double              txnexttime;                     /* min time for next transmit character */
    uint8               *txpb;                          /* xmt packet buffer */
    uint32              txpbsize;                       /* xmt packet buffer size */
    uint32              txppsize;                       /* xmt packet packet size */
    uint32              txppoffset;                     /* xmt packet buffer offset */
    TMXR                *mp;                            /* back pointer to mux */
    char                *serconfig;                     /* line config */
    SERHANDLE           serport;                        /* serial port handle */
    t_bool              ser_connect_pending;            /* serial connection notice pending */
    SOCKET              connecting;                     /* Outgoing socket while connecting */
    char                *destination;                   /* Outgoing destination address:port */
    t_bool              loopback;                       /* Line in loopback mode */
    t_bool              halfduplex;                     /* Line in half-duplex mode */
    t_bool              datagram;                       /* Line is datagram packet oriented */
    t_bool              packet;                         /* Line is packet oriented */
    int32               lpbpr;                          /* loopback buf remove */
    int32               lpbpi;                          /* loopback buf insert */
    int32               lpbcnt;                         /* loopback buf used count */
    int32               lpbsz;                          /* loopback buffer size */
    char                *lpb;                           /* loopback buffer */
    UNIT                *uptr;                          /* input polling unit (default to mp->uptr) */
    UNIT                *o_uptr;                        /* output polling unit (default to lp->uptr)*/
    DEVICE              *dptr;                          /* line specific device */
    EXPECT              expect;                         /* Expect rules */
    SEND                send;                           /* Send input state */
    };

struct tmxr {
    int32               lines;                          /* # lines */
    char                *port;                          /* listening port */
    SOCKET              master;                         /* master socket */
    TMLN                *ldsc;                          /* line descriptors */
    int32               *lnorder;                       /* line connection order */
    DEVICE              *dptr;                          /* multiplexer device */
    UNIT                *uptr;                          /* polling unit (connection) */
    char                logfiletmpl[FILENAME_MAX];      /* template logfile name */
    int32               txcount;                        /* count of transmit bytes */
    int32               buffered;                       /* Buffered Line Behavior and Buffer Size Flag */
    int32               sessions;                       /* count of tcp connections received */
    uint32              poll_interval;                  /* frequency of connection polls (seconds) */
    uint32              last_poll_time;                 /* time of last connection poll */
    uint32              ring_start_time;                /* time ring signal was raised */
    char                *ring_ipad;                     /* incoming connection address awaiting DTR */
    SOCKET              ring_sock;                      /* incoming connection socket awaiting DTR */
    t_bool              notelnet;                       /* default telnet capability for incoming connections */
    t_bool              modem_control;                  /* multiplexer supports modem control behaviors */
    t_bool              packet;                         /* Lines are packet oriented */
    t_bool              datagram;                       /* Lines use datagram packet transport */
    };

int32 tmxr_poll_conn (TMXR *mp);
t_stat tmxr_reset_ln (TMLN *lp);
t_stat tmxr_detach_ln (TMLN *lp);
int32 tmxr_input_pending_ln (TMLN *lp);
int32 tmxr_getc_ln (TMLN *lp);
t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize);
t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte);
void tmxr_poll_rx (TMXR *mp);
t_stat tmxr_putc_ln (TMLN *lp, int32 chr);
t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size);
t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte);
void tmxr_poll_tx (TMXR *mp);
int32 tmxr_send_buffered_data (TMLN *lp);
t_stat tmxr_open_master (TMXR *mp, CONST char *cptr);
t_stat tmxr_close_master (TMXR *mp);
t_stat tmxr_connection_poll_interval (TMXR *mp, uint32 seconds);
t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, CONST char *cptr, t_bool async);
t_stat tmxr_detach (TMXR *mp, UNIT *uptr);
t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
char *tmxr_line_attach_string(TMLN *lp);
t_stat tmxr_set_modem_control_passthru (TMXR *mp);
t_stat tmxr_clear_modem_control_passthru (TMXR *mp);
t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits);
t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback);
t_bool tmxr_get_line_loopback (TMLN *lp);
t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_loopback);
t_bool tmxr_get_line_halfduplex (TMLN *lp);
t_stat tmxr_set_line_speed (TMLN *lp, CONST char *speed);
t_stat tmxr_set_config_line (TMLN *lp, CONST char *config);
t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll);
t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll);
t_stat tmxr_set_console_units (UNIT *rxuptr, UNIT *txuptr);
t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
void tmxr_msg (SOCKET sock, const char *msg);
void tmxr_linemsg (TMLN *lp, const char *msg);
void tmxr_linemsgf (TMLN *lp, const char *fmt, ...);
void tmxr_linemsgvf (TMLN *lp, const char *fmt, va_list args);
void tmxr_fconns (FILE *st, const TMLN *lp, int32 ln);
void tmxr_fstats (FILE *st, const TMLN *lp, int32 ln);
t_stat tmxr_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat tmxr_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_dscln (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
int32 tmxr_rqln (const TMLN *lp);
int32 tmxr_tqln (const TMLN *lp);
int32 tmxr_tpqln (const TMLN *lp);
t_bool tmxr_tpbusyln (const TMLN *lp);
t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc);
t_stat tmxr_activate (UNIT *uptr, int32 interval);
t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime);
t_stat tmxr_activate_after_abs (UNIT *uptr, uint32 usecs_walltime);
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);
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)
#define tmxr_debug_return(lp, val) do {if (sim_deb && (val) && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_RET & (lp)->mp->dptr->dctrl)) { if ((lp)->rxbps) sim_debug (TMXR_DBG_RET, (lp)->mp->dptr, "Ln%d: 0x%x - Next after: %.0f\n", (int)((lp)-(lp)->mp->ldsc), val, (lp)->rxnexttime); else sim_debug (TMXR_DBG_RET, (lp)->mp->dptr, "Ln%d: 0x%x\n", (int)((lp)-(lp)->mp->ldsc), val); } } while (0)
#define tmxr_debug_trace(mp, msg) do {if (sim_deb && (mp)->dptr && (TMXR_DBG_TRC & (mp)->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, mp->dptr, "%s\n", (msg)); } while (0)
#define tmxr_debug_trace_line(lp, msg) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_TRC & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, (lp)->mp->dptr, "Ln%d:%s\n", (int)((lp)-(lp)->mp->ldsc), (msg)); } while (0)
#define tmxr_debug_connect(mp, msg) do {if (sim_deb && (mp)->dptr && (TMXR_DBG_CON & (mp)->dptr->dctrl)) sim_debug (TMXR_DBG_CON, mp->dptr, "%s\n", (msg)); } while (0)
#define tmxr_debug_connect_line(lp, msg) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_CON & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_CON, (lp)->mp->dptr, "Ln%d:%s\n", (int)((lp)-(lp)->mp->ldsc), (msg)); } while (0)

#if defined(SIM_ASYNCH_MUX) && !defined(SIM_ASYNCH_IO)
#undef SIM_ASYNCH_MUX
#endif /* defined(SIM_ASYNCH_MUX) && !defined(SIM_ASYNCH_IO) */

#if defined(SIM_ASYNCH_MUX)
#define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, TRUE)
#else
#define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, FALSE)
#endif
#if (!defined(NOT_MUX_USING_CODE))
#define sim_activate tmxr_activate
#define sim_activate_after tmxr_activate_after
#define sim_activate_after_abs tmxr_activate_after_abs
#define sim_clock_coschedule tmxr_clock_coschedule 
#define sim_clock_coschedule_abs tmxr_clock_coschedule_abs
#define sim_clock_coschedule_tmr tmxr_clock_coschedule_tmr
#define sim_clock_coschedule_tmr_abs tmxr_clock_coschedule_tmr_abs
#endif


#ifdef  __cplusplus
}
#endif

#endif /* _SIM_TMXR_H_ */
Added src/test.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* test.c - Front panel LED and switch testing program, built as pidp8i-test

   Copyright © 2016 Paul R. Bernard

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

#include "gpio-common.h"

#include <assert.h>
#include <ctype.h>
#include <pthread.h>
#include <signal.h>
#include <stddef.h>
#include <stdlib.h>
#include <time.h>

typedef unsigned int    uint32; 
typedef unsigned char   uint8;

static uint16_t lastswitchstatus[3];    // to watch for switch changes

uint8 path[] = { 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x11, 0x12, 0x13, 0x14, 0x15,
		 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x2c, 0x2b, 0x2a, 0x29,
		 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x31, 0x32, 0x33,
		 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x4c, 0x4b,
		 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x87,
		 0x76, 0x77, 0x78, 0x79, 0x7a, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
		 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x68, 0x67, 0x66, 0x65, 0x64,
		 0x63, 0x62, 0x61, 0x69, 0x6a, 0x6b, 0x6c, 0x71, 0x72, 0x75, 0x74,
		 0x73 };

#define CHECK(cond,name) while(!(cond)) goto CLN_ ## name;
#define CLEANUP(name)    if( 0 ) { CLN_ ## name: {
#define CLEANUP_END      } } else;

int terminate=0;

void sig_handler( int signo )
{
  if( signo == SIGINT )
    terminate = 1;
}


int main( int argc, char *argv[] )
{
  pthread_t thread1;
  int iret1, row, col, i, chr;
  int path_idx = 0, led_row = 0, delay = 0;

  assert(sizeof(lastswitchstatus == switchstatus));

  // install handler to terminate future thread
  if( signal(SIGINT, sig_handler) == SIG_ERR )
    {
      fprintf( stderr, "Failed to install SIGINT handler.\n" );
      exit( EXIT_FAILURE );
    }
	
  // create thread
  iret1 = pthread_create( &thread1, NULL, blink, &terminate );
  if( iret1 )
    {
      fprintf( stderr, "Error creating thread, return code %d\n", iret1 );
      exit( EXIT_FAILURE );
    }
  sleep( 2 );			// allow 2 sec for multiplex to start
  if( !pidp8i_gpio_present )
    {
      fprintf( stderr, "Cannot run the test while another PiDP-8/I program runs.\n");
      exit( EXIT_FAILURE );
	}

  fprintf( stdout, "turn on ALL LEDs\n" );
  for( row=0; row<nledrows; ++row )
    ledstatus[row] = 07777;
  CHECK( !terminate, TERMINATE )
  sleep( 5 );
  for( row=0; row<nledrows; ++row )
    ledstatus[row] = 0;
  fprintf( stdout, "turn off ALL LEDs\n" );
  CHECK( !terminate, TERMINATE )
  sleep( 5 );

  for( row=0; row<nledrows; ++row )
    {
      CHECK( !terminate, TERMINATE )
      fprintf( stdout, "turning on LED row %d\n", row );
      ledstatus[row] = 07777;
      sleep( 5 );
      ledstatus[row] = 0;
    }

  for( col=0; col<ncols; ++col )
    {
      CHECK( !terminate, TERMINATE )
      fprintf( stdout, "turning on LED col %d\n", col );

      for( row=0; row<nledrows; ++row )
	ledstatus[row] |= 1<<col;
      sleep( 5 );
      for( row=0; row<nledrows; ++row )
	ledstatus[row] = 0;
    }
	
  fprintf( stdout, "Reading the switches.  Toggle any pattern desired.  CTRL-C to quit.\n" );
  
  for( i=0; i<nrows; ++i )
    lastswitchstatus[i] = switchstatus[i];
  
  for( ;; )
    {
      CHECK( !terminate, TERMINATE )

      if( delay++ >= 30 )
	{
	  delay = 0;
	  
	  ledstatus[led_row] = 0;
	  ledstatus[led_row=(path[path_idx]>>4) - 1] = 04000 >> ((path[path_idx]&0x0F) - 1);
	  if( ++path_idx >= sizeof(path)/sizeof(path[0]) ) path_idx = 0;
	}

      if( lastswitchstatus[0]!=switchstatus[0] ||
	  lastswitchstatus[1]!=switchstatus[1] ||
	  lastswitchstatus[2]!=switchstatus[2] )
	{
	  for( i=0; i<nrows; ++i )
	    {
	      fprintf( stdout, "%04o ", ~switchstatus[i] & 07777 );
	      lastswitchstatus[i] = switchstatus[i];
	    }
	  fprintf( stdout, "\n" );
	}
      usleep( 1000 );
    }	  

  CLEANUP( TERMINATE )

    if( pthread_join(thread1, NULL) )
      printf( "\r\nError joining multiplex thread\r\n" );
    return 0;

  CLEANUP_END
}
Added tools/bosi.

































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/bash -x
# bosi - The Binary OS Image creation/update script
#
# Copyright © 2016-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.

verb="$1"
object="$2"

tag=$(echo $verb | cut -f2 -d- -s)
test -z "$tag" && tag=ils

nu=pidp8i
nh=/home/$nu
repo=pidp8i
dldir="$HOME/tangentsoft.com/dl"
os=jessie-lite
img=$dldir/pidp8i-$(date +%Y.%m.%d)-$tag-$os.img
greadlink=$(type -p greadlink || type -p readlink)
this=$($greadlink -f $0)


# Initial steps
function do_init() {
	sudo apt-get update && sudo apt-get -y upgrade || true

	test "$USER" = "root" && exec sudo -i -u $nu $nh/bosi init $object
	cd $HOME

	test -n "$(type -p fossil)" || sudo apt-get -y install fossil

	if [ ! -d museum ]
	then
		mkdir -p museum $repo
		fossil clone https://tangentsoft.com/$repo museum/$repo.fossil
	fi

	cd $repo

	if [ -r ChangeLog.md ]
	then
		fossil revert			# just in case
		fossil update release
	else
		fossil open ~/museum/$repo.fossil release
		./configure $object
	fi

	tools/mmake
	sudo make install || true
	sudo reboot
}


# This script resets the OS's configuration to a clean first boot state.
function do_reset() {
	if [ "$USER" != "root" ]
	then
		echo "The reset step has to be run as root.  The explanation is"
		echo "given in the section for 'reset' in RELEASE-PROCESS.md."
		echo
		exit 1
	fi

	history -c ; rm -f ~/.bash_history
	test "$USER" = "root" || exec sudo $this reset

	shred -u /etc/ssh/*key*
	dphys-swapfile uninstall
	dd if=/dev/zero of=/junk bs=1M || true		# it *will* error-out!
	rm /junk
	encpass=$(openssl passwd -1 edsonDeCastro1968)
	if [ -e "$nh" ]
	then
		# A prior pass already renamed the default user and moved its
		# home directory, so just change the password.
		usermod -p $encpass pidp8i
	else
		# First pass on a clean SD card: rename 'pi' user to 'pidp8i',
		# move its home directory
		usermod -d $nh -l $nu -p $encpass -m pi 
	fi
	passwd -e pidp8i
	( sleep 1 ; poweroff ) & exit
}


# 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.
#
# The extra 100 megs in the arithmetic below accounts for the /boot
# partition, since the `resizepart` command takes a partition end value,
# not a size value.
#
# We don't calculate the actual end of the /boot partition and use that
# value because we want a bit of slack space to buy time for an end user
# who neglects to expand the card image into the free space available on
# their SD card after first boot.
function do_shrink() {
	test "$USER" = "root" || exec sudo $this shrink $object

	umount /dev/sda2 || true	# might auto-mount, might not
	e2fsck -f /dev/sda2			# resize2fs demands it
	blocks=$(
		resize2fs -M /dev/sda2 2>&1 |
		grep 'blocks long' | 
		grep -wo '[0-9]\+'
	)
	if [ "$blocks" -gt 0 ]
	then
		parted /dev/sda resizepart 2 $(($blocks * 4096 + 10**8))b
		blocks=$(
			resize2fs /dev/sda2 2>&1 |
			grep 'blocks long' | 
			grep -wo '[0-9]\+'
		)

		cat <<NEXT
Move the USB SD card reader to the desktop machine and resume the
process with

    bosi image $blocks

NEXT
	else
		echo "Failed to extract new filesystem size from resize2fs!"
		echo
		exit 1
	fi
}


# This script images the OS SD card in a USB reader on a Mac OS X box.
function do_image() {
	set +x
	if [ -n "$object" ]
	then
		while read line
		do
			case $line in
				/dev/*)
					dev=$(echo $line | cut -f1 -d' ')
					;;

				0:*)
					case $line in
						*FDisk_partition_scheme*) ;;
						*) dev= ;;		# can't be the OS SD card
					esac
					;;

				1:*)
					case $line in
						*Windows_FAT_32\ boot*) ;;
						*) dev= ;;		# can't be the OS SD card
					esac
					;;

				2:*)
					case $line in
						*Linux\ Untitled*) break ;;	# found it!
						*) dev= ;;		# can't be the OS SD card
					esac
					;;
			esac
		done < <(diskutil list)

		if [ -z "$dev" ]
		then
			echo "Failed to find OS SD card!"
			echo
			exit 1
		fi

		echo
		echo "-------------------------------------------------------"
		diskutil info "$dev"
		echo "-------------------------------------------------------"
		echo
		read -p "Is that the OS SD card? [y/N]: " answer
		case $answer in
			[Yy]*) ;;
			*) exit 1
		esac

		set -x
		sudo diskutil unmountDisk $dev			# it auto-mounted
		sudo dd if=$dev bs=4k count=$object of=$img
		zip -9j $img.zip $img

		sudo dd if=$img of=$dev bs=1m
		sudo diskutil unmountDisk $dev || true	# Paragon ExtFS might be installed
	else
		usage
	fi
}


# Clean up after the above
function do_finish() {
	rmtrash $img
	cd $dldir/..
	make synch
}


# Display the usage message
function usage() {
	cat <<USAGE
usage: $0 <verb[-tag]> [object]

    The available verbs are init, fossil, shrink, image, and finish.

    You may append a tag to the image and finish verbs (e.g. image-nls)
    to override the default tag ('all') used in image and zip file
    outputs.

    The object depends on the verb.  See RELEASE-PROCESS.md.

USAGE
	exit 1
}


# Main routine
set -e
case "$verb" in
	in*) do_init ;;
	re*) do_reset ;;
	sh*) do_shrink ;;
	im*) do_image ;;
	fi*) do_finish ;;
	*)   usage ;;
esac
Added tools/corecount.












































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/bash
########################################################################
# corecount - Prints the number of CPU cores found on this system
#
# 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.
########################################################################

sys="$(uname -s)"
if [ -r /proc/cpuinfo ]
then
	# It's a Linux box
	grep -Ec '^processor\s+:' /proc/cpuinfo
elif [ "$sys" = "Darwin" -o "$sys" = "FreeBSD" -o "$sys" = "OpenBSD" -o "$sys" = "NetBSD" ]
then
	# It's a macOS or BSD box
	/usr/sbin/sysctl -n hw.ncpu
else
	# No known way to find out, so report only 1 core
	echo 1
fi
Added tools/mkbootscript.



















































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/env perl
########################################################################
# mkbootscript - Generate boot/*.script from obj/*.lst.  See the usage
#     message below for more details.
#
# 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 File::Basename;

# Parse command line
my $outPath = '../boot';
if (@ARGV == 0 || ! -r $ARGV[0]) {
	print <<USAGE;
usage: $0 <somefile.lst>

Given a palbart listing file, transform its contents into a SIMH boot
script named after the listing file.  Comments in the listing file are
variously translated to either comments or echo statements in the boot
script.

Output is stored in $outPath/somefile.script relative to the listing
file, as makes sense when translating obj/*.lst files produced from
examples/*.pal files.

USAGE
	exit 1;
}

# Globals
my $keepComments = 1;	# keep header comments; ignore the rest
my (@comments, @directives);
my $firstAddr;
my %core;
my $inFile = $ARGV[0];
my $bni = basename($inFile);
my $outFile = join('/', ( dirname($inFile), $outPath, $bni ));
$outFile =~ s{\.lst$}{.script};
my $oneLiner = $bni;	# use input file name as one-liner fall-back


# Parse the input file
my $comment;
open my $lst, '<', $inFile or die "Cannot read $inFile: $!\n";
while (<$lst>) {
	chomp;
	my ($line, $addr, $val, $tail) = m{
		^\s+		# ignore leading whitespace
		(\d+\s+)	# first number on the line must be a line number
		(\d+)?		# if followed by another number, it's an address
		\s*			# ignore space between addr and val
		(\d+)?		# ...and the value to store at that address
		(.*)		# everything else on the line
		$
	}x;
	my ($lineIsEmpty) = m{^\s+\d+\s*$};
	next unless $lineIsEmpty || $tail;
	($comment) = $tail =~ m{/\s*(.*)$} if $tail;

	if (defined $line and int($line) == 1 and defined $comment) {
		# Save the comment on the first line as a one-line description
		# of what the program does, emitted to the console by SIMH when
		# running our output script unless we find a "SIMH: echo..."
		# directive later on in that file, which overrides it.
		$oneLiner = $comment;
		$oneLiner =~ s{ - }{: };
		$oneLiner =~ s{([\w-]+).pal}{"$1" example};
	}
	elsif ($keepComments and defined $lineIsEmpty) {
		# The first blank line in the input file will appear in the
		# listing as a file containing only a line number.  Stop saving
		# comments, since everything after this would be comments on
		# individual source lines, which don't get copied into output.
		$keepComments = 0;
	}
	elsif (defined $addr and defined $val) {
		# Save address and value to our core image
		$firstAddr = oct($addr) if not defined $firstAddr;
		$core{oct($addr)} = oct($val);
	}
	elsif ($keepComments and defined $comment and defined $line) {
		# It's a header comment line
		if ($comment =~ m{^SIMH: }) {
			# It's a directive to SIMH, so save it separately
			push @directives, substr($comment, 6);
		}
		else {
			# Nothing special, so just save the text portion
			push @comments, $comment;
		}
	}
}
close $lst;

# Remove leading and trailing blank comment lines 
while (@comments and length($comments[0]) == 0) {
	shift @comments;
}
while (@comments and length($comments[$#comments]) == 0) {
	pop @comments;
}

# Write parsed data into output file
open my $scr, '>', $outFile or die "Cannot write $outFile: $!\n";
for my $c (@comments) {
	print $scr "; $c\n";
}
print $scr ";\n";
my $foundEcho;
for my $d (@directives) {
	print $scr $d, "\n";
	$foundEcho = 1 if $d =~ m{^echo };
}
print $scr "echo Running $oneLiner...\n" unless $foundEcho;
for my $a (sort { $a <=> $b } keys %core) {
	printf $scr "dep %05o %04o\n", $a, $core{$a};
}
printf $scr "go  %05o\n", $firstAddr;
close $scr;
print "Converted $inFile to $outFile\n";
Added tools/mkrel.













































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/bash
########################################################################
# mkrel - Automatically merge trunk changes into the release branch
#   for a new public release of the software.  Also tags the trunk with
#   the date of release, so old releases can be easily checked out.
#
# Copyright © 2016-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.
########################################################################

set -e

tag=v$(date +%Y%m%d)
fossil update &&
	fossil ci --tag $tag -m "Tagged release $tag" &&
	cd ../release &&
	fossil update &&
	fossil merge $tag &&
	! fossil status | grep -q '^CONFLICT' &&
	tools/mmake &&
	fossil diff -w | less &&
	fossil ci -m "Merged trunk changes for $tag into release branch"
Added tools/mmake.
































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/bash
########################################################################
# mmake - Runs make -jN where N is 1.5x the number of CPU cores
#
# 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.
########################################################################

make -j$(($("$(dirname "$0")"/corecount) * 15 / 10)) "$@"
Added tools/simh-update.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/bash
########################################################################
# simh-update - Attempt to automatically merge in the latest upstream
#	SIMH 4 changes from the GitHub repository.
#
# 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.
########################################################################

SRCDIR="@srcdir@"
SRCSUBDIR="$SRCDIR/src"
PRGNAME="$(basename "$0")"
WORKDIR="@builddir@/$PRGNAME-temp"
OUR_SIMH_DIR="$WORKDIR/simh/ours"
CURR_SIMH_DIR="$WORKDIR/simh/curr"
LOGFILE="$WORKDIR/output.log"
PATCHFILE="$WORKDIR/pidp8i.patch"
OLD_SGCID=$(grep ^SGCID "@srcdir@"/Makefile.in | cut -f2 -d=)

rm -rf "$WORKDIR"
mkdir -p "$WORKDIR"

# From here on, send all output to the log file.
# Code based on http://stackoverflow.com/a/20564208/142454
exec 1<&-
exec 2<&-
exec 1<>"$LOGFILE"
exec 2>&1
function say() {
	echo "$@" >> /dev/tty
}

# Bail on errors so we don't have to test everything.  Ideally, nothing
# from here on will fail.  If it does, the log file will explain it.
# Code based on http://stackoverflow.com/a/185900/142454
function error() {
	lineno="$1"
	message="$2"
	code="${3:-1}"
	if [ -n "$message" ] ; then message=": $message" ; fi
	read -r -d '%' errmsg <<ERROR
Error on or near line $lineno, code ${code}$message!  (See log file for
more info: $LOGFILE)
%
ERROR
	say
	say "$errmsg"
	say
	exit $code
}
trap 'error ${LINENO}' ERR

# Deal with uncommitted changes in $SRCSUBDIR
cd "$SRCSUBDIR/.."		# we need the src/ prefix to do this test properly!
if [ $(fossil status | grep '^EDITED.*src/' | wc -l) -gt 0 ]
then
	if [ "$1" = "-f" ]
	then
		say "Tossing uncommitted changes in $SRCSUBDIR..."
		fossil revert $(fossil status | grep '^EDITED.*src/' | cut -f 2- -d' ')
		shift
	else
		read -r -d '%' errmsg <<ERROR
Cowardly refusing to update SIMH to the current upstream version while
there are uncommitted changes in $SRCSUBDIR.  Say

    make simh-update-f

or pass -f to force those changes to be tossed.
%
ERROR
		say
		say "$errmsg"
		say
		exit 3
	fi
fi
cd "@builddir@"

# Retreive the tip-of-master and $OLD_SGCID versions of SIMH
say Retreiving upstream SIMH versions...
mkdir -p "$OUR_SIMH_DIR"
mkdir -p "$CURR_SIMH_DIR"
git clone https://github.com/simh/simh "$CURR_SIMH_DIR"
pushd "$CURR_SIMH_DIR"
NEW_SGCID=$(git rev-parse HEAD)
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"
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
	doc="doc/simh/$ft.doc"
	cp "$CURR_SIMH_DIR/doc/$fs.doc" "$doc"
	fossil uv add "$doc"
done
fossil uv sync
popd

# Rename upstream Git paths to match our *.in files so that our produced
# patches are made against those higher-level versions.  If we didn't do
# this, we'd have to manually resubstitute variables for absolute paths.
say Renaming upstream files to match our \*.in variants...
find "$SRCSUBDIR" -name \*.in -print | while read f
do
	inf="${f#$SRCSUBDIR/}"	# make path fully relative
	genf="${inf%.in}"		# remove .in from the end
	mv  "$OUR_SIMH_DIR/$genf"  "$OUR_SIMH_DIR/$inf"
	mv "$CURR_SIMH_DIR/$genf" "$CURR_SIMH_DIR/$inf"
done

# Produce a patch file for modifying the upstream $OLD_SGCID version to
# merge in our local changes.
#
# For some reason, diff(1) returns an error when we do this, at least on
# OS X.  Perhaps it is not happy about the fact that the file set in
# each tree is different?  Regardless of reason, we must check for a
# non-empty patch file to determine whether an actual error occurred.
say Producing clean patch file from upstream ${OLD_SGCID:0:8} version
say to our local PiDP-8/I version...
if ! diff -ru "$OUR_SIMH_DIR" "$SRCSUBDIR" | grep -v '^Only in' > "$PATCHFILE" &&
		 [ ! -s "$PATCHFILE" ]
then
	error $LINENO "patch generation failed" 2
fi

# For each file in src that is also present in the current upstream
# version of SIMH, overwrite our version.
find "$SRCSUBDIR" -type f -print | while read f
do
	base="${f#$SRCSUBDIR}"
	upstream="$CURR_SIMH_DIR/$base"
	test -e "$upstream" && cp "$upstream" "$f"
done

# Now try to apply the patch we made above to the upstream files.
patch -p0 < "$PATCHFILE"

# No error, so save the new tip-of-master Git commit ID and rebuild
say "Patch appears to have applied cleanly.  Attempting a rebuild..."
sed -e "s/^SGCID=.*/SGCID=$NEW_SGCID/" -i tmp @srcdir@/Makefile.in
tools/mmake

# Restore stdout and let the human test it
say "Build completed without error.  Starting OS/8 in the simulator..."
say
say "(Nuke $WORKDIR when finished.)"
say
exec 1<&-
exec 1<>/dev/tty
exec make os8test
Added tools/version.













































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/env perl
########################################################################
# tools/version - Print a string summarizing the software version
#
# 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 'abs_path';
use File::Basename;

my $topdir = dirname($0) . '/..';
if (not -e "$topdir/.fslckout") {
	# We're not within a Fossil checkout, so try to get the version
	# number from the top directory name.
	$topdir = basename(abs_path($topdir));
	my ($v) = $topdir =~ m{v\d+};
	print 'pkg:', ($v ? $v : 'vUNKNOWN'), "\n";
	exit 0;
}

# Get version info from Fossil
my ($branch, $checkout, $version, $comment);
open my $s, '-|', 'fossil status';
while (<$s>) {
	chomp;
	my ($attr, $value) = split /:\s+/;

	if ($attr eq 'checkout') {
		my @elems = split ' ', $value;
		$checkout = substr($elems[0], 0, 8);
	}
	elsif ($attr eq 'tags') {
		my @tags = split /, /, $value;
		for my $t (@tags) {
			if ($t =~ m{^v\d+}) {
				$version = $t;
			}
			else {
				$branch = $t;
			}
		}
	}
	elsif ($attr eq 'comment') {
		$comment = $value;
	}
}
close $s;

($version) = $comment =~ m{(v\d+)} if $branch eq 'release';
print $branch, ':', ($version || "i$checkout");