Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 3e64e6c138f89e77 To bc13bb069bccb650
2019-04-25
| ||
09:46 | Merged trunk changes for v20190425 into release branch check-in: 2e08e8ff1a user: tangent tags: release, v20190425 | |
2018-02-06
| ||
02:34 | Added named anchors to README-throttle.md section heads check-in: e336b70fc4 user: tangent tags: trunk | |
2018-01-30
| ||
09:46 | Added examples/fpptst.ft to demonstrate the generation of FPP12 instructions in OS/8 FORTRAN IV assembly (RALF) output even though the FPP feature of SIMH's PDP-8 simulator is not enabled by default in the PiDP-8/I stock simulator init scripts. check-in: bc13bb069b user: tangent tags: trunk | |
2018-01-29
| ||
09:36 | Merged SIMH upstream again, this time finally fixing upstream issue #508, which I'm now going to close. check-in: 3278b60481 user: tangent tags: trunk | |
2017-12-23
| ||
03:55 | Merged doc fix in check-in: 3e64e6c138 user: tangent tags: release, v20171222 | |
03:55 | Prior update to doc/OS-images.md made for confusing grammar in the paragraph following the removed paragraph. Fixed. Retagged release v20171222. check-in: 4c2353525a user: tangent tags: trunk | |
03:29 | Merged dependency fix in from trunk check-in: 32e794a04b user: tangent tags: release | |
Changes to HACKERS.md.
︙ | ︙ | |||
433 434 435 436 437 438 439 | should be placed here. (Contrast `bin` which does have some files checked into Fossil; all of the *other* files that end up in `bin` can be recreated by `make`, but not these few hand-written programs.) * <b>`src`</b> - Source code for the project's programs, especially | | > | | | | > > > | > > | > | 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 | should be placed here. (Contrast `bin` which does have some files checked into Fossil; all of the *other* files that end up in `bin` can be recreated by `make`, but not these few hand-written programs.) * <b>`src`</b> - Source code for the project's programs, especially those that cannot be used until they are built. The great majority of these programs are written in C. The build system's output directories are `bin`, `boot`, `libexec`, and `obj`. Programs that can be used without being "built," example programs, and single-file scripts are placed elsewhere: `bin`, `examples`, `libexec`, `tools`, etc. Basically, we place such files where the build system *would* place them if they were built from something under `src`. There are no program sources in the top level of `src`. The file `src/config.h` may appear to be an exception to that restriction, but it is *generated output* of the `configure` script, not "source code" *per se*. Multi-module programs each have their own subdirectory of `src`, each named after the program contained within. Single module programs live in `src/misc` or `src/asm`, depending on whether they are host-side C programs or PAL8 assembly programs. * <b>`test`</b> - Output directory used by `tools/test-*`. * <b>`tools`</b> - Programs run only during development and not installed. If a program is initially created here but we later decide that it |
︙ | ︙ |
Changes to Makefile.in.
1 2 3 4 5 6 7 8 9 | ######################################################################## # 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. # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ######################################################################## # 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 by Oscar Vermeulen, © 2016-2018 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 |
︙ | ︙ | |||
32 33 34 35 36 37 38 | # 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. | | | < | | > > | | > > | > | 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 | # 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=097ebbb8ebf262c80c39f0d52a8880551abf55bf # C build flags for the PDP-8 simulator and its PiDP-8/I extensions. SIM_CFLAGS := @CFLAGS@ @BUILDMODE@ \ -Wno-unused-result -Wno-parentheses \ -DUSE_READER_THREAD -DHAVE_DLOPEN=$(subst .,,@SH_SOEXT@) -DPIDP8I \ -DSIM_ASYNCH_IO -DHAVE_REGEX_H -DHAVE_GLOB \ -DSIM_GIT_COMMIT_ID=$(SGCID) -D_GNU_SOURCE \ -U__STRICT_ANSI__ \ -I @srcdir@/src/SIMH -I @srcdir@/src/pidp8i -I src -I src/SIMH -I src/pidp8i PIDP8I_CFLAGS = $(SIM_CFLAGS) SIMH_CFLAGS = $(SIM_CFLAGS) SIMH_PDP8_CFLAGS = $(SIM_CFLAGS) # Greatly stripped-down build options for the cc8 cross-compiler # primarily because it's K&R C. Building under SIM_CFLAGS spews # pages of warnings. The only thing we share is whether to build # a debug or optimized version, from BUILDMODE, set by auto.def. CC8_CROSS_CFLAGS = -w @BUILDMODE@ # Krten's d8tape doesn't need much in the way of options. It builds # without warnings under the supported version of Raspbian. On some # other OS types, it complains about one of the printf() format # specifiers, which we cannot fix portably, so we suppress that one. D8TAPE_CFLAGS = -Wno-format @BUILDMODE@ # Simpler options for src/misc/*.c MISC_CFLAGS = -I @srcdir@/src/pidp8i -I src/pidp8i @BUILDMODE@ -std=c99 # Even simpler options for palbart. PALBART_CFLAGS = @BUILDMODE@ SIM = bin/pidp8i-sim BINS = $(SIM) @CC8_CROSS@ \ bin/d8tape \ bin/palbart \ bin/pidp8i-test \ |
︙ | ︙ | |||
93 94 95 96 97 98 99 | MKOS8_PY := \ $(MKOS8_LIB)/__init__.py \ $(MKOS8_LIB)/argparser.py \ lib/mkos8/opts.py MKOS8_PY_ALL := $(GENNED_PY) $(MKOS8_PY) $(SIMH_PY_SRC) MKOS8_SRCS := $(MKOS8) $(MKOS8_PY_ALL) $(PIDP8I_DIN) | > > | > > > > > > > > > | > | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | 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 | MKOS8_PY := \ $(MKOS8_LIB)/__init__.py \ $(MKOS8_LIB)/argparser.py \ lib/mkos8/opts.py MKOS8_PY_ALL := $(GENNED_PY) $(MKOS8_PY) $(SIMH_PY_SRC) MKOS8_SRCS := $(MKOS8) $(MKOS8_PY_ALL) $(PIDP8I_DIN) # If you edit this directory list, you should probably also edit # the argument list to the mkadrules call in auto.def! BUILDDIRS := \ bin \ libexec \ obj/cc8/cross \ obj/d8tape \ obj/misc \ obj/palbart \ obj/pidp8i \ obj/SIMH \ obj/SIMH/PDP8 INSTDIRS := bin etc lib/mkos8 lib/pidp8i libexec share/boot share/media share/include share/man/man1 SIM_OBJS := \ obj/pidp8i/gpio-common.o \ obj/pidp8i/main.o \ obj/SIMH/PDP8/pdp8_df.o \ obj/SIMH/PDP8/pdp8_cpu.o \ obj/SIMH/PDP8/pdp8_clk.o \ obj/SIMH/PDP8/pdp8_ct.o \ obj/SIMH/PDP8/pdp8_dt.o \ obj/SIMH/PDP8/pdp8_fpp.o \ obj/SIMH/PDP8/pdp8_lp.o \ obj/SIMH/PDP8/pdp8_mt.o \ obj/SIMH/PDP8/pdp8_pt.o \ obj/SIMH/PDP8/pdp8_rf.o \ obj/SIMH/PDP8/pdp8_rk.o \ obj/SIMH/PDP8/pdp8_rl.o \ obj/SIMH/PDP8/pdp8_rx.o \ obj/SIMH/PDP8/pdp8_sys.o \ obj/SIMH/PDP8/pdp8_td.o \ obj/SIMH/PDP8/pdp8_tsc.o \ obj/SIMH/PDP8/pdp8_tt.o \ obj/SIMH/PDP8/pdp8_ttx.o \ obj/SIMH/scp.o \ obj/SIMH/sim_console.o \ obj/SIMH/sim_disk.o \ obj/SIMH/sim_ether.o \ obj/SIMH/sim_fio.o \ obj/SIMH/sim_serial.o \ obj/SIMH/sim_sock.o \ obj/SIMH/sim_tape.o \ obj/SIMH/sim_timer.o \ obj/SIMH/sim_tmxr.o \ obj/SIMH/sim_video.o CC8_OBJS := \ obj/cc8/cross/code8.o \ obj/cc8/cross/data.o \ obj/cc8/cross/error.o \ obj/cc8/cross/expr.o \ obj/cc8/cross/function.o \ |
︙ | ︙ | |||
153 154 155 156 157 158 159 | D8TAPE_OBJS := \ obj/d8tape/dasm.o \ obj/d8tape/flow.o \ obj/d8tape/main.o \ obj/d8tape/version.o MISC_OBJS := \ | | | | | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | D8TAPE_OBJS := \ obj/d8tape/dasm.o \ obj/d8tape/flow.o \ obj/d8tape/main.o \ obj/d8tape/version.o MISC_OBJS := \ obj/misc/ptp2txt.o \ obj/misc/scanswitch.o \ obj/misc/test.o PALBART_OBJS := obj/palbart/palbart.o ifeq (@BUILD_DEEPER_THOUGHT@, 1) BINS += bin/deeper endif |
︙ | ︙ | |||
198 199 200 201 202 203 204 | # rule being processed to create that target fails. # # The rest have no special treatment. MKOS8_INFILES = \ @srcdir@/lib/pidp8i/__init__.py.in \ @srcdir@/lib/pidp8i/ips.py.in \ @srcdir@/media/os8/init.tx.in \ | | > | | | 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 | # rule being processed to create that target fails. # # The rest have no special treatment. MKOS8_INFILES = \ @srcdir@/lib/pidp8i/__init__.py.in \ @srcdir@/lib/pidp8i/ips.py.in \ @srcdir@/media/os8/init.tx.in \ @srcdir@/src/pidp8i/main.c.in \ $(PIDP8I_DIN) PRECIOUS_INFILES = \ @srcdir@/Makefile.in \ @srcdir@/examples/Makefile.in \ @srcdir@/src/Makefile.in \ @srcdir@/src/cc8/Makefile.in \ @srcdir@/src/SIMH/Makefile.in \ @srcdir@/src/SIMH/PDP8/Makefile.in INFILES = \ @srcdir@/bin/pidp8i.in \ @srcdir@/boot/0.script.in \ @srcdir@/boot/2.script.in \ @srcdir@/boot/3.script.in \ @srcdir@/boot/4.script.in \ @srcdir@/boot/6.script.in \ @srcdir@/boot/7.script.in \ @srcdir@/boot/run.script.in \ @srcdir@/etc/pidp8i-init.in \ @srcdir@/etc/sudoers.in \ @srcdir@/etc/usb-mount@.service.in \ @srcdir@/src/pidp8i/gpio-common.c.in \ @srcdir@/tools/simh-update.in \ $(MKOS8_INFILES) MKOS8_OUTFILES := $(subst @srcdir@/,,$(MKOS8_INFILES)) MKOS8_OUTFILES := $(subst .in,,$(MKOS8_OUTFILES)) PRECIOUS_OUTFILES := $(subst @srcdir@/,,$(PRECIOUS_INFILES)) PRECIOUS_OUTFILES := $(subst .in,,$(PRECIOUS_OUTFILES)) OUTFILES := $(subst @srcdir@/,,$(INFILES)) |
︙ | ︙ | |||
271 272 273 274 275 276 277 | endif install: all instdirs @echo Installing to @prefix@... @# Install files into those dirs and set their perms @for f in $(BINS) ; do \ | | | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | endif install: all instdirs @echo Installing to @prefix@... @# Install files into those dirs and set their perms @for f in $(BINS) ; do \ dest=@prefix@/$$f ; \ echo "Installing binary $$dest..." ; \ @INSTALL@ -m 755 -D -s $$f $$dest ; \ done @for f in $(BIN_SCRIPTS) ; do \ dest=@prefix@/bin/$$(basename $$f) ; \ echo "Installing script $$dest..." ; \ @INSTALL@ -m 755 -D $$f $$dest ; \ |
︙ | ︙ | |||
415 416 417 418 419 420 421 422 423 424 425 426 427 428 | release: all @srcdir@/tools/mkrel run: $(SIM) $(SIM) boot/run.script simh-update simh-update-f: @@srcdir@/tools/simh-update $(subst simh-update,,$@) test-mkos8: tools/test-mkos8 | > > > | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | release: all @srcdir@/tools/mkrel run: $(SIM) $(SIM) boot/run.script run-tss8: $(SIM) $(SIM) boot/tss8.script simh-update simh-update-f: @@srcdir@/tools/simh-update $(subst simh-update,,$@) test-mkos8: tools/test-mkos8 |
︙ | ︙ | |||
493 494 495 496 497 498 499 | ln -f $< $@ boot/5.script: boot/ac-mq-blinker.script ln -f $< $@ $(BUILDDIRS): mkdir -p $@ | | | | | | | 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 | ln -f $< $@ boot/5.script: boot/ac-mq-blinker.script ln -f $< $@ $(BUILDDIRS): mkdir -p $@ $(SIM): $(SIM_OBJS) obj/pidp8i/gpio-@LED_DRIVER_MODULE@ls.o $(CC) -o $@ $^ $(LIBS) bin/cc8: $(CC8_OBJS) $(CC) -o $@ $^ $(LIBS) bin/d8tape: $(D8TAPE_OBJS) $(CC) -o $@ $^ bin/palbart: $(PALBART_OBJS) $(CC) -o $@ $^ bin/pidp8i-test: obj/misc/test.o obj/pidp8i/gpio-nls.o obj/pidp8i/gpio-common.o $(CC) -o $@ $^ $(LIBS) -lncurses bin/ptp2txt: obj/misc/ptp2txt.o $(CC) -o $@ $^ ln -f bin/ptp2txt bin/txt2ptp bin/txt2ptp: bin/ptp2txt ln -f bin/ptp2txt bin/txt2ptp bin/deeper: obj/deeper.o obj/pidp8i/gpio-@LED_DRIVER_MODULE@ls.o obj/pidp8i/gpio-common.o $(CC) -o $@ $^ $(LIBS) libexec/scanswitch: obj/misc/scanswitch.o obj/pidp8i/gpio-nls.o obj/pidp8i/gpio-common.o $(CC) -o $@ $^ $(LIBS) # Reconfigure whenever one of the *.in or autosetup files changes unless # this is "make clean". # # We purposely list only one of the OUTFILES on the left hand side # because to list them all is to invite Make to run N copies of the |
︙ | ︙ | |||
541 542 543 544 545 546 547 | # that if re-run, it would generate different output. $(ADF): @srcdir@/tools/mkadrules @AUTOREMAKE@ && $(MAKE) endif # Rebuild simulator if the version string tool changes, since its output # may have changed. | | | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 | # that if re-run, it would generate different output. $(ADF): @srcdir@/tools/mkadrules @AUTOREMAKE@ && $(MAKE) endif # Rebuild simulator if the version string tool changes, since its output # may have changed. src/pidp8i/gpio-common.c: @srcdir@/tools/version # Pull in *.d files generated by the autodependency mechanism. See the # header comment of tools/mkadrules. -include \ $(SIM_OBJS:.o=.d) \ $(CC8_OBJS:.o=.d) \ $(D8TAPE_OBJS:.o=.d) \ $(MISC_OBJS:.o=.d) \ $(PALBART_OBJS:.o=.d) -include $(ADF) |
Changes to README-throttle.md.
︙ | ︙ | |||
170 171 172 173 174 175 176 177 178 179 180 181 182 183 | value set. Therefore, you should only use the ratio form of throttle value when you need to get under 1000 instructions per second. For any value over that, give `--throttle=1k` or higher, which allows the ILS feature to properly maintain its LED brightness values. ## 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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | value set. Therefore, you should only use the ratio form of throttle value when you need to get under 1000 instructions per second. For any value over that, give `--throttle=1k` or higher, which allows the ILS feature to properly maintain its LED brightness values. ## Throttle Stabilization In early January 2018, the [upstream SIMH v4 project][simh] changed the way throttling is handled in that the simulator doesn't make any decisions about whether your requested throttle value is plausible until some seconds after the simulator starts. The SIMH default for this is 20 seconds, since the default must work for all simulators in the SIMH family, some of which have long bootup cycles. 20 seconds is far too long for a PDP-8, which boots instantly, so we've overridden that in the stock `boot/*.script` files, setting the throttle calibration delay to 3 seconds in order to give the SIMH timing code a sufficiently long baseline to work from. For those first 3 seconds, the simulator runs *unthrottled*, after which the SIMH core timing code looks at the number of instructions executed during that time and then determines from that what timing values it needs to use to achieve your requested throttle value. It also checks whether the throttle value is even possible at this time. There is one case where we anticipate that you might want to increase this value: you've set a fixed throttle value that is right near the host CPU's ability to achieve and you have the simulator set to start at host boot time, so that there are lots of processes starting up and initializing in parallel with the PiDP-8/I simulator. In that case, you might want to override the following line in `boot/*.script`, setting a long enough value for the system load to stabilize: deposit int-throttle THROT_DELAY 15 That would override the 20-second stabilization time default to 15 seconds. [simh]: https://github.com/simh/simh/issues/508#issuecomment-359855788 ## 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 |
︙ | ︙ |
Changes to README.md.
︙ | ︙ | |||
23 24 25 26 27 28 29 | * We require several tools and libraries that aren't always installed: * A working C compiler and other standard Linux build tools, such as `make(1)`. * Python's `pexpect` library | < < < < < < < < < < < < < < < < < < < < < | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | * We require several tools and libraries that aren't always installed: * A working C compiler and other standard Linux build tools, such as `make(1)`. * Python's `pexpect` library * The `ncurses` development libraries To install all of this on a Raspbian type OS, say: $ sudo apt update $ sudo apt install build-essential $ sudo apt install libncurses-dev python-pip $ sudo pip install pexpect [os]: https://tangentsoft.com/pidp8i/wiki?name=OS+Compatibility <a id="unpacking"></a> ## Getting the Software onto Your Pi |
︙ | ︙ | |||
647 648 649 650 651 652 653 | holds the OS/8 image the simulator boots from by default. These media image files are considered "precious" because you may have modified the OS configuration or saved personal files to the disk the OS boots from, which in turn modifies this media image file out in the host operating environment. There is an important exception here: when upgrading from v20170404 | | | 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 | holds the OS/8 image the simulator boots from by default. These media image files are considered "precious" because you may have modified the OS configuration or saved personal files to the disk the OS boots from, which in turn modifies this media image file out in the host operating environment. There is an important exception here: when upgrading from v20170404 to v20171222 or newer, the old `os8.rk05` disk image will be left untouched per the above, but because `os8v3d-*.rk05` does not exist yet, those will be copied alongside `os8.rk05`. However, the next item still holds, so that the simulator will continue to use `os8.rk05` because it will be booted by the preexisting scripts. 2. **The PDP-8 simulator configuration files**, installed as `$prefix/share/boot/*.script`, which may similarly have local |
︙ | ︙ |
Changes to auto.def.
1 2 3 4 | ######################################################################## # auto.def - Configure file for the PiDP-8/I software build system, # based on autosetup. # | | | 1 2 3 4 5 6 7 8 9 10 11 12 | ######################################################################## # auto.def - Configure file for the PiDP-8/I software build system, # based on autosetup. # # Copyright © 2016-2018 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 |
︙ | ︙ | |||
431 432 433 434 435 436 437 438 | } # Generate autodependency Makefile rule sets. # # It is important to list "src" last, because GNU make takes the first # one that matches, and the wildcards in the generated rules for "src" # match all "src/*" subdirs. set status [catch { | > > > | > > > > > > > > | 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 | } # Generate autodependency Makefile rule sets. # # It is important to list "src" last, because GNU make takes the first # one that matches, and the wildcards in the generated rules for "src" # match all "src/*" subdirs. # # If you edit this directory list, you should probably also edit # BUILDDIRS in Makefile.in! set status [catch { exec $srcdir/tools/mkadrules \ $srcdir \ src/cc8/cross \ src/d8tape \ src/misc \ src/palbart \ src/pidp8i \ src/SIMH \ src/SIMH/PDP8 } result] if {$status == 0} { msg-result $result } else { user-error "Failed to generate autodependency rules: $result!" } |
︙ | ︙ | |||
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | make-template boot/0.script.in make-template boot/2.script.in make-template boot/3.script.in make-template boot/4.script.in make-template boot/6.script.in make-template boot/7.script.in make-template boot/run.script.in make-template etc/pidp8i-init.in make-template etc/sudoers.in make-template etc/usb-mount@.service.in make-template examples/Makefile.in make-template lib/pidp8i/__init__.py.in make-template lib/pidp8i/dirs.py.in make-template lib/pidp8i/ips.py.in make-template media/os8/init.tx.in make-template src/Makefile.in make-template src/cc8/Makefile.in make-template src/cc8/os8/Makefile.in | > > | | | | 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 | make-template boot/0.script.in make-template boot/2.script.in make-template boot/3.script.in make-template boot/4.script.in make-template boot/6.script.in make-template boot/7.script.in make-template boot/run.script.in make-template boot/tss8.script.in make-template etc/pidp8i-init.in make-template etc/sudoers.in make-template etc/usb-mount@.service.in make-template examples/Makefile.in make-template lib/pidp8i/__init__.py.in make-template lib/pidp8i/dirs.py.in make-template lib/pidp8i/ips.py.in make-template media/os8/init.tx.in make-template src/Makefile.in make-template src/cc8/Makefile.in make-template src/cc8/os8/Makefile.in make-template src/pidp8i/gpio-common.c.in make-template src/pidp8i/main.c.in make-template src/SIMH/Makefile.in make-template src/SIMH/PDP8/Makefile.in make-template tools/simh-update.in exec chmod +x "$builddir/tools/simh-update" |
Changes to bin/cc8-to-os8.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # $ cc8-to-os8 < foo.c | txt2ptp > foo.pt # # Or you can just give it one or more file name(s), with output going # to stdout, which gives this way to hack around the lack of #include: # # $ cc8-to-os8 myheader.h foo.c > OS8FOO.C # | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # $ cc8-to-os8 < foo.c | txt2ptp > foo.pt # # Or you can just give it one or more file name(s), with output going # to stdout, which gives this way to hack around the lack of #include: # # $ cc8-to-os8 myheader.h foo.c > OS8FOO.C # # Copyright © 2017-2018 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 |
︙ | ︙ | |||
45 46 47 48 49 50 51 | use strict; use warnings; my $nblines = 0; # number of non-blank lines written out while (<>) { | < < < < < | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | use strict; use warnings; my $nblines = 0; # number of non-blank lines written out while (<>) { if (!m{^\#include } and length and $nblines) { # Pass it through unchanged. print; } else { # Unlike the CC8 cross-compiler, the OS/8 version of CC8 doesn't # process #includes, or in fact any C preprocessor directives, # not even #asm. Strip just the #includes in the hope that it's # init.h or libc.h, the contents of which are hard-coded into # the OS/8 CC8 compiler. } } |
Changes to boot/0.script.in.
︙ | ︙ | |||
23 24 25 26 27 28 29 30 31 32 33 34 35 36 | ; reset echo Loading OS/8 from the RK05 cartridge disk... set cpu 32k set cpu noidle set df disabled @SET_THROTTLE@ @if SIMH_PASS_LOWERCASE ; The software was configured with either --lowercase=auto or =pass, ; so send all text input to the simulator as 7-bit ASCII, including ; lowercase. Lowercase output from the simulator will be sent to the ; console unchanged, as will non-printing chars. set tti 7b | > > > > | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ; reset echo Loading OS/8 from the RK05 cartridge disk... set cpu 32k set cpu noidle set df disabled @SET_THROTTLE@ ; Override the default simulator throttle stabilization time. See ; README-throttle.md for details. deposit int-throttle THROT_DELAY 3 @if SIMH_PASS_LOWERCASE ; The software was configured with either --lowercase=auto or =pass, ; so send all text input to the simulator as 7-bit ASCII, including ; lowercase. Lowercase output from the simulator will be sent to the ; console unchanged, as will non-printing chars. set tti 7b |
︙ | ︙ |
Changes to boot/2.script.in.
1 2 3 4 5 6 7 8 9 10 11 12 13 | ; 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 cpu noidle @SET_THROTTLE@ attach rf @MEDIADIR@/tss8/tss8_rf.dsk attach ttix 4000 run 24200 | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ; 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 cpu noidle @SET_THROTTLE@ deposit int-throttle THROT_DELAY 3 attach rf @MEDIADIR@/tss8/tss8_rf.dsk attach ttix 4000 run 24200 |
Changes to boot/3.script.in.
1 2 3 4 5 6 7 8 9 10 11 | ; Loads OS/8 from DECtape, as opposed to the RK05 based environment ; in 0.script. ; echo Loading OS/8 from DECtape... set df disabled set dt disabled set td enabled set cpu noidle @SET_THROTTLE@ attach td0 @MEDIADIR@/os8/os8.tu56 boot td0 | > | 1 2 3 4 5 6 7 8 9 10 11 12 | ; Loads OS/8 from DECtape, as opposed to the RK05 based environment ; in 0.script. ; echo Loading OS/8 from DECtape... set df disabled set dt disabled set td enabled set cpu noidle @SET_THROTTLE@ deposit int-throttle THROT_DELAY 3 attach td0 @MEDIADIR@/os8/os8.tu56 boot td0 |
Changes to boot/4.script.in.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 | 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 cpu noidle @SET_THROTTLE@ set df disabled at ttix 2222 set ttox0 8b g 200 | > | 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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 cpu noidle @SET_THROTTLE@ deposit int-throttle THROT_DELAY 3 set df disabled at ttix 2222 set ttox0 8b g 200 |
Changes to boot/6.script.in.
1 2 3 4 5 6 7 8 9 10 11 12 13 | ; 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 df disabled set tsc enabled attach ttix 4000 att rk0 @MEDIADIR@/etos/etosv5b-demo.rk05 boot -d rk0 | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ; 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@ deposit int-throttle THROT_DELAY 3 set df disabled set tsc enabled attach ttix 4000 att rk0 @MEDIADIR@/etos/etosv5b-demo.rk05 boot -d rk0 |
Changes to boot/run.script.in.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ; Same as 0.script, only with RK0 attached to the *.rk05 file in the ; build directory, not the one in the installation directory. Used ; by "make run" so you don't have to "make mediainstall" first. ; reset echo Loading OS/8 from the RK05 cartridge disk in the build directory... set cpu 32k set cpu noidle set df disabled @SET_THROTTLE@ @if SIMH_PASS_LOWERCASE set tti 7b @else set tti ksr @endif | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ; Same as 0.script, only with RK0 attached to the *.rk05 file in the ; build directory, not the one in the installation directory. Used ; by "make run" so you don't have to "make mediainstall" first. ; reset echo Loading OS/8 from the RK05 cartridge disk in the build directory... set cpu 32k set cpu noidle set df disabled @SET_THROTTLE@ deposit int-throttle THROT_DELAY 3 @if SIMH_PASS_LOWERCASE set tti 7b @else set tti ksr @endif |
︙ | ︙ |
Added boot/tss8.script.in.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ; Same as 2.script, except that the binary disk image and core init ; image files are copied into the build tree's bin/ directory and run ; from there rather than being run from the installation tree. ; ; This is used by "make run-tss8" so you don't have to say ; "make mediainstall" then manually start the simulator with 2.script. ; echo Loading TSS/8 from the RS08 fixed-head disk... !cp media/tss8/tss8_init.bin bin load bin/tss8_init.bin set rf enabled set df disabled set cpu noidle @SET_THROTTLE@ deposit int-throttle THROT_DELAY 3 !cp -n media/tss8/tss8_rf.dsk bin attach rf bin/tss8_rf.dsk attach ttix 4000 run 24200 |
Changes to doc/RELEASE-PROCESS.md.
︙ | ︙ | |||
11 12 13 14 15 16 17 | * Fix all Immediate, High, and Medium priority [Bugs](/bugs) * Implement all Immediate and High priority [Features](/features) Or reclassify them, of course. Most of the bug levels simply aid scheduling: Immediate priority bugs should be fixed before High, etc. Low priority bugs are "someone should | | | > > > | | | | 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 | * Fix all Immediate, High, and Medium priority [Bugs](/bugs) * Implement all Immediate and High priority [Features](/features) Or reclassify them, of course. Most of the bug levels simply aid scheduling: Immediate priority bugs should be fixed before High, etc. Low priority bugs are "someone should fix this someday" type of problems; these are the only ones allowed to move from release to release. Think of bugs at this level as analogous to the `BUGS` section of a Unix man page: our "low" intent to fix these problems means they may stay at this level indefinitely, acting only as advisories to the software's users. The Features levels may be read as: * **Immediate**: ASAP, or sooner. :) * **High**: Features for the upcoming release. * **Medium**: Features we'll look at lifting to High for the release after that. * **Low**: "Wouldn't it be nice if..." ## Update SIMH If `tools/simh-update` hasn't been run recently, you might want to do that and re-test before publishing a new version. |
︙ | ︙ |
Changes to doc/class-simh.md.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # How to Control SIMH and OS/8 from Python ## Introduction While we were building the `libexec/mkos8` tool, we built up a set of functionality for driving SIMH and OS/8 running under SIMH from the outside using [Python][py], a very powerful programming language well suited to scripting tasks. It certainly beats writing PDP-8 code to achieve the same ends! When someone on the mailing list asked for a way to automatically drive a demo script he'd found online, it was natural to generalize the core functionality of `mkos8` as a reusable Python class, then write a script | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # How to Control SIMH and OS/8 from Python ## Introduction While we were building the `libexec/mkos8` tool, we built up a set of functionality for driving SIMH and OS/8 running under SIMH from the outside using [Python][py], a very powerful programming language well suited to scripting tasks. It certainly beats writing PDP-8 code to achieve the same ends! When someone on the mailing list asked for a way to automatically drive a demo script he'd found online, it was natural to generalize the core functionality of `mkos8` as a reusable Python class, then write a script to make use of it. The result is `class simh`, currently used by three different scripts, including `mkos8` and the `teco-pi-demo` demo script. This document describes how `teco-pi-demo` works, and through it, how `class simh` works, with an eye toward teaching you how to reuse this functionality for your own ends. [py]: https://www.python.org/ |
︙ | ︙ |
Changes to doc/os8-macrel.md.
︙ | ︙ | |||
34 35 36 37 38 39 40 | DECtapes, `AL-5643B-SA`, `AL-5644B-SA`, `AL-H602B-SA`, and `AL-H602B-SA`.) Very recently we found a complete set of `MACREL` version 2 binaries as part of a buildable RTS-8 Archive at [ibiblio.org ... pdp-8/rts8/v3/release][rts8rel] We found a source distribution of `MACREL` v2 in Dave Gesswein's | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | DECtapes, `AL-5643B-SA`, `AL-5644B-SA`, `AL-H602B-SA`, and `AL-H602B-SA`.) Very recently we found a complete set of `MACREL` version 2 binaries as part of a buildable RTS-8 Archive at [ibiblio.org ... pdp-8/rts8/v3/release][rts8rel] We found a source distribution of `MACREL` v2 in Dave Gesswein's [`misc_floppy`][dgfloppy] archive. Part one is flagged as having errors, but another obscure site had a mis-labeled archive of this same stuff so we may be ok. The `MACREL` v2 source would not build under `MACREL` v1, but now we have `MACREL` v2 and initial tests look promising. Baseline `MACREL` v2 will be integrated into the system packs. |
︙ | ︙ | |||
63 64 65 66 67 68 69 | integrate onto the system packs when we add `MACREL`. To reduce uncertainty around the operation of `OVRDRV.MA`, Source patch `41.5.1M` has been applied by hand to `OVRDRV.MA`. See also: [our documentaiton on the OS/8 Patching][os8patches] | | | | | | | | | | | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | integrate onto the system packs when we add `MACREL`. To reduce uncertainty around the operation of `OVRDRV.MA`, Source patch `41.5.1M` has been applied by hand to `OVRDRV.MA`. See also: [our documentaiton on the OS/8 Patching][os8patches] [vandermarkman]: http://vandermark.ch/pdp8/index.php?n=PDP8.Manuals [maclinkss]: http://vandermark.ch/pdp8/uploads/PDP8/PDP8.Manuals/AA-J073A-TA.txt [maclinkuser]: http://vandermark.ch/pdp8/uploads/PDP8/PDP8.Manuals/AA-5664B-TA.txt [dbitdocs]: ftp://ftp.dbit.com/pub/pdp8/doc/ [dbitmacssm]: ftp://ftp.dbit.com/pub/pdp8/doc/maclkssm.doc [dbitmacuser]: ftp://ftp.dbit.com/pub/pdp8/doc/maclnkum.doc [dbitmacrel]: ftp://ftp.dbit.com/pub/pdp8/doc/macrelrn.doc [rts8rel]: http://www.ibiblio.org/pub/academic/computer-science/history/pdp-8/rts8/v3/release [dgfloppy]: http://www.pdp8online.com/images/images/misc_floppy.shtml [os8patches]: https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-patching.md ### <a id="license"></a>License Copyright © 2017 by Bill Cattey. Licensed under the terms of [the SIMH license][sl]. [sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md |
Changes to doc/os8-patching.md.
︙ | ︙ | |||
25 26 27 28 29 30 31 | Then I enhanced our `mkos8` script to apply the patches in an automated way. Most of the patches were for programs available in source form, so I built the programs from source, and then bench checked the patch against the source. In a few cases the code was too obscure, and I marked the patch as "plausable" rather than "verified" in my spreadsheet. | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | Then I enhanced our `mkos8` script to apply the patches in an automated way. Most of the patches were for programs available in source form, so I built the programs from source, and then bench checked the patch against the source. In a few cases the code was too obscure, and I marked the patch as "plausable" rather than "verified" in my spreadsheet. The file [`patch_list.txt`][pl] lists all of the patch files in `media/os8/patches`. Comments in that file begin with `#` and are used to disable patches we have rejected for one reason or another. Each rejected patch also has a comment that explains why that particular patch was rejected from the default set. Typical reasons are: * The patch requires hardware our simulator doesn't have. |
︙ | ︙ | |||
143 144 145 146 147 148 149 | support. But if you happen to be using this setup under `TD8E` and `FRTS` doesn't work, then back out this patch. ## Patch Application Order The `patch` routine in `mkos8` applies the patches in the order they | | | | > | 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 | support. But if you happen to be using this setup under `TD8E` and `FRTS` doesn't work, then back out this patch. ## Patch Application Order The `patch` routine in `mkos8` applies the patches in the order they appear in [`patch_list.txt`][pl]. That list is currently in alphabetical order. However, there may in future emerge patches that impose an order. For example, if the `ABSLDR` patch actually did work, it needs the `FUTIL 31.21.2 M` in order to patch into the `ABSLDR` overlay bits. I was skeptical of `FUTIL 31.21.2M` because, when I load `ABSLDR.SV` into core with GET, the contents of memory showed by `ODT` are *DIFFERENT* from those shown by `FUTIL`. With deeper understanding of the OS/8 Device Extensions kit, I see that the patch was incorporated into the version 8 `FUTIL` source, and also that `ODT` is expected to be updated in version 3S of the Keyboard Monitor. ## Then There's `MACREL` I've gone into detail on the explorations and understandings with regard to `MACREL` in a [sister document to this one][macreldoc]. Originally I reviewed the patches for `MACREL` v1, because that's all |
︙ | ︙ | |||
179 180 181 182 183 184 185 | with a 12 bit number. It's called "5.06" but it's represented as `0772` octal, or `506` decimal. With that TECO patch, I simply changed the version amendment line in that `TECO` patch, because the rest was correct. Whoever published the patch got the version number wrong, and nobody complained. | | | < < | > | | > | | 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 | with a 12 bit number. It's called "5.06" but it's represented as `0772` octal, or `506` decimal. With that TECO patch, I simply changed the version amendment line in that `TECO` patch, because the rest was correct. Whoever published the patch got the version number wrong, and nobody complained. With no `MACREL` v1 source code, verification was not really possible, so applying those patches was postponed. But then we found both binary and source of `MACREL` v2! None of the available `MACREL` v2 patches are currently applied. We may get to that later. After further testing of `MACREL`, I have concluded that integrating the source-level patch `41.5.1M` will reduce uncertainty, so I have hand-integrated that patch into the `MACREL` tu56 image as well. [macreldoc]:https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-macrel.md ## `FUTIL` I was dubious of some of the `FUTIL` patches, but with finding source to version 8A, I gained confidence in the version 7 patches, and understood how seriously important the first patch was to version 8. The `MACREL` v2 tape shipped with version 8A of `FUTIL`. That was necessary because V2 of `MACREL` supported the latest memory expansion, |
︙ | ︙ |
Changes to etc/pidp8i-init.in.
1 2 | #!/bin/sh ### BEGIN INIT INFO | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/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 ######################################################################## |
︙ | ︙ | |||
58 59 60 61 62 63 64 | # 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() { | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > | | | | 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 | # 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..." elif [ $script -eq 127 ]; then echo "PiDP-8/I panel not present. Booting from 0.script..." bscript="@BOOTDIR@/0.script" else echo "Bad return value $script from $scanswitch!" exit 1 fi # We want SIMH to have a sensible working directory: somewhere the # user can write files and which makes sense when giving SIMH # commands involving file paths. This default is chosen because it # satisfies both criteria, plus it makes tools/mkos8 happy. # If you change the default here, change that script as well. cd "$prefix/share/media" log_daemon_msg "Starting PiDP-8/I simulator" "pidp8i" screenu -dmS pidp8i "$sim" $bscript status=$? log_end_msg $status 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 ; sleep 1 ; pkill -f pidp8i-sim status=$? log_end_msg $status fi return $status } case "$1" in start) do_start ;; stop) do_stop ;; restart) do_stop do_start ;; status) if screenu -ls pidp8i | egrep -q '[0-9]+\.pidp8i' then echo "The PiDP-8/I simulator is running." else echo "The PiDP-8/I simulator is STOPPED." fi ;; *) log_action_msg "Usage: /etc/init.d/pidp8i {start|stop|restart|status}" || true exit 1 esac exit 0 |
Changes to examples/README.md.
1 2 3 4 5 6 7 8 9 10 11 12 | # 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 | | > > > > > > | | 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 | # 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 * [**`pep001-*.c`**][c] — two solutions for the CC8 dialects of C * [**`pep001.fc`**][fc] — U/W FOCAL * [**`pep001-f?.ft`**][ft] — FORTRAN II and IV [pal]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.PA [bas]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.BA [c]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.C [fc]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.FC [ft]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.FT ## How to Use the BASIC Examples Here's one way to run the `pep001.ba` program mentioned above: .R BASIC NEW OR OLD--NEW FILE NAME--PEP001.BA READY 10 FOR I = 1 TO 999 |
︙ | ︙ | |||
54 55 56 57 58 59 60 | PEP001 BA 5A TOTAL: xxxxxxx READY BYE | | > > > > | > > > > > | > > > > > > > > > | > > > | | | > | > | | | 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 | PEP001 BA 5A TOTAL: xxxxxxx READY BYE While you could simply type all of that, if you're SSH'd into the PiDP-8/I, you could instead just copy-and-paste the bulk of the text above into OS/8 BASIC from the `examples/pep001.ba` file on the host side. This and several more useful methods are given in the companion article [Getting Text In][gti]. Other methods given in that article let you create the `PEP001.BA` file on the OS/8 disk first, allowing you to load it up within OS/8 BASIC like so: .R BASIC NEW OR OLD--old pep001.ba Notice that you can give the file name with the `NEW` or `OLD` command above, rather than wait for OS/8 BASIC to prompt you for it separately. Also notice that our version of OS/8 BASIC has a patch applied to it by default which allows it to tolerate lowercase input. (This patch may be disabled by giving the [`--lowercase` option to the `configure` script][lcopt].) I obscured the output in the first terminal transcript above 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. [gti]: https://tangentsoft.com/pidp8i/wiki?name=Getting+Text+In [lcopt]: https://tangentsoft.com/pidp8i/doc/trunk/README.md#lowercase ## How to Use the Assembly Language Examples For each PAL8 assembly program in `src/asm/*.pal` or `examples/*.pal`, the build process produces several output files: | Extension | Meaning | --------------- | --------------- | `*.pal` | the PAL8 assembly source code for the program; input to the process | `obj/*.lst` | the human-readable assembler output | `bin/*-pal.pt` | the machine-readable assembler output (RIM format) | `boot/*.script` | a SIMH-readable version of the assembled code Each of those files has a corresponding way of getting the example running in the simulator: 1. Transcribe the assembly program text to a file within a PDP-8 operating system and assemble it inside the simulator. 2. Toggle the machine code for the program in from the front panel. I can recommend this method only for very short programs. 3. Attach the `*-pal.pt` file to the simulator and read the assembly language text in, such as via the PiDP-8/I [automatic media mounting feature][howto]. 4. Boot SIMH with the example in core, running the program immediately. I cover each of these options below, in the same order as the list above. ### Option 1: Transcribing the Assembly Code into an OS/8 Session Perhaps the most period-correct of the options given here is to transcribe [`examples/add.pal`][pal] into the OS/8 simulation on a PiDP-8/I using the OS/8 `EDIT` program: .R EDIT *ADD.PA< #A ← append to ADD.PA *0200 CLA CLL MAIN, TAD A |
︙ | ︙ | |||
157 158 159 160 161 162 163 | .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! | | | | | > < | | | | | < < < < < < < | > | | | 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 | .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! 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`][teco] over `EDIT` or [MACREL][macrel] over [PAL8][pal8] — but the idea is the same regardless. There are [many more methods][gti] for getting program text into the simulator than simply transcribing it into an `EDIT` or `TECO` session. [macrel]: https://tangentsoft.com/pidp8i/wiki?name=A+Field+Guide+to+PDP-8+Assemblers#macrel [pal8]: https://tangentsoft.com/pidp8i/wiki?name=A+Field+Guide+to+PDP-8+Assemblers#pal8 [teco]: https://en.wikipedia.org/wiki/TECO_(text_editor) ### Option 2: Toggling Programs in Via the Front Panel One of the automatic steps in building the PiDP-8/I software is that each of the `examples/*.pal` and `src/asm/*.pal` files are assembled by `palbart` which writes out a human-readable listing file to `obj/*.lst`, each 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 |
︙ | ︙ | |||
219 220 221 222 223 224 225 | To run it, repeat the first step in the table above, loading the program's starting address (0200) first into the switch register (SR) and then into the PDP-8's program counter (PC) via `Load Add`. Then toggle `Start` to run the program. If you then toggle 000 010 000 111 into SR, press `Load Add` followed by | | | | | | | < | | | > | | 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 | To run it, repeat the first step in the table above, loading the program's starting address (0200) first into the switch register (SR) and then into the PDP-8's program counter (PC) via `Load Add`. Then toggle `Start` to run the program. If you then toggle 000 010 000 111 into SR, press `Load Add` followed by `Exam`, you should see 000 000 000 101 in the fourth row of lights — the Accumulator — that being the bit pattern for "five" at memory location 0207, the correct answer for "2 + 3", the purpose of `add.pal`. 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/*-pal.pt`. One 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, use the automatic USB media mounting feature of the PiDP-8/I to attach it to the simulator. 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 |
︙ | ︙ | |||
276 277 278 279 280 281 282 283 284 285 | `Start` to run the program. There is an SVG template for USB stick labels in the distribution under the [`labels/`][label] directory, for use if you find yourself creating long-lived USB sticks. See [`labels/README.md`][lread] for more information. ### Option 4: Booting SIMH with the Example in Core | > > | | > > > > | | > | | 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 | `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. This USB method is most convenient for often-reused binary media images. ### Option 4: Booting SIMH with the Example in Core Another step in the PiDP-8/I software build process produces a `boot/*.script` file for each `obj/*.lst` file produced. These files are direct translations of the machine code from the assembler's listing file into SIMH `deposit` commands, populating the simulated PDP-8's core memory with the program's machine code. Each script ends with a jump to the start of the program. You can thus load and run each example at the Linux command line with a command like this: $ bin/pidp8i-sim boot/add.script That runs the `examples/add.pal` program's assembled binary code under the simulator, just as if you'd loaded it there with option #3 above. ## License Copyright © 2016-2018 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/fpptst.ft.
> > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | C Test behavior of FP arithmetic on OS/8 FORTRAN IV: does it always C use the FPP, or does it fall back to a software library when the FPP C is not present? Generate RALF assembly output with: C C .R F4 C *,TTY:<FPPTST/F C C Note then the generated FLDA, FDIV, and FSTA instructions: these are C FPP12 instructions, not PDP-8 instructions. But also, note that it C runs on the PiDP-8/I via "EXE FPPTST.FT" even though we do not enable C the SIMH FPP8A feature by default! C C Fun note: This comment is in lowercase ASCII, but the F4 compiler C chokes with an RW error (read/write syntax error) if you give C lowercase in the FORMAT statement. Adventure manages to pull this C trick off somehow, so how do it do dat? C X = 1.0 / 2.0 WRITE (4,10) X 10 FORMAT (' HALF: ', F5.3) |
Changes to examples/pep001-ire0.c.
1 2 3 | #include <init.h> #include <libc.h> | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <init.h> #include <libc.h> int ire0 (n, d) int n, d; { while (n > 0) n = n - d; return n == 0; } int main () { int i, st; st = 0; for (i = 3; i < 1000; i++) { if (ire0 (i, 3) | ire0 (i, 5)) st = st + i; |
︙ | ︙ |
Changes to examples/pep001-mod.c.
1 2 3 | #include <init.h> #include <libc.h> | | | 1 2 3 4 5 6 7 8 9 10 11 | #include <init.h> #include <libc.h> int main () { int i, st; st = 0; for (i = 3; i < 1000; i++) { if ((i % 3 == 0) | (i % 5 == 0)) st = st + i; |
︙ | ︙ |
Changes to examples/pep001.bas.
1 2 3 4 5 6 7 | 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 | > > > | 1 2 3 4 5 6 7 8 9 10 | 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 |
︙ | ︙ |
Deleted src/PDP8/Makefile.in.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_clk.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_cpu.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_ct.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_defs.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_df.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_dt.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_fpp.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_lp.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_mt.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_pt.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_rf.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_rk.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_rl.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_rx.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_sys.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_td.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_tsc.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_tt.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pdp8_ttx.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pidp8i.c.in.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/PDP8/pidp8i.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added src/SIMH/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/SIMH 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/SIMH/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/SIMH/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/SIMH/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/SIMH/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 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 | /* pdp8_cpu.c: PDP-8 CPU simulator Copyright (c) 1993-2017, 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 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 07-Sep-17 RMS Fixed sim_eval declaration in history routine (COVERITY) 09-Mar-17 RMS Fixed PCQ_ENTRY for interrupts (COVERITY) 13-Feb-17 RMS RESET clear L'AC, per schematics 28-Jan-17 RMS Renamed switch register variable to SR, per request 18-Sep-16 RMS Added alternate dispatch table for non-contiguous devices 17-Sep-13 RMS Fixed boot in wrong field problem (Dave Gesswein) 28-Apr-07 RMS Removed clock initialization 30-Oct-06 RMS Added idle and infinite loop detection 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 */ #include "pdp8_defs.h" /* ---PiDP add---------------------------------------------------------------------------------------------- */ #include <pidp8i.h> /* ---PiDP end---------------------------------------------------------------------------------------------- */ #define PCQ_SIZE 64 /* must be 2**n */ #define PCQ_MASK (PCQ_SIZE - 1) #define PCQ_ENTRY(x) pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = x #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 SR = 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, SR, 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--------------------------------------------------------------------------------------------- */ // PiDP-8/I specific flag, set when the last instruction was an IOT // instruction to a real device. SIMH doesn't track this, but the front // panel needs it. int Pause = 0; // Set our initial IPS value from the throttle, if given. static time_t last_update = 0; static size_t max_skips = 0; static const size_t pidp8i_updates_per_sec = 3200; max_skips = get_pidp8i_initial_max_skips (pidp8i_updates_per_sec); srand48 (time (&last_update)); // Reset display info in case we're re-entering the simulator from Ctrl-E extern display display_bufs[2]; memset (display_bufs, 0, sizeof(display_bufs)); static size_t skip_count, dither, inst_count; skip_count = dither = inst_count = 0; /* ---PiDP end---------------------------------------------------------------------------------------------- */ /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ // Allow clean exit to SCP: https://github.com/simh/simh/issues/387 if (cpu_astop != 0) { cpu_astop = 0; reason = SCPE_STOP; break; } if (sim_interval <= 0) { /* check clock queue */ if ((reason = sim_process_event ())) { /* ---PiDP add--------------------------------------------------------------------------------------------- */ // We're about to leave the loop, so repaint one last time // in case this is a Ctrl-E and we later get a "cont" // command. Set a flag that will let us auto-resume. extern int resumeFromInstructionLoopExit, swStop, swSingInst; resumeFromInstructionLoopExit = swStop = swSingInst = 1; set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, int_req, Pause); // Also copy SR hardware value to software register in case // the user tries poking at it from the sim> prompt. SR = get_switch_register(); /* ---PiDP end---------------------------------------------------------------------------------------------- */ break; } } /* ---PiDP add--------------------------------------------------------------------------------------------- */ switch (handle_flow_control_switches(M, &PC, &MA, &MB, &LAC, &IF, &DF, &int_req)) { case pft_stop: // Tell the SIMH event queue to keep running even though // we're stopped. Without this, it will ignore Ctrl-E // until the simulator is back in free-running mode. sim_interval = sim_interval - 1; // Have to keep display updated while stopped. This does // mean if the software starts with the STOP switch held // down, we'll put garbage onto the display for MA, MB, and // IR, but that's what the real hardware does, too. See // https://github.com/simh/simh/issues/386 set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, int_req, Pause); // Go no further in STOP mode. In particular, fetch no more // instructions, and do not touch PC! continue; case pft_halt: // 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 */ PCQ_ENTRY (IF | PC); /* save old PC with IF */ IF = IB = DF = UF = UB = 0; /* clear mem ext */ 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 */ if (sim_brk_summ && sim_brk_test (IR, (2u << SIM_BKPT_V_SPC) | SWMASK ('I'))) { /* breakpoint? */ reason = STOP_OPBKPT; /* stop simulation */ break; } PC = (PC + 1) & 07777; /* increment PC */ int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ sim_interval = sim_interval - 1; /* 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); 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 = (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); 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 = (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); 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 = (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); 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 = (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--------------------------------------------------------------------------------------------- */ SR = get_switch_register(); /* get current SR */ /* ---PiDP end---------------------------------------------------------------------------------------------- */ LAC = LAC | SR; } if (IR & 02) { /* HLT */ //--- PiDP change----------------------------------------------------------------------- // reason = STOP_HALT; extern int swStop; swStop = 1; } //--- 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> */ 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 */ 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 Pause = 1; /* ---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--------------------------------------------------------------------------------------------- */ // Update the front panel with this instruction's final state. // // There's no point saving *every* LED "on" count. We just need a // suitable amount of oversampling. We can skip this if we called // set_pidp8i_leds recently enough, avoiding all the expensive bit // shift and memory update work it does. // // The trick here is figuring out what "recently enough" means // without making expensive OS timer calls. These timers aren't // hopelessly slow (http://stackoverflow.com/a/13096917/142454) but // we still don't want to be taking dozens of cycles per instruction // just to keep our update estimate current in the face of system // load changes and SET THROTTLE updates. // // Instead, we maintain a model of the current IPS value — seeded // with the initial "SET THROTTLE" value, if any — to figure out // how many calls we can skip while still meeting our other goals. // This involves a bit of math, but when paid only once a second, // it amortizes much nicer than estimating the skip count directly // based on a more accurate time source which is more expensive // to call. It's also cheaper than continually asking SIMH to // estimate the SIMH IPS value, since it uses FP math. // // Each LED panel repaint takes about 10 ms, so we do about 100 // full-panel updates per second. We need a bare minimum of 32 // discernible brightness values per update for ILS, so if we don't // update the LED status data at least 3,200 times per second, we // don't have enough data for smooth panel updates. Fortunately, // computers are pretty quick, and our slowest script runs at 30 // kIPS. (5.script.) // // We deliberately add some timing jitter here to get stochastic // sampling of the incoming instructions to avoid beat frequencies // between our update rate and the instruction pattern being // executed by the front panel. It's a form of dithering. // // You might think to move this code to the top of set_pidp8i_leds, // but the function call itself is a nontrivial hit. In fact, you // don't even want to move all of this to a function here in this // module and try to get GCC to inline it: that's good for a 1 MIPS // speed hit in my testing! (GCC 4.9.2, Raspbian Jessie on Pi 3B.) if (++skip_count >= (max_skips - dither)) { // Save skips to inst counter and reset inst_count += skip_count; skip_count = 0; // We need to update the LED data again set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, int_req, Pause); // Has it been ~1s since we updated our max_skips value? time_t now; if (time(&now) > last_update) { // Yep; simulator IPS may have changed, so freshen it. last_update = now; max_skips = inst_count / pidp8i_updates_per_sec; //printf("Inst./repaint: %zu - %zu; %.2f MIPS\r\n", // max_skips, dither, inst_count / 1e6); inst_count = 0; } dither = max_skips > 32 ? lrand48() % (max_skips >> 3) : 0; // 12.5% } Pause = 0; // it's set outside the "if", so it must be *reset* outside /* ---PiDP end---------------------------------------------------------------------------------------------- */ } /* end while */ /* Simulation halted */ saved_PC = IF | (PC & 07777); /* save copies */ saved_DF = DF & 070000; 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) { saved_LAC = 0; 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; 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[0] = h->ir; if ((fprint_sym (st, h->pc & ADDRMASK, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "(undefined) %04o", h->ir); if (h->ir < 04000) fprintf (st, " [%04o]", h->opnd); fputc ('\n', st); /* end line */ } /* end else instruction */ } /* end for */ return SCPE_OK; } |
Added src/SIMH/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/SIMH/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/SIMH/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/SIMH/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 1344 1345 1346 1347 1348 1349 | /* pdp8_dt.c: PDP-8 DECtape simulator Copyright (c) 1993-2017, 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 15-Mar-17 RMS Fixed dt_seterr to clear successor states 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; /* fall through */ 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; /* fall through */ 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 */ } else DTS_SETSTA (mot, 0); /* clear 2nd, 3rd */ 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/SIMH/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/SIMH/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/SIMH/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/SIMH/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 292 293 | /* pdp8_pt.c: PDP-8 paper tape reader/punch simulator Copyright (c) 1993-2017, 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 13-Mar-17 RMS Annotated fall through in switch 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); /* activate */ /* fall through */ 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/SIMH/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/SIMH/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/SIMH/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/SIMH/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/SIMH/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/SIMH/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/SIMH/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/SIMH/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, 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/SIMH/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/SIMH/scp.c.
more than 10,000 changes
Added src/SIMH/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 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 | /* 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 delete_cmd (int32 flg, CONST char *cptr); t_stat copy_cmd (int32 flg, CONST char *cptr); t_stat brk_cmd (int32 flag, CONST char *ptr); t_stat do_cmd (int32 flag, CONST char *ptr); t_stat goto_cmd (int32 flag, CONST char *ptr); t_stat return_cmd (int32 flag, CONST char *ptr); t_stat shift_cmd (int32 flag, CONST char *ptr); t_stat call_cmd (int32 flag, CONST char *ptr); t_stat on_cmd (int32 flag, CONST char *ptr); t_stat noop_cmd (int32 flag, CONST char *ptr); t_stat assert_cmd (int32 flag, CONST char *ptr); t_stat send_cmd (int32 flag, CONST char *ptr); t_stat expect_cmd (int32 flag, CONST char *ptr); t_stat sleep_cmd (int32 flag, CONST char *ptr); t_stat help_cmd (int32 flag, CONST char *ptr); t_stat screenshot_cmd (int32 flag, CONST char *ptr); t_stat spawn_cmd (int32 flag, CONST char *ptr); t_stat echo_cmd (int32 flag, CONST char *ptr); t_stat echof_cmd (int32 flag, CONST char *ptr); t_stat debug_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); const char *sim_set_uname (UNIT *uptr, const char *uname); t_stat get_yn (const char *ques, t_stat deflt); char *sim_trim_endspc (char *cptr); int sim_isspace (int c); #ifdef isspace #undef isspace #endif #ifndef IN_SCP_C #define isspace(chr) sim_isspace (chr) #endif int sim_islower (int c); #ifdef islower #undef islower #endif #ifndef IN_SCP_C #define islower(chr) sim_islower (chr) #endif int sim_isalpha (int c); #ifdef isalpha #undef isalpha #endif #ifndef IN_SCP_C #define isalpha(chr) sim_isalpha (chr) #endif int sim_isprint (int c); #ifdef isprint #undef isprint #endif #ifndef IN_SCP_C #define isprint(chr) sim_isprint (chr) #endif int sim_isdigit (int c); #ifdef isdigit #undef isdigit #endif #ifndef IN_SCP_C #define isdigit(chr) sim_isdigit (chr) #endif int sim_isgraph (int c); #ifdef isgraph #undef isgraph #endif #ifndef IN_SCP_C #define isgraph(chr) sim_isgraph (chr) #endif int sim_isalnum (int c); #ifdef isalnum #undef isalnum #endif #ifndef IN_SCP_C #define isalnum(chr) sim_isalnum (chr) #endif int sim_toupper (int c); int sim_tolower (int c); #ifdef toupper #undef toupper #endif #define toupper(chr) sim_toupper(chr) #ifdef tolower #undef tolower #endif #define tolower(chr) sim_tolower(chr) int sim_strncasecmp (const char *string1, const char *string2, size_t len); int sim_strcasecmp (const char *string1, const char *string2); size_t sim_strlcat (char *dst, const char *src, size_t size); size_t sim_strlcpy (char *dst, const char *src, size_t size); #ifndef strlcpy #define strlcpy(dst, src, size) sim_strlcpy((dst), (src), (size)) #endif #ifndef strlcat #define strlcat(dst, src, size) sim_strlcat((dst), (src), (size)) #endif #ifndef strncasecmp #define strncasecmp(str1, str2, len) sim_strncasecmp((str1), (str2), (len)) #endif #ifndef strcasecmp #define strcasecmp(str1, str2) sim_strcasecmp ((str1), (str2)) #endif CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st); const char *put_switches (char *buf, size_t bufsize, uint32 sw); CONST char *get_glyph (const char *iptr, char *optr, char mchar); CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar); CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar); CONST char *get_glyph_cmd (const char *iptr, char *optr); t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status); 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); char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream); void fprint_reg_help (FILE *st, DEVICE *dptr); void fprint_set_help (FILE *st, DEVICE *dptr); void fprint_show_help (FILE *st, DEVICE *dptr); CTAB *find_cmd (const char *gbuf); DEVICE *find_dev (const char *ptr); DEVICE *find_unit (const char *ptr, UNIT **uptr); DEVICE *find_dev_from_unit (UNIT *uptr); 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); t_value get_rval (REG *rptr, uint32 idx); 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); char *sim_brk_replace_act (char *new_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 #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 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 DEVICE *sim_dfdev; extern UNIT *sim_dfunit; extern int32 sim_interval; extern int32 sim_switches; extern int32 sim_switch_number; extern int32 sim_quiet; extern int32 sim_step; extern t_stat sim_last_cmd_stat; /* Command Status */ extern FILE *sim_log; /* log file */ extern FILEREF *sim_log_ref; /* log file file reference */ extern FILE *sim_deb; /* debug file */ extern FILEREF *sim_deb_ref; /* debug file file reference */ 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[64]; extern DEVICE *sim_devices[]; extern REG *sim_PC; extern const char *sim_stop_messages[SCPE_BASE]; 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/SIMH/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 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 | /* 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 sim_set_noconsole_port Enable automatic WRU console polling sim_set_stable_registers_state Declare that all registers are always stable 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 extern TMLN *sim_oline; /* global output socket */ 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 | DEV_NOSAVE, 0, sim_con_debug, NULL, NULL, NULL, NULL, NULL, sim_con_telnet_description}; TMLN sim_con_ldsc = { 0 }; /* console line descr */ TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc, NULL, &sim_con_telnet };/* console line mux */ SEND sim_con_send = {0, &sim_con_telnet, DBG_SND}; EXPECT sim_con_expect = {&sim_con_telnet, DBG_EXP}; static t_bool sim_con_console_port = TRUE; /* Enable automatic WRU console polling */ t_stat sim_set_noconsole_port (void) { sim_con_console_port = FALSE; return SCPE_OK; } static t_bool sim_con_stable_registers = FALSE; /* Enable automatic WRU console polling */ t_stat sim_set_stable_registers_state (void) { sim_con_stable_registers = TRUE; 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, -1 }, { "INPUT", &sim_show_cons_send_input, 0 }, { "RESPONSE", &sim_show_cons_send_input, -1 }, { "DELAY", &sim_show_cons_expect, -1 }, { NULL, NULL, 0 } }; static CTAB set_con_telnet_tab[] = { { "LOG", &sim_set_cons_log, 0 }, { "NOLOG", &sim_set_cons_nolog, 0 }, { "BUFFERED", &sim_set_cons_buff, 0 }, { "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++) if (show_con_tab[i].arg != -1) 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; } #define MAX_REMOTE_SESSIONS 40 /* Arbitrary Session Limit */ 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_repeat_svc (UNIT *uptr); /* remote auto repeat command console timing routine */ t_stat sim_rem_con_smp_collect_svc (UNIT *uptr); /* remote remote register data sampling routine */ t_stat sim_rem_con_reset (DEVICE *dptr); /* remote console reset routine */ #define rem_con_poll_unit (&sim_remote_console.units[0]) #define rem_con_data_unit (&sim_remote_console.units[1]) #define REM_CON_BASE_UNITS 2 #define rem_con_repeat_units (&sim_remote_console.units[REM_CON_BASE_UNITS]) #define rem_con_smp_smpl_units (&sim_remote_console.units[REM_CON_BASE_UNITS+sim_rem_con_tmxr.lines]) #define DBG_MOD 0x00000004 /* Remote Console Mode activities */ #define DBG_REP 0x00000008 /* Remote Console Repeat activities */ #define DBG_SAM 0x00000010 /* Remote Console Sample activities */ #define DBG_CMD 0x00000020 /* Remote Console Command activities */ 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"}, {"CMD", DBG_CMD, "Remote Console Command activity"}, {"MODE", DBG_MOD, "Remote Console Mode activity"}, {"REPEAT", DBG_REP, "Remote Console Repeat activity"}, {"SAMPLE", DBG_SAM, "Remote Console Sample 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", NULL, NULL, sim_rem_con_mod, 0, 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}; typedef struct BITSAMPLE BITSAMPLE; struct BITSAMPLE { int tot; /* total of all values */ int ptr; /* pointer to next value cell */ int depth; /* number of values */ int *vals; /* values */ }; typedef struct BITSAMPLE_REG BITSAMPLE_REG; struct BITSAMPLE_REG { REG *reg; /* Register to be sampled */ uint32 idx; /* Register index */ t_bool indirect; /* Register value points at memory */ DEVICE *dptr; /* Device register is part of */ UNIT *uptr; /* Unit Register is related to */ uint32 width; /* number of bits to sample */ BITSAMPLE *bits; }; typedef struct REMOTE REMOTE; struct REMOTE { int32 buf_size; int32 buf_ptr; char *buf; char *act_buf; size_t act_buf_size; char *act; t_bool single_mode; uint32 read_timeout; int line; /* remote console line number */ TMLN *lp; /* mux line/socket for remote session */ UNIT *uptr; /* remote console unit */ uint32 repeat_interval; /* usecs between repeat execution */ t_bool repeat_pending; /* repeat delivery pending */ char *repeat_action; /* command(s) to repeatedly execute */ int smp_sample_interval; /* cycles between samples */ int smp_sample_dither_pct; /* dithering of cycles interval */ uint32 smp_reg_count; /* sample register count */ BITSAMPLE_REG *smp_regs; /* registers being sampled */ }; REMOTE *sim_rem_consoles = NULL; static TMXR sim_rem_con_tmxr = { 0, 0, 0, NULL, NULL, &sim_remote_console };/* remote console line mux */ static uint32 sim_rem_read_timeout = 30; /* seconds before automatic continue */ static 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 */ static t_stat sim_rem_sample_output (FILE *st, int32 line) { REMOTE *rem = &sim_rem_consoles[line]; uint32 reg; if (rem->smp_reg_count == 0) { fprintf (st, "Samples are not being collected\n"); return SCPE_OK; } for (reg = 0; reg < rem->smp_reg_count; reg++) { uint32 bit; if (rem->smp_regs[reg].reg->depth > 1) fprintf (st, "}%s %s[%d] %s %d:", rem->smp_regs[reg].dptr->name, rem->smp_regs[reg].reg->name, rem->smp_regs[reg].idx, rem->smp_regs[reg].indirect ? " -I" : "", rem->smp_regs[reg].bits[0].depth); else fprintf (st, "}%s %s%s %d:", rem->smp_regs[reg].dptr->name, rem->smp_regs[reg].reg->name, rem->smp_regs[reg].indirect ? " -I" : "", rem->smp_regs[reg].bits[0].depth); for (bit = 0; bit < rem->smp_regs[reg].width; bit++) fprintf (st, "%s%d", (bit != 0) ? "," : "", rem->smp_regs[reg].bits[bit].tot); fprintf (st, "\n"); } return SCPE_OK; } /* 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; REMOTE *rem; 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", rem_con_poll_unit->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++) { rem = &sim_rem_consoles[i]; if (!rem->lp->conn) continue; ++connections; if (connections == 1) fprintf (st, "Remote Console Connections:\n"); tmxr_fconns (st, rem->lp, i); if (rem->read_timeout != sim_rem_read_timeout) { if (rem->read_timeout) fprintf (st, "Remote Console Input on connection %d automatically continues after %d seconds\n", i, rem->read_timeout); else fprintf (st, "Remote Console Input on connection %d does not continue automatically\n", i); } if (rem->repeat_action) { fprintf (st, "The Command: %s\n", rem->repeat_action); fprintf (st, " is repeated every %s\n", sim_fmt_secs (rem->repeat_interval / 1000000.0)); } if (rem->smp_reg_count) { uint32 reg; DEVICE *dptr = NULL; if (rem->smp_sample_dither_pct) fprintf (st, "Register Bit Sampling is occurring every %d cycles (dithered %d percent)\n", rem->smp_sample_interval, rem->smp_sample_dither_pct); else fprintf (st, "Register Bit Sampling is occurring every %d cycles\n", rem->smp_sample_interval); fprintf (st, " Registers being sampled are: "); for (reg = 0; reg < rem->smp_reg_count; reg++) { if (rem->smp_regs[reg].indirect) fprintf (st, " indirect "); if (dptr != rem->smp_regs[reg].dptr) fprintf (st, "%s ", rem->smp_regs[reg].dptr->name); if (rem->smp_regs[reg].reg->depth > 1) fprintf (st, "%s[%d]%s", rem->smp_regs[reg].reg->name, rem->smp_regs[reg].idx, ((reg + 1) < rem->smp_reg_count) ? ", " : ""); else fprintf (st, "%s%s", rem->smp_regs[reg].reg->name, ((reg + 1) < rem->smp_reg_count) ? ", " : ""); dptr = rem->smp_regs[reg].dptr; } fprintf (st, "\n"); if (sim_switches & SWMASK ('D')) sim_rem_sample_output (st, rem->line); } } 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 */ REMOTE *rem = &sim_rem_consoles[c]; TMLN *lp = rem->lp; char wru_name[8]; sim_activate_after(uptr+1, 1000000); /* start data poll after 1 second */ lp->rcve = 1; /* rcv enabled */ rem->buf_ptr = 0; /* start with empty command buffer */ rem->single_mode = TRUE; /* start in single command mode */ rem->read_timeout = 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? */ rem->single_mode = 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 1+SCPE_IERR; /* This routine should never be called */ } static t_stat x_repeat_cmd (int32 flag, CONST char *cptr) { return 2+SCPE_IERR; /* This routine should never be called */ } static t_stat x_collect_cmd (int32 flag, CONST char *cptr) { return 3+SCPE_IERR; /* This routine should never be called */ } static t_stat x_sampleout_cmd (int32 flag, CONST char *cptr) { return 4+SCPE_IERR; /* This routine should never be called */ } static t_stat x_execute_cmd (int32 flag, CONST char *cptr) { return 5+SCPE_IERR; /* This routine should never be called */ } static t_stat x_step_cmd (int32 flag, CONST char *cptr) { return 6+SCPE_IERR; /* This routine should never be called */ } static t_stat x_run_cmd (int32 flag, CONST char *cptr) { return 7+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 }, { "REPEAT", &x_repeat_cmd, 0 }, { "COLLECT", &x_collect_cmd, 0 }, { "SAMPLEOUT",&x_sampleout_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 }, { "ECHOF", &echof_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 }, { "REPEAT", &x_repeat_cmd, 0 }, { "COLLECT", &x_collect_cmd, 0 }, { "SAMPLEOUT",&x_sampleout_cmd, 0 }, { "EXECUTE", &x_execute_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 }, { "ECHOF", &echof_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 }, { "EXPECT", &expect_cmd, 1 }, { "NOEXPECT", &expect_cmd, 0 }, { "DEBUG", &debug_cmd, 1 }, { "NODEBUG", &debug_cmd, 0 }, { "SEND", &send_cmd, 0 }, { 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 }, { "REPEAT", &x_repeat_cmd, 0 }, { "COLLECT", &x_collect_cmd, 0 }, { "SAMPLEOUT",&x_sampleout_cmd, 0 }, { "EXECUTE", &x_execute_cmd, 0 }, { "PWD", &pwd_cmd, 0 }, { "DIR", &dir_cmd, 0 }, { "LS", &dir_cmd, 0 }, { "ECHO", &echo_cmd, 0 }, { "ECHOF", &echof_cmd, 0 }, { "SHOW", &show_cmd, 0 }, { "DEBUG", &debug_cmd, 1 }, { "NODEBUG", &debug_cmd, 0 }, { "HELP", &x_help_cmd, 0 }, { NULL, NULL } }; static CTAB remote_only_cmds[] = { { "REPEAT", &x_repeat_cmd, 0 }, { "COLLECT", &x_collect_cmd, 0 }, { "SAMPLEOUT",&x_sampleout_cmd, 0 }, { "EXECUTE", &x_execute_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]; REMOTE *rem = &sim_rem_consoles[(int)(lp - sim_rem_con_tmxr.ldsc)]; int line = rem->line; if ((!sim_oline) && (sim_log)) { 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); } sim_oline = NULL; if ((rem->act == NULL) && (!tmxr_input_pending_ln (lp))) { int32 unwritten; 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; strlcpy (cbuf, sim_rem_command_buf, sizeof (cbuf)); while (isspace(cbuf[0])) memmove (cbuf, cbuf+1, strlen(cbuf+1)+1); /* skip leading whitespace */ sim_sub_args (cbuf, sizeof(cbuf), argv); cptr = cbuf; cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ sim_rem_active_command = find_cmd (gbuf); /* find command */ if (!sim_processing_event) sim_ttcmd (); /* restore console */ stat = sim_rem_active_command->action (sim_rem_active_command->arg, cptr);/* execute command */ if (stat != SCPE_OK) stat = _sim_rem_message (gbuf, stat); /* display results */ sim_last_cmd_stat = SCPE_BARE_STATUS(stat); if (sim_vm_post != NULL) /* optionally let the simulator know */ (*sim_vm_post) (TRUE); /* something might have changed */ if (!sim_processing_event) { sim_ttrun (); /* set console mode */ sim_cancel (rem_con_data_unit); /* force immediate activation of sim_rem_con_data_svc */ sim_activate (rem_con_data_unit, -1); } sim_switches = saved_switches; /* restore original switches */ } /* Clear pending actions */ static char *sim_rem_clract (int32 line) { REMOTE *rem = &sim_rem_consoles[line]; tmxr_send_buffered_data (rem->lp); /* flush any buffered data */ return rem->act = NULL; } /* Set up pending actions */ static void sim_rem_setact (int32 line, const char *action) { if (action) { size_t act_size = strlen (action) + 1; REMOTE *rem = &sim_rem_consoles[line]; if (act_size > rem->act_buf_size) { /* expand buffer if necessary */ rem->act_buf = (char *)realloc (rem->act_buf, act_size); rem->act_buf_size = act_size; } strcpy (rem->act_buf, action); /* populate buffer */ rem->act = rem->act_buf; /* start at beginning of buffer */ } else sim_rem_clract (line); } /* Get next pending action, if any */ static char *sim_rem_getact (int32 line, char *buf, int32 size) { char *ep; size_t lnt; REMOTE *rem = &sim_rem_consoles[line]; if (rem->act == NULL) /* any action? */ return NULL; while (sim_isspace (*rem->act)) /* skip spaces */ rem->act++; if (*rem->act == 0) /* now empty? */ return sim_rem_clract (line); ep = strpbrk (rem->act, ";\"'"); /* search for a semicolon or single or double quote */ if ((ep != NULL) && (*ep != ';')) { /* if a quoted string is present */ char quote = *ep++; /* then save the opening quotation mark */ while (ep [0] != '\0' && ep [0] != quote) /* while characters remain within the quotes */ if (ep [0] == '\\' && ep [1] == quote) /* if an escaped quote sequence follows */ ep = ep + 2; /* then skip over the pair */ else /* otherwise */ ep = ep + 1; /* skip the non-quote character */ ep = strchr (ep, ';'); /* the next semicolon is outside the quotes if it exists */ } if (ep != NULL) { /* if a semicolon is present */ lnt = ep - rem->act; /* cmd length */ memcpy (buf, rem->act, lnt + 1); /* copy with ; */ buf[lnt] = 0; /* erase ; */ rem->act += lnt + 1; /* adv ptr */ } else { strlcpy (buf, rem->act, size); /* copy action */ rem->act += strlen (rem->act); /* adv ptr to end */ sim_rem_clract (line); } return buf; } /* Parse and setup Remote Console REPEAT command: REPEAT EVERY nnn USECS Command {; command...} */ static t_stat sim_rem_repeat_cmd_setup (int32 line, CONST char **iptr) { char gbuf[CBUFSIZE]; int32 val; t_bool all_stop = FALSE; t_stat stat = SCPE_OK; CONST char *cptr = *iptr; REMOTE *rem = &sim_rem_consoles[line]; sim_debug (DBG_REP, &sim_remote_console, "Repeat Setup: %s\n", cptr); if (*cptr == 0) /* required argument? */ stat = SCPE_2FARG; else { cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (MATCH_CMD (gbuf, "EVERY") == 0) { cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ val = (int32) get_uint (gbuf, 10, INT_MAX, &stat); if ((stat != SCPE_OK) || (val <= 0)) /* error? */ stat = SCPE_ARG; else { cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if ((MATCH_CMD (gbuf, "USECS") != 0) || (*cptr == 0)) stat = SCPE_ARG; else rem->repeat_interval = val; } } else { if (MATCH_CMD (gbuf, "STOP") == 0) { if (*cptr) { /* more command arguments? */ cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if ((MATCH_CMD (gbuf, "ALL") != 0) || /* */ (*cptr != 0) || /* */ (line != 0)) /* master line? */ stat = SCPE_ARG; else all_stop = TRUE; } else rem->repeat_interval = 0; } else stat = SCPE_ARG; } } if (stat == SCPE_OK) { if (all_stop) { for (line = 0; line < sim_rem_con_tmxr.lines; line++) { rem = &sim_rem_consoles[line]; free (rem->repeat_action); rem->repeat_action = NULL; sim_cancel (rem->uptr); rem->repeat_pending = FALSE; sim_rem_clract (line); } } else { if (rem->repeat_interval != 0) { rem->repeat_action = (char *)realloc (rem->repeat_action, 1 + strlen (cptr)); strcpy (rem->repeat_action, cptr); cptr += strlen (cptr); stat = sim_activate_after (rem->uptr, rem->repeat_interval); } else { free (rem->repeat_action); rem->repeat_action = NULL; sim_cancel (rem->uptr); } rem->repeat_pending = FALSE; sim_rem_clract (line); } } *iptr = cptr; return stat; } /* Parse and setup Remote Console REPEAT command: COLLECT nnn SAMPLES EVERY nnn CYCLES reg{,reg...} */ static t_stat sim_rem_collect_cmd_setup (int32 line, CONST char **iptr) { char gbuf[CBUFSIZE]; int32 samples, cycles, dither_pct; t_bool all_stop = FALSE; t_stat stat = SCPE_OK; CONST char *cptr = *iptr; REMOTE *rem = &sim_rem_consoles[line]; sim_debug (DBG_SAM, &sim_remote_console, "Collect Setup: %s\n", cptr); if (*cptr == 0) /* required argument? */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ samples = (int32) get_uint (gbuf, 10, INT_MAX, &stat); if ((stat != SCPE_OK) || (samples <= 0)) { /* error? */ if (MATCH_CMD (gbuf, "STOP") == 0) { stat = SCPE_OK; if (*cptr) { /* more command arguments? */ cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if ((MATCH_CMD (gbuf, "ALL") != 0) || /* */ (*cptr != 0) || /* */ (line != 0)) /* master line? */ stat = SCPE_ARG; else all_stop = TRUE; } if (stat == SCPE_OK) { for (line = all_stop ? 0 : rem->line; line < (all_stop ? sim_rem_con_tmxr.lines : (rem->line + 1)); line++) { uint32 i, j; rem = &sim_rem_consoles[line]; for (i = 0; i< rem->smp_reg_count; i++) { for (j = 0; j < rem->smp_regs[i].width; j++) free (rem->smp_regs[i].bits[j].vals); free (rem->smp_regs[i].bits); } free (rem->smp_regs); rem->smp_regs = NULL; rem->smp_reg_count = 0; sim_cancel (&rem_con_smp_smpl_units[rem->line]); rem->smp_sample_interval = 0; } } } else stat = sim_messagef (SCPE_ARG, "Expected value or STOP found: %s\n", gbuf); } else { const char *tptr; int32 event_time = rem->smp_sample_interval; cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (MATCH_CMD (gbuf, "SAMPLES") != 0) { *iptr = cptr; return sim_messagef (SCPE_ARG, "Expected SAMPLES found: %s\n", gbuf); } cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (MATCH_CMD (gbuf, "EVERY") != 0) { *iptr = cptr; return sim_messagef (SCPE_ARG, "Expected EVERY found: %s\n", gbuf); } cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ cycles = (int32) get_uint (gbuf, 10, INT_MAX, &stat); if ((stat != SCPE_OK) || (cycles <= 0)) { /* error? */ *iptr = cptr; return sim_messagef (SCPE_ARG, "Expected value found: %s\n", gbuf); } cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if ((MATCH_CMD (gbuf, "CYCLES") != 0) || (*cptr == 0)) { *iptr = cptr; return sim_messagef (SCPE_ARG, "Expected CYCLES found: %s\n", gbuf); } cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if ((MATCH_CMD (gbuf, "DITHER") != 0) || (*cptr == 0)) { *iptr = cptr; return sim_messagef (SCPE_ARG, "Expected DITHER found: %s\n", gbuf); } cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ dither_pct = (int32) get_uint (gbuf, 10, INT_MAX, &stat); if ((stat != SCPE_OK) || /* error? */ (dither_pct < 0) || (dither_pct > 25)) { *iptr = cptr; return sim_messagef (SCPE_ARG, "Expected value found: %s\n", gbuf); } cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if ((MATCH_CMD (gbuf, "PERCENT") != 0) || (*cptr == 0)) { *iptr = cptr; return sim_messagef (SCPE_ARG, "Expected PERCENT found: %s\n", gbuf); } tptr = strcpy (gbuf, "STOP"); /* Start from a clean slate */ sim_rem_collect_cmd_setup (rem->line, &tptr); rem->smp_sample_interval = cycles; rem->smp_reg_count = 0; while (cptr && *cptr) { const char *comma = strchr (cptr, ','); char tbuf[2*CBUFSIZE]; uint32 bit, width; REG *reg; uint32 idx; int32 saved_switches = sim_switches; t_bool indirect = FALSE; BITSAMPLE_REG *smp_regs; if (comma) { strncpy (tbuf, cptr, comma - cptr); tbuf[comma - cptr] = '\0'; cptr = comma + 1; } else { strcpy (tbuf, cptr); cptr += strlen (cptr); } tptr = tbuf; if (strchr (tbuf, ' ')) { sim_switches = 0; tptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, tbuf, &stat); /* get switches and device */ indirect = ((sim_switches & SWMASK('I')) != 0); sim_switches = saved_switches; } if (stat != SCPE_OK) break; tptr = get_glyph (tptr, gbuf, 0); /* get next glyph */ reg = find_reg (gbuf, &tptr, sim_dfdev); if (reg == NULL) { stat = sim_messagef (SCPE_NXREG, "Nonexistent Register: %s\n", gbuf); break; } if (*tptr == '[') { /* subscript? */ const char *tgptr = ++tptr; if (reg->depth <= 1) { /* array register? */ stat = sim_messagef (SCPE_SUB, "Not Array Register: %s\n", reg->name); break; } idx = (uint32) strtotv (tgptr, &tptr, 10); /* convert index */ if ((tgptr == tptr) || (*tptr++ != ']')) { stat = sim_messagef (SCPE_SUB, "Missing or Invalid Register Subscript: %s[%s\n", reg->name, tgptr); break; } if (idx >= reg->depth) { /* validate subscript */ stat = sim_messagef (SCPE_SUB, "Invalid Register Subscript: %s[%d]\n", reg->name, idx); break; } } else idx = 0; /* not array */ smp_regs = (BITSAMPLE_REG *)realloc (rem->smp_regs, (rem->smp_reg_count + 1) * sizeof(*smp_regs)); if (smp_regs == NULL) { stat = SCPE_MEM; break; } rem->smp_regs = smp_regs; smp_regs[rem->smp_reg_count].reg = reg; smp_regs[rem->smp_reg_count].idx = idx; smp_regs[rem->smp_reg_count].dptr = sim_dfdev; smp_regs[rem->smp_reg_count].uptr = sim_dfunit; smp_regs[rem->smp_reg_count].indirect = indirect; width = indirect ? sim_dfdev->dwidth : reg->width; smp_regs[rem->smp_reg_count].width = width; smp_regs[rem->smp_reg_count].bits = (BITSAMPLE *)calloc (width, sizeof (*smp_regs[rem->smp_reg_count - 1].bits)); if (smp_regs[rem->smp_reg_count].bits == NULL) { stat = SCPE_MEM; break; } rem->smp_reg_count += 1; for (bit = 0; bit < width; bit++) { smp_regs[rem->smp_reg_count - 1].bits[bit].depth = samples; smp_regs[rem->smp_reg_count - 1].bits[bit].vals = (int *)calloc (samples, sizeof (int)); if (smp_regs[rem->smp_reg_count - 1].bits[bit].vals == NULL) { stat = SCPE_MEM; break; } } if (stat != SCPE_OK) break; } if (stat != SCPE_OK) { /* Error? */ *iptr = cptr; cptr = strcpy (gbuf, "STOP"); sim_rem_collect_cmd_setup (line, &cptr);/* Cleanup mess */ return stat; } if (rem->smp_sample_dither_pct) event_time = (((rand() % (2 * rem->smp_sample_dither_pct)) - rem->smp_sample_dither_pct) * event_time) / 100; sim_activate (&rem_con_smp_smpl_units[rem->line], event_time); } *iptr = cptr; return stat; } t_stat sim_rem_con_repeat_svc (UNIT *uptr) { int line = uptr - rem_con_repeat_units; REMOTE *rem = &sim_rem_consoles[line]; sim_debug (DBG_REP, &sim_remote_console, "sim_rem_con_repeat_svc(line=%d) - interval=%d usecs\n", line, rem->repeat_interval); if (rem->repeat_interval) { rem->repeat_pending = TRUE; sim_activate_after (uptr, rem->repeat_interval); /* reschedule */ sim_activate_abs (rem_con_data_unit, -1); /* wake up to process */ } return SCPE_OK; } static void sim_rem_record_reg_bit (BITSAMPLE *bit, int val) { bit->tot -= bit->vals[bit->ptr]; /* remove retired value */ bit->tot += val; /* accumulate new value */ bit->vals[bit->ptr] = val; /* save new value */ ++bit->ptr; /* increment next pointer */ if (bit->ptr >= bit->depth) /* if too big */ bit->ptr = 0; /* wrap around */ } static void sim_rem_set_reg_bit (BITSAMPLE *bit, int val) { int i; bit->tot = bit->depth * val; /* compute total */ for (i = 0; i < bit->depth; i++) /* set all value bits */ bit->vals[i] = val; } static void sim_rem_collect_reg_bits (BITSAMPLE_REG *reg) { uint32 i; t_value val = get_rval (reg->reg, reg->idx); if (reg->indirect) val = get_aval ((t_addr)val, reg->dptr, reg->uptr); val = val >> reg->reg->offset; for (i = 0; i < reg->width; i++) { if (sim_is_running) sim_rem_record_reg_bit (®->bits[i], val&1); else sim_rem_set_reg_bit (®->bits[i], val&1); val = val >> 1; } } static void sim_rem_collect_registers (REMOTE *rem) { uint32 i; for (i = 0; i < rem->smp_reg_count; i++) sim_rem_collect_reg_bits (&rem->smp_regs[i]); } static void sim_rem_collect_all_registers (void) { int32 line; for (line = 0; line < sim_rem_con_tmxr.lines; line++) sim_rem_collect_registers (&sim_rem_consoles[line]); } t_stat sim_rem_con_smp_collect_svc (UNIT *uptr) { int line = uptr - rem_con_smp_smpl_units; REMOTE *rem = &sim_rem_consoles[line]; sim_debug (DBG_SAM, &sim_remote_console, "sim_rem_con_smp_collect_svc(line=%d) - interval=%d, dither=%d%%\n", line, rem->smp_sample_interval, rem->smp_sample_dither_pct); if (rem->smp_sample_interval && (rem->smp_reg_count != 0)) { int32 event_time = rem->smp_sample_interval; if (rem->smp_sample_dither_pct) event_time = (((rand() % (2 * rem->smp_sample_dither_pct)) - rem->smp_sample_dither_pct) * event_time) / 100; sim_rem_collect_registers (rem); sim_activate (uptr, event_time); /* reschedule */ } return SCPE_OK; } /* 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++) { REMOTE *rem = &sim_rem_consoles[i]; t_bool master_session = (sim_rem_master_mode && (i == 0)); lp = rem->lp; if (!lp->conn) { if (rem->repeat_interval) { /* was repeated enabled? */ cptr = strcpy (gbuf, "STOP"); sim_rem_repeat_cmd_setup (i, &cptr); /* make sure it is now disabled */ } if (rem->smp_reg_count) { /* were bit samples being collected? */ cptr = strcpy (gbuf, "STOP"); sim_rem_collect_cmd_setup (i, &cptr); /* make sure it is now disabled */ } 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 && !rem->single_mode)) { sim_debug (DBG_MOD, &sim_remote_console, "Session: %d %s %s\n", i, was_active_command ? "Was Active" : "", (master_session && !rem->single_mode) ? "master_session && !single_mode" : ""); 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_rem_collect_all_registers (); sim_stop_timer_services (); if (rem->act == NULL) { 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 */ } } } } else { if (((!rem->repeat_pending) && (rem->act == NULL)) || /* Repeat isn't pending AND no prior commands still active */ (rem->buf_ptr != 0) || /* OR Not at beginning of line */ (tmxr_input_pending_ln (lp))) { /* OR input available to read */ c = tmxr_getc_ln (lp); if (!(TMXR_VALID & c)) continue; c = c & ~TMXR_VALID; if (rem->single_mode) { if (c == sim_int_char) { /* ^E (the interrupt character) must start continue mode console interaction */ rem->single_mode = FALSE; /* enter multi command mode */ sim_is_running = 0; sim_rem_collect_all_registers (); 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 && rem->read_timeout) { tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", rem->read_timeout); tmxr_linemsgf (lp, "\r\n"); tmxr_send_buffered_data (lp); /* flush any buffered data */ } } else { if ((rem->buf_ptr == 0) && /* At beginning of input line */ ((c == '\n') || /* Ignore bare LF between commands (Microsoft Telnet bug) */ (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 (rem->buf_ptr == 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 ((rem->act == NULL) && (!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 ((!rem->single_mode) && (rem->act == NULL)) { 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 (rem->buf_ptr == 0) { if (sim_rem_getact (i, rem->buf, rem->buf_size)) { if (!master_session) tmxr_linemsgf (lp, "%s%s\n", sim_prompt, rem->buf); else tmxr_linemsgf (lp, "%s%s\n", "SIM> ", rem->buf); rem->buf_ptr = strlen (rem->buf); got_command = TRUE; break; } if ((rem->repeat_pending) && /* New repeat pending */ (rem->act == NULL) && /* AND no prior still active */ (!tmxr_input_pending_ln (lp))) { /* AND no session input pending */ rem->repeat_pending = FALSE; sim_rem_setact (rem-sim_rem_consoles, rem->repeat_action); sim_rem_getact (rem-sim_rem_consoles, rem->buf, rem->buf_size); if (!master_session) tmxr_linemsgf (lp, "%s%s\n", sim_prompt, rem->buf); else tmxr_linemsgf (lp, "%s%s\n", "SIM> ", rem->buf); rem->buf_ptr = strlen (rem->buf); got_command = TRUE; break; } } if (!rem->single_mode) { c = tmxr_getc_ln (lp); if (!(TMXR_VALID & c)) { tmxr_send_buffered_data (lp); /* flush any buffered data */ if (!master_session && rem->read_timeout && ((sim_os_msec() - read_start_time)/1000 >= rem->read_timeout)) { while (rem->buf_ptr > 0) { /* Erase current input line */ tmxr_linemsg (lp, "\b \b"); --rem->buf_ptr; } if (rem->buf_ptr+80 >= rem->buf_size) { rem->buf_size += 1024; rem->buf = (char *)realloc (rem->buf, rem->buf_size); } strcpy (rem->buf, "CONTINUE ! Automatic continue due to timeout"); tmxr_linemsgf (lp, "%s\n", rem->buf); got_command = TRUE; break; } sim_os_ms_sleep (50); tmxr_poll_rx (&sim_rem_con_tmxr); /* poll input */ if (!lp->conn) { /* if connection lost? */ rem->single_mode = 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 (rem->buf_ptr > 0) { tmxr_linemsg (lp, "\b \b"); --rem->buf_ptr; } break; case 27: /* escape */ case 21: /* ^U */ while (rem->buf_ptr > 0) { tmxr_linemsg (lp, "\b \b"); --rem->buf_ptr; } break; case '\n': if (rem->buf_ptr == 0) break; case '\r': tmxr_linemsg (lp, "\r\n"); if (rem->buf_ptr+1 >= rem->buf_size) { rem->buf_size += 1024; rem->buf = (char *)realloc (rem->buf, rem->buf_size); } rem->buf[rem->buf_ptr++] = '\0'; sim_debug (DBG_RCV, &sim_remote_console, "Got Command (%d bytes still in buffer): %s\n", tmxr_input_pending_ln (lp), rem->buf); got_command = TRUE; break; case '\004': /* EOF (^D) */ case '\032': /* EOF (^Z) */ while (rem->buf_ptr > 0) { /* Erase current input line */ tmxr_linemsg (lp, "\b \b"); --rem->buf_ptr; } if (!rem->single_mode) { if (rem->buf_ptr+80 >= rem->buf_size) { rem->buf_size += 1024; rem->buf = (char *)realloc (rem->buf, rem->buf_size); } strcpy (rem->buf, "CONTINUE ! Automatic continue before close"); tmxr_linemsgf (lp, "%s\n", rem->buf); got_command = TRUE; } close_session = TRUE; break; default: tmxr_putc_ln (lp, c); if (rem->buf_ptr+2 >= rem->buf_size) { rem->buf_size += 1024; rem->buf = (char *)realloc (rem->buf, rem->buf_size); } rem->buf[rem->buf_ptr++] = (char)c; rem->buf[rem->buf_ptr] = '\0'; if (((size_t)rem->buf_ptr) >= sizeof(cbuf)) got_command = TRUE; /* command too long */ break; } c = 0; if ((!got_command) && /* No Command yet */ (rem->single_mode) && /* AND single command mode */ (tmxr_input_pending_ln (lp)) && /* AND something ready to read */ (rem->act == NULL)) { /* AND no prior still active */ c = tmxr_getc_ln (lp); c = c & ~TMXR_VALID; } } while ((!got_command) && ((!rem->single_mode) || c)); if ((rem->act == NULL) && (!tmxr_input_pending_ln (lp))) tmxr_send_buffered_data (lp); /* flush any buffered data */ if ((rem->single_mode) && !got_command) { break; } if (!sim_rem_master_mode) sim_printf ("Remote Console Command from %s> %s\r\n", lp->ipad, rem->buf); got_command = FALSE; if (strlen(rem->buf) >= 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, rem->buf); rem->buf_ptr = 0; rem->buf[rem->buf_ptr] = '\0'; while (isspace(cbuf[0])) memmove (cbuf, cbuf+1, strlen(cbuf+1)+1); /* skip leading whitespace */ if (cbuf[0] == '\0') { if (rem->single_mode) { rem->single_mode = 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) basecmdp = find_ctab (remote_only_cmds, 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 (rem->single_mode ? allowed_single_remote_cmds : (master_session ? allowed_master_remote_cmds : allowed_remote_cmds), gbuf))) {/* lookup command */ sim_debug (DBG_CMD, &sim_remote_console, "gbuf='%s', basecmd='%s', cmd='%s'\n", gbuf, basecmdp->name, cmdp->name); if (cmdp->action == &x_continue_cmd) { sim_debug (DBG_CMD, &sim_remote_console, "continue_cmd executing\n"); stat = SCPE_OK; } else { if (cmdp->action == &exit_cmd) return SCPE_EXIT; if (cmdp->action == &x_step_cmd) { sim_debug (DBG_CMD, &sim_remote_console, "step_cmd executing\n"); 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_debug (DBG_CMD, &sim_remote_console, "run_cmd executing\n"); if (sim_con_stable_registers && /* can we process command now? */ sim_rem_master_mode) sim_oline = lp; /* specify output socket */ 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 { if (cmdp->action == &x_sampleout_cmd) { sim_debug (DBG_CMD, &sim_remote_console, "sampleout_cmd executing\n"); sim_oline = lp; /* specify output socket */ stat = sim_rem_sample_output (NULL, i); } else { if (cmdp->action == &x_repeat_cmd) { sim_debug (DBG_CMD, &sim_remote_console, "repeat_cmd executing\n"); stat = sim_rem_repeat_cmd_setup (i, &cptr); } else { if (cmdp->action == &x_execute_cmd) { sim_debug (DBG_CMD, &sim_remote_console, "execute_cmd executing\n"); if (rem->act) stat = SCPE_IERR; else { sim_rem_setact (rem-sim_rem_consoles, cptr); stat = SCPE_OK; } } else { if (cmdp->action == &x_collect_cmd) { sim_debug (DBG_CMD, &sim_remote_console, "collect_cmd executing\n"); stat = sim_rem_collect_cmd_setup (i, &cptr); } else { if (sim_con_stable_registers && sim_rem_master_mode) { /* can we process command now? */ sim_debug (DBG_CMD, &sim_remote_console, "Processing Command directly\n"); sim_oline = lp; /* specify output socket */ sim_remote_process_command (); stat = SCPE_OK; /* any message has already been emitted */ } else { sim_debug (DBG_CMD, &sim_remote_console, "Processing Command via SCPE_REMOTE\n"); 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) { rem->single_mode = 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; (void)remove (sim_rem_con_temp_name); sim_log_temp = FALSE; } else { fflush (sim_log); sim_rem_cmd_log_start = sim_ftell (sim_log); } if (!rem->single_mode) { 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)) rem->single_mode = TRUE; else { if (!rem->single_mode) { 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); rem->single_mode = FALSE; } } if (sim_rem_master_was_connected && /* Master mode ever connected? */ !sim_rem_con_tmxr.ldsc[0].sock) /* Master Connection lost? */ return sim_messagef (SCPE_EXIT, "Master Session Disconnect");/* 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; sim_debug (DBG_REP, &sim_remote_console, "sim_rem_con_reset(lines=%d)\n", sim_rem_con_tmxr.lines); for (i=0; i<sim_rem_con_tmxr.lines; i++) { REMOTE *rem = &sim_rem_consoles[i]; if (!sim_rem_con_tmxr.ldsc[i].conn) continue; sim_debug (DBG_REP, &sim_remote_console, "sim_rem_con_reset(line=%d, usecs=%d)\n", i, rem->repeat_interval); if (rem->repeat_interval) sim_activate_after (&rem_con_repeat_units[rem->line], rem->repeat_interval); /* schedule */ if (rem->smp_reg_count) sim_activate (&rem_con_smp_smpl_units[rem->line], rem->smp_sample_interval); /* schedule */ } if (i != sim_rem_con_tmxr.lines) sim_activate_after (rem_con_data_unit, 100000); /* continue polling for open sessions */ return sim_rem_con_poll_svc (rem_con_poll_unit); /* establish polling for new sessions */ } 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, rem_con_poll_unit, cptr);/* open master socket */ if (r == SCPE_OK) sim_activate_after(rem_con_poll_unit, 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, rem_con_poll_unit); for (i=0; i<sim_rem_con_tmxr.lines; i++) { REMOTE *rem = &sim_rem_consoles[i]; free (rem->buf); rem->buf = NULL; rem->buf_size = 0; rem->buf_ptr = 0; rem->single_mode = TRUE; } } } return SCPE_OK; } static t_stat sim_set_rem_connections (int32 flag, CONST char *cptr) { int32 lines; REMOTE *rem; 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_ALATT; if (sim_rem_con_tmxr.lines) { sim_cancel (rem_con_poll_unit); sim_cancel (rem_con_data_unit); } for (i=0; i<sim_rem_con_tmxr.lines; i++) { rem = &sim_rem_consoles[i]; free (rem->buf); free (rem->act_buf); free (rem->act); free (rem->repeat_action); sim_cancel (&rem_con_repeat_units[i]); sim_cancel (&rem_con_smp_smpl_units[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_remote_console.units = (UNIT *)realloc (sim_remote_console.units, sizeof(*sim_remote_console.units)*((2 * lines) + REM_CON_BASE_UNITS)); memset (sim_remote_console.units, 0, sizeof(*sim_remote_console.units)*((2 * lines) + REM_CON_BASE_UNITS)); sim_remote_console.numunits = (2 * lines) + REM_CON_BASE_UNITS; rem_con_poll_unit->action = &sim_rem_con_poll_svc;/* remote console connection polling unit */ rem_con_poll_unit->flags |= UNIT_IDLE; rem_con_data_unit->action = &sim_rem_con_data_svc;/* console data handling unit */ rem_con_data_unit->flags |= UNIT_IDLE|UNIT_DIS; sim_rem_consoles = (REMOTE *)realloc (sim_rem_consoles, sizeof(*sim_rem_consoles)*lines); memset (sim_rem_consoles, 0, sizeof(*sim_rem_consoles)*lines); sim_rem_command_buf = (char *)realloc (sim_rem_command_buf, 4*CBUFSIZE+1); memset (sim_rem_command_buf, 0, 4*CBUFSIZE+1); for (i=0; i<lines; i++) { rem_con_repeat_units[i].flags = UNIT_DIS; rem_con_repeat_units[i].action = &sim_rem_con_repeat_svc; rem_con_smp_smpl_units[i].flags = UNIT_DIS; rem_con_smp_smpl_units[i].action = &sim_rem_con_smp_collect_svc; rem = &sim_rem_consoles[i]; rem->line = i; rem->lp = &sim_rem_con_tmxr.ldsc[i]; rem->uptr = &rem_con_repeat_units[i]; } 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_consoles[sim_rem_active_number].read_timeout = 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; sim_last_cmd_stat = SCPE_OK; while (sim_rem_master_mode) { char *brk_action; sim_rem_consoles[0].single_mode = FALSE; sim_cancel (rem_con_data_unit); sim_activate (rem_con_data_unit, -1); stat = run_cmd (RU_GO, ""); if (stat != SCPE_TTMO) { stat_nomessage = stat & SCPE_NOMESSAGE; /* extract possible message supression flag */ stat = _sim_rem_message ("RUN", stat); } brk_action = sim_brk_replace_act (NULL); sim_debug (DBG_MOD, &sim_remote_console, "Master Session Returned: Status - %d Active_Line: %d, Mode: %s, Active Cmd: %s%s%s\n", stat, sim_rem_cmd_active_line, sim_rem_consoles[0].single_mode ? "Single" : "^E Stopped", sim_rem_active_command ? sim_rem_active_command->name : "", brk_action ? " Break Action: " : "", brk_action ? brk_action : ""); if (stat == SCPE_EXIT) sim_rem_master_mode = FALSE; if (brk_action) { free (sim_rem_consoles[0].act); sim_rem_consoles[0].act = brk_action; } sim_rem_cmd_active_line = 0; /* Make it look like */ sim_rem_consoles[0].single_mode = FALSE; if (stat != SCPE_STEP) sim_rem_active_command = &allowed_single_remote_cmds[0];/* Dummy */ sim_last_cmd_stat = SCPE_BARE_STATUS(stat); /* make exit status available to remote console */ } sim_rem_master_was_enabled = FALSE; sim_rem_master_was_connected = FALSE; if (sim_log_temp) { /* If we setup a temporary log, clean it now */ int32 save_quiet = sim_quiet; sim_quiet = 1; sim_set_logoff (0, NULL); sim_quiet = save_quiet; (void)remove (sim_rem_con_temp_name); sim_log_temp = FALSE; } stat |= stat_nomessage; } else { sim_rem_consoles[0].single_mode = 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) fprintf (stdout, "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) fprintf (stdout, "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; 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; sim_deb_switches = sim_switches; /* save debug switches */ 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->debflags)) && (dptr->dctrl)) { fprintf (st, "Device: %-6s ", dptr->name); show_dev_debug (st, dptr, NULL, 0, NULL); } } for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) { if (!(dptr->flags & DEV_DIS) && ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) && (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) { fprintf (st, "Console Expect processing:\n"); 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 (rem_con_poll_unit); 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) { fprintf (st, "Console Send processing:\n"); return sim_show_send_input (st, &sim_con_send); } /* Poll for character */ t_stat sim_poll_kbd (void) { t_stat c; if (sim_send_poll_data (&sim_con_send, &c)) /* injected input characters available? */ return c; if (!sim_rem_master_mode) { if ((sim_con_ldsc.rxbps) && /* rate limiting && */ (sim_gtime () < sim_con_ldsc.rxnexttime)) /* too soon? */ return SCPE_OK; /* not yet */ if (sim_ttisatty ()) c = sim_os_poll_kbd (); /* get character */ else c = SCPE_OK; if (c == SCPE_STOP) { /* ^E */ stop_cpu = 1; /* Force a stop (which is picked up by sim_process_event */ return SCPE_OK; } if ((sim_con_tmxr.master == 0) && /* not Telnet? */ (sim_con_ldsc.serport == 0)) { /* and not serial? */ if (c && sim_con_ldsc.rxbps) /* got something && rate limiting? */ 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) { t_stat r1 = tmxr_shutdown (); t_stat r2 = sim_os_ttclose (); if (r1 != SCPE_OK) return r1; return r2; } t_bool sim_ttisatty (void) { static int answer = -1; if (answer == -1) answer = sim_os_ttisatty (); return (t_bool)answer; } /* 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 ((sim_ttisatty ()) && (std_input) && /* If Not Background process? */ (std_input != INVALID_HANDLE_VALUE)) { if (!GetConsoleMode(std_input, &saved_input_mode)) return sim_messagef (SCPE_TTYERR, "GetConsoleMode() error: 0x%X\n", (unsigned int)GetLastError ()); if ((!SetConsoleMode(std_input, ENABLE_VIRTUAL_TERMINAL_INPUT)) && (!SetConsoleMode(std_input, RAW_MODE))) return sim_messagef (SCPE_TTYERR, "SetConsoleMode() error: 0x%X\n", (unsigned int)GetLastError ()); } if ((std_output) && /* If Not Background process? */ (std_output != INVALID_HANDLE_VALUE)) { if (GetConsoleMode(std_output, &saved_output_mode)) if (!SetConsoleMode(std_output, ENABLE_VIRTUAL_TERMINAL_PROCESSING|ENABLE_PROCESSED_OUTPUT)) SetConsoleMode(std_output, ENABLE_PROCESSED_OUTPUT); } if (sim_log) { fflush (sim_log); _setmode (_fileno (sim_log), _O_BINARY); } sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL); return SCPE_OK; } static t_stat sim_os_ttcmd (void) { if (sim_log) { fflush (sim_log); _setmode (_fileno (sim_log), _O_TEXT); } sim_os_set_thread_priority (PRIORITY_NORMAL); if ((sim_ttisatty ()) && (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); strlcat(title, sim_name, sizeof(title)); strlcat(title, " Simulator", sizeof(title)); title[0] = strlen(title) - 1; /* Pascal string done */ for (i = 0; i <= title[0]; i++) { /* copy to unsigned char */ ptitle[i] = title[i]; } SIOUXSetTitle(ptitle); return SCPE_OK; } 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_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_switches = EXP_TYP_PERSIST; sim_set_expect (&sim_con_expect, mbuf); 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); /* decode string */ sim_send_input (&sim_con_send, rbuf, strlen((char *)rbuf), 0, 0); /* queue it for output */ free (rbuf); } return SCPE_OK; } /* Set console delay */ static t_stat sim_set_delay (int32 flag, CONST char *cptr) { int32 val; t_stat r; if (cptr == NULL || *cptr == 0) /* no argument string? */ return SCPE_2FARG; /* need an argument */ val = (int32) get_uint (cptr, 10, INT_MAX, &r); /* parse the argument */ if (r == SCPE_OK) { /* parse OK? */ char gbuf[CBUFSIZE]; snprintf (gbuf, sizeof (gbuf), "HALTAFTER=%d", val); expect_cmd (1, gbuf); } return r; } |
Added src/SIMH/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 141 | /* 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_set_stable_registers_state (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/SIMH/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 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 | /* sim_defs.h: 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. 25-Sep-16 RMS Removed KBD_WAIT and friends 08-Mar-16 RMS Added shutdown invisible switch 24-Dec-14 JDB Added T_ADDR_FMT 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 "sim_rev.h" #include <stddef.h> #include <stdlib.h> #include <stdio.h> #if defined(_MSC_VER) && (_MSC_VER < 1900) #define snprintf _snprintf /* poor man's snprintf which will work most of the time but has different return value */ #endif #if defined(__VAX) extern int sim_vax_snprintf(char *buf, size_t buf_size, const char *fmt, ...); #define snprintf sim_vax_snprintf #endif #include <stdarg.h> #include <string.h> #include <errno.h> #include <limits.h> #include <ctype.h> #ifdef _WIN32 #include <winsock2.h> #undef PACKED /* avoid macro name collision */ #undef ERROR /* avoid macro name collision */ #undef MEM_MAPPED /* avoid macro name collision */ #include <process.h> #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 */ /* Handle the special/unusual cases first with everything else leveraging stdints.h */ #if defined (VMS) #include <ints.h> #elif defined(_MSC_VER) && (_MSC_VER < 1600) typedef __int8 int8; typedef __int16 int16; typedef __int32 int32; typedef unsigned __int8 uint8; typedef unsigned __int16 uint16; typedef unsigned __int32 uint32; #else /* All modern/standard compiler environments */ /* any other environment needa a special case above */ #include <stdint.h> typedef int8_t int8; typedef int16_t int16; typedef int32_t int32; typedef uint8_t uint8; typedef uint16_t uint16; typedef uint32_t uint32; #endif /* end standard integers */ typedef int t_stat; /* status */ typedef int t_bool; /* boolean */ /* 64b integers */ #if defined (__GNUC__) /* GCC */ typedef signed long long t_int64; 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 */ #define T_VALUE_MAX 0xffffffffffffffffuLL #else /* 32b data */ typedef int32 t_svalue; typedef uint32 t_value; #define T_VALUE_MAX 0xffffffffUL #endif /* end 64b data */ #if defined (USE_INT64) && defined (USE_ADDR64) /* 64b address */ typedef t_uint64 t_addr; #define T_ADDR_W 64 #define T_ADDR_FMT LL_FMT #else /* 32b address */ 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" #define LL_TYPE long long #else #if defined (__VAX) /* No 64 bit ints on VAX */ #define LL_FMT "l" #define LL_TYPE long #else #define LL_FMT "ll" #define LL_TYPE long long #endif #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_AMBREG (SCPE_BASE + 46) /* ambiguous register */ #define SCPE_REMOTE (SCPE_BASE + 47) /* 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 SERIAL_IN_WAIT 100 /* serial in time */ #define SERIAL_OUT_WAIT 100 /* serial output */ #define NOQUEUE_WAIT 1000000 /* min check time */ /* 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)) || 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 */ uint16 us9; /* device specific */ uint16 us10; /* device specific */ void *tmxr; /* TMXR linkage */ t_bool (*cancel)(UNIT *); double usecs_remaining; /* time balance for long delays */ char *uname; /* Unit name */ #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 qptr; /* circ q ptr */ size_t str_size; /* structure size */ /* NOTE: Flags MUST always be last since it is initialized outside of macro definitions */ uint32 flags; /* flags */ }; /* Register flags */ #define REG_FMT 00003 /* see PV_x */ #define REG_RO 00004 /* read only */ #define REG_HIDDEN 00010 /* hidden */ #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 */ uint32 after; /* delay before halting */ int32 switches; /* flags */ #define EXP_TYP_PERSIST (SWMASK ('P')) /* rule persists after match, default is once a rule matches, it is removed */ #define EXP_TYP_CLEARALL (SWMASK ('C')) /* clear all rules after matching this rule, default is to once a rule matches, it is removed */ #define EXP_TYP_REGEX (SWMASK ('R')) /* rule pattern is a regular expression */ #define EXP_TYP_REGEX_I (SWMASK ('I')) /* regular expression pattern matching should be case independent */ #define EXP_TYP_TIME (SWMASK ('T')) /* halt delay is in microseconds instead of instructions */ #if defined(USE_REGEX) regex_t regex; /* compiled regular expression */ #endif char *act; /* action string */ }; /* Expect Context */ struct EXPECT { DEVICE *dptr; /* Device (for Debug) */ uint32 dbit; /* Debugging Bit */ EXPTAB *rules; /* match rules */ int32 size; /* count of match rules */ uint8 *buf; /* buffer of output data which has produced */ uint32 buf_ins; /* buffer insertion point for the next output data */ uint32 buf_size; /* buffer size */ uint32 buf_data; /* count of data in buffer */ }; /* Send Context */ struct SEND { uint32 delay; /* instruction delay between sent data */ #define SEND_DEFAULT_DELAY 1000 /* default delay instruction count */ 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 /* Internal use ONLY (see below) Generic Register declaration for all fields */ #define _REGDATANF(nm,loc,rdx,wd,off,dep,desc,flds,qptr,siz) \ nm, (loc), (rdx), (wd), (off), (dep), (desc), (flds), (qptr), (siz) #if defined (__STDC__) || defined (_WIN32) /* Variants which depend on how macro arguments are convered to strings */ /* Generic Register declaration for all fields. If the register structure is extended, this macro will be retained and a new internal macro will be provided that populates the new register structure */ #define REGDATA(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \ _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,flds,qptr,siz),(fl) #define REGDATAC(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \ _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,flds,qptr,siz),(fl) /* Right Justified Octal Register Data */ #define ORDATA(nm,loc,wd) \ _REGDATANF(#nm,&(loc),8,wd,0,1,NULL,NULL,0,0) #define ORDATAD(nm,loc,wd,desc) \ _REGDATANF(#nm,&(loc),8,wd,0,1,desc,NULL,0,0) #define ORDATADF(nm,loc,wd,desc,flds) \ _REGDATANF(#nm,&(loc),8,wd,0,1,desc,flds,0,0) /* Right Justified Decimal Register Data */ #define DRDATA(nm,loc,wd) \ _REGDATANF(#nm,&(loc),10,wd,0,1,NULL,NULL,0,0) #define DRDATAD(nm,loc,wd,desc) \ _REGDATANF(#nm,&(loc),10,wd,0,1,desc,NULL,0,0) #define DRDATADF(nm,loc,wd,desc,flds) \ _REGDATANF(#nm,&(loc),10,wd,0,1,desc,flds,0,0) /* Right Justified Hexadecimal Register Data */ #define HRDATA(nm,loc,wd) \ _REGDATANF(#nm,&(loc),16,wd,0,1,NULL,NULL,0,0) #define HRDATAD(nm,loc,wd,desc) \ _REGDATANF(#nm,&(loc),16,wd,0,1,desc,NULL,0,0) #define HRDATADF(nm,loc,wd,desc,flds) \ _REGDATANF(#nm,&(loc),16,wd,0,1,desc,flds,0,0) /* Right Justified Binary Register Data */ #define BINRDATA(nm,loc,wd) \ _REGDATANF(#nm,&(loc),2,wd,0,1,NULL,NULL,0,0) #define BINRDATAD(nm,loc,wd,desc) \ _REGDATANF(#nm,&(loc),2,wd,0,1,desc,NULL,0,0) #define BINRDATADF(nm,loc,wd,desc,flds) \ _REGDATANF(#nm,&(loc),2,wd,0,1,desc,flds,0,0) /* One-bit binary flag at an arbitrary offset in a 32-bit word Register */ #define FLDATA(nm,loc,pos) \ _REGDATANF(#nm,&(loc),2,1,pos,1,NULL,NULL,0,0) #define FLDATAD(nm,loc,pos,desc) \ _REGDATANF(#nm,&(loc),2,1,pos,1,desc,NULL,0,0) #define FLDATADF(nm,loc,pos,desc,flds) \ _REGDATANF(#nm,&(loc),2,1,pos,1,desc,flds,0,0) /* Arbitrary location and Radix Register */ #define GRDATA(nm,loc,rdx,wd,pos) \ _REGDATANF(#nm,&(loc),rdx,wd,pos,1,NULL,NULL,0,0) #define GRDATAD(nm,loc,rdx,wd,pos,desc) \ _REGDATANF(#nm,&(loc),rdx,wd,pos,1,desc,NULL,0,0) #define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) \ _REGDATANF(#nm,&(loc),rdx,wd,pos,1,desc,flds,0,0) /* Arrayed register whose data is kept in a standard C array Register */ #define BRDATA(nm,loc,rdx,wd,dep) \ _REGDATANF(#nm,loc,rdx,wd,0,dep,NULL,NULL,0,0) #define BRDATAD(nm,loc,rdx,wd,dep,desc) \ _REGDATANF(#nm,loc,rdx,wd,0,dep,desc,NULL,0,0) #define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) \ _REGDATANF(#nm,loc,rdx,wd,0,dep,desc,flds,0,0) /* Arrayed register whose data is part of the UNIT structure */ #define URDATA(nm,loc,rdx,wd,off,dep,fl) \ _REGDATANF(#nm,&(loc),rdx,wd,off,dep,NULL,NULL,0,0),((fl) | REG_UNIT) #define URDATAD(nm,loc,rdx,wd,off,dep,fl,desc) \ _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,NULL,0,0),((fl) | REG_UNIT) #define URDATADF(nm,loc,rdx,wd,off,dep,fl,desc,flds) \ _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,flds,0,0),((fl) | REG_UNIT) /* Arrayed register whose data is part of an arbitrary structure */ #define STRDATA(nm,loc,rdx,wd,off,dep,siz,fl) \ _REGDATANF(#nm,&(loc),rdx,wd,off,dep,NULL,NULL,0,siz),((fl) | REG_STRUCT) #define STRDATAD(nm,loc,rdx,wd,off,dep,siz,fl,desc) \ _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,NULL,0,siz),((fl) | REG_STRUCT) #define STRDATADF(nm,loc,rdx,wd,off,dep,siz,fl,desc,flds) \ _REGDATANF(#nm,&(loc),rdx,wd,off,dep,desc,flds,0,siz),((fl) | REG_STRUCT) #define BIT(nm) {#nm, 0xffffffff, 1} /* Single Bit definition */ #define BITNC {"", 0xffffffff, 1} /* Don't care Bit definition */ #define BITF(nm,sz) {#nm, 0xffffffff, sz} /* Bit Field definition */ #define BITNCF(sz) {"", 0xffffffff, sz} /* Don't care Bit Field definition */ #define BITFFMT(nm,sz,fmt) {#nm, 0xffffffff, sz, NULL, #fmt}/* Bit Field definition with Output format */ #define BITFNAM(nm,sz,names) {#nm, 0xffffffff, sz, names} /* Bit Field definition with value->name map */ #else /* For non-STD-C compiler which can't stringify macro arguments with # */ /* Generic Register declaration for all fields. If the register structure is extended, this macro will be retained and a new macro will be provided that populates the new register structure */ #define REGDATA(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \ _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,flds,qptr,siz),(fl) #define REGDATAC(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \ _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,flds,qptr,siz),(fl) /* Right Justified Octal Register Data */ #define ORDATA(nm,loc,wd) \ _REGDATANF("nm",&(loc),8,wd,0,1,NULL,NULL,0,0) #define ORDATAD(nm,loc,wd,desc) \ _REGDATANF("nm",&(loc),8,wd,0,1,desc,NULL,0,0) #define ORDATADF(nm,loc,wd,desc,flds) \ _REGDATANF("nm",&(loc),8,wd,0,1,desc,flds,0,0) /* Right Justified Decimal Register Data */ #define DRDATA(nm,loc,wd) \ _REGDATANF("nm",&(loc),10,wd,0,1,NULL,NULL,0,0) #define DRDATAD(nm,loc,wd,desc) \ _REGDATANF("nm",&(loc),10,wd,0,1,desc,NULL,0,0) #define DRDATADF(nm,loc,wd,desc,flds) \ _REGDATANF("nm",&(loc),10,wd,0,1,desc,flds,0,0) /* Right Justified Hexadecimal Register Data */ #define HRDATA(nm,loc,wd) \ _REGDATANF("nm",&(loc),16,wd,0,1,NULL,NULL,0,0) #define HRDATAD(nm,loc,wd,desc) \ _REGDATANF("nm",&(loc),16,wd,0,1,desc,NULL,0,0) #define HRDATADF(nm,loc,wd,desc,flds) \ _REGDATANF("nm",&(loc),16,wd,0,1,desc,flds,0,0) /* Right Justified Binary Register Data */ #define BINRDATA(nm,loc,wd) \ _REGDATANF("nm",&(loc),2,wd,0,1,NULL,NULL,0,0) #define BINRDATAD(nm,loc,wd,desc) \ _REGDATANF("nm",&(loc),2,wd,0,1,desc,NULL,0,0) #define BINRDATADF(nm,loc,wd,desc,flds) \ _REGDATANF("nm",&(loc),2,wd,0,1,desc,flds,0,0) /* One-bit binary flag at an arbitrary offset in a 32-bit word Register */ #define FLDATA(nm,loc,pos) \ _REGDATANF("nm",&(loc),2,1,pos,1,NULL,NULL,0,0) #define FLDATAD(nm,loc,pos,desc) \ _REGDATANF("nm",&(loc),2,1,pos,1,desc,NULL,0,0) #define FLDATADF(nm,loc,pos,desc,flds) \ _REGDATANF("nm",&(loc),2,1,pos,1,desc,flds,0,0) /* Arbitrary location and Radix Register */ #define GRDATA(nm,loc,rdx,wd,pos) \ _REGDATANF("nm",&(loc),rdx,wd,pos,1,NULL,NULL,0,0) #define GRDATAD(nm,loc,rdx,wd,pos,desc) \ _REGDATANF("nm",&(loc),rdx,wd,pos,1,desc,NULL,0,0) #define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) \ _REGDATANF("nm",&(loc),rdx,wd,pos,1,desc,flds,0,0) /* Arrayed register whose data is kept in a standard C array Register */ #define BRDATA(nm,loc,rdx,wd,dep) \ _REGDATANF("nm",loc,rdx,wd,0,dep,NULL,NULL,0,0) #define BRDATAD(nm,loc,rdx,wd,dep,desc) \ _REGDATANF("nm",loc,rdx,wd,0,dep,desc,NULL,0,0) #define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) \ _REGDATANF("nm",loc,rdx,wd,0,dep,desc,flds,0,0) /* Arrayed register whose data is part of the UNIT structure */ #define URDATA(nm,loc,rdx,wd,off,dep,fl) \ _REGDATANF("nm",&(loc),rdx,wd,off,dep,NULL,NULL,0,0),((fl) | REG_UNIT) #define URDATAD(nm,loc,rdx,wd,off,dep,fl,desc) \ _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,NULL,0,0),((fl) | REG_UNIT) #define URDATADF(nm,loc,rdx,wd,off,dep,fl,desc,flds) \ _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,flds,0,0),((fl) | REG_UNIT) /* Arrayed register whose data is part of an arbitrary structure */ #define STRDATA(nm,loc,rdx,wd,off,dep,siz,fl) \ _REGDATANF("nm",&(loc),rdx,wd,off,dep,NULL,NULL,0,siz),((fl) | REG_STRUCT) #define STRDATAD(nm,loc,rdx,wd,off,dep,siz,fl,desc) \ _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,NULL,0,siz),((fl) | REG_STRUCT) #define STRDATADF(nm,loc,rdx,wd,off,dep,siz,fl,desc,flds) \ _REGDATANF("nm",&(loc),rdx,wd,off,dep,desc,flds,0,siz),((fl) | REG_STRUCT) #define BIT(nm) {"nm", 0xffffffff, 1} /* Single Bit definition */ #define BITNC {"", 0xffffffff, 1} /* Don't care Bit definition */ #define BITF(nm,sz) {"nm", 0xffffffff, sz} /* Bit Field definition */ #define BITNCF(sz) {"", 0xffffffff, sz} /* Don't care Bit Field definition */ #define BITFFMT(nm,sz,fmt) {"nm", 0xffffffff, sz, NULL, "fmt"}/* Bit Field definition with Output format */ #define BITFNAM(nm,sz,names) {"nm", 0xffffffff, sz, names} /* Bit Field definition with value->name map */ #endif #define ENDBITS {NULL} /* end of bitfield list */ /* 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" #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(newval, oldval) (UNIT *)(InterlockedCompareExchangePointer((void * volatile *)&sim_asynch_queue, (void *)newval, oldval)) #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" #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(newval, oldval) ((sim_asynch_queue = newval),oldval) #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/SIMH/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 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 | /* 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 char *HostPathToVhdPath (const char *szHostPath, char *szVhdPath, size_t VhdPathSize); static char *VhdPathToHostPath (const char *szVhdPath, char *szHostPath, size_t HostPathSize); static t_offset get_filesystem_size (UNIT *uptr); 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) { t_offset physical_size, filesystem_size; t_bool saved_quiet = sim_quiet; switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* SIMH format */ physical_size = sim_fsize_ex (uptr->fileref); break; case DKUF_F_VHD: /* VHD format */ physical_size = sim_vhd_disk_size (uptr->fileref); break; case DKUF_F_RAW: /* Raw Physical Disk Access */ physical_size = sim_os_disk_size_raw (uptr->fileref); break; default: return (t_offset)-1; } sim_quiet = TRUE; filesystem_size = get_filesystem_size (uptr); sim_quiet = saved_quiet; if ((filesystem_size == (t_offset)-1) || (filesystem_size < physical_size)) return physical_size; return filesystem_size; } /* 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 _ODS1_HomeBlock { uint16 hm1_w_ibmapsize; uint32 hm1_l_ibmaplbn; uint16 hm1_w_maxfiles; uint16 hm1_w_cluster; uint16 hm1_w_devtype; uint16 hm1_w_structlev; #define HM1_C_LEVEL1 0401 #define HM1_C_LEVEL2 0402 uint8 hm1_t_volname[12]; uint8 hm1_b_fill_1[4]; uint16 hm1_w_volowner; uint16 hm1_w_protect; uint16 hm1_w_volchar; uint16 hm1_w_fileprot; uint8 hm1_b_fill_2[6]; uint8 hm1_b_window; uint8 hm1_b_extend; uint8 hm1_b_lru_lim; uint8 hm1_b_fill_3[11]; uint16 hm1_w_checksum1; uint8 hm1_t_credate[14]; uint8 hm1_b_fill_4[382]; uint32 hm1_l_serialnum; uint8 hm1_b_fill_5[12]; uint8 hm1_t_volname2[12]; uint8 hm1_t_ownername[12]; uint8 hm1_t_format[12]; uint8 hm1_t_fill_6[2]; uint16 hm1_w_checksum2; } ODS1_HomeBlock; 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 _ODS1_FileHeader { uint8 fh1_b_idoffset; uint8 fh1_b_mpoffset; uint16 fh1_w_fid_num; uint16 fh1_w_fid_seq; uint16 fh1_w_struclev; uint16 fh1_w_fileowner; uint16 fh1_w_fileprot; uint16 fh1_w_filechar; uint16 fh1_w_recattr; uint8 fh1_b_fill_1[494]; uint16 fh1_w_checksum; } ODS1_FileHeader; 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 _ODS1_Retreval { uint8 fm1_b_ex_segnum; uint8 fm1_b_ex_rvn; uint16 fm1_w_ex_filnum; uint16 fm1_w_ex_filseq; uint8 fm1_b_countsize; uint8 fm1_b_lbnsize; uint8 fm1_b_inuse; uint8 fm1_b_avail; union { struct { uint8 fm1_b_highlbn; uint8 fm1_b_count; uint16 fm1_w_lowlbn; } fm1_s_fm1def1; struct { uint8 fm1_b_highlbn; uint8 fm1_b_count; uint16 fm1_w_lowlbn; } fm1_s_fm1def2; } fm1_pointers[4]; } ODS1_Retreval; typedef struct _ODS1_StorageControlBlock { uint8 scb_b_unused[3]; uint8 scb_b_bitmapblks; struct _bitmapblk { uint16 scb_w_freeblks; uint16 scb_w_freeptr; } scb_r_blocks[1]; } ODS1_SCB; 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 ODSChecksum (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_ods2_filesystem_size (UNIT *uptr) { DEVICE *dptr; t_addr saved_capac; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; 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 = 0; 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, 512 / ctx->sector_size, (uint8 *)&Home, NULL, sizeof (Home) / ctx->sector_size)) goto Return_Cleanup; CheckSum1 = ODSChecksum (&Home, (uint16)((((char *)&Home.hm2_w_checksum1)-((char *)&Home.hm2_l_homelbn))/2)); CheckSum2 = ODSChecksum (&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) * (512 / ctx->sector_size), (uint8 *)&Header, NULL, sizeof (Header) / ctx->sector_size)) goto Return_Cleanup; CheckSum1 = ODSChecksum (&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 * (512 / ctx->sector_size), (uint8 *)&Scb, NULL, sizeof (Scb) / ctx->sector_size)) goto Return_Cleanup; CheckSum1 = ODSChecksum (&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 ("Sectors In Volume: %u\n", Scb.scb_l_volsize); } ret_val = ((t_offset)Scb.scb_l_volsize) * 512; Return_Cleanup: uptr->capac = saved_capac; return ret_val; } static t_offset get_ods1_filesystem_size (UNIT *uptr) { DEVICE *dptr; t_addr saved_capac; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; t_offset temp_capac = 512 * (t_offset)0xFFFFFFFFu; /* Make sure we can access the largest sector */ uint32 capac_factor; ODS1_HomeBlock Home; ODS1_FileHeader Header; ODS1_Retreval *Retr; uint8 scb_buf[512]; ODS1_SCB *Scb = (ODS1_SCB *)scb_buf; 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, 512 / ctx->sector_size, (uint8 *)&Home, NULL, sizeof (Home) / ctx->sector_size)) goto Return_Cleanup; CheckSum1 = ODSChecksum (&Home, (uint16)((((char *)&Home.hm1_w_checksum1)-((char *)&Home.hm1_w_ibmapsize))/2)); CheckSum2 = ODSChecksum (&Home, (uint16)((((char *)&Home.hm1_w_checksum2)-((char *)&Home.hm1_w_ibmapsize))/2)); if ((Home.hm1_w_ibmapsize == 0) || (Home.hm1_l_ibmaplbn == 0) || (Home.hm1_w_maxfiles == 0) || (Home.hm1_w_cluster != 1) || ((Home.hm1_w_structlev != HM1_C_LEVEL1) && (Home.hm1_w_structlev != HM1_C_LEVEL2)) || (Home.hm1_l_ibmaplbn == 0) || (Home.hm1_w_checksum1 != CheckSum1) || (Home.hm1_w_checksum2 != CheckSum2)) goto Return_Cleanup; if (sim_disk_rdsect (uptr, (((Home.hm1_l_ibmaplbn << 16) + ((Home.hm1_l_ibmaplbn >> 16) & 0xFFFF)) + Home.hm1_w_ibmapsize + 1) * (512 / ctx->sector_size), (uint8 *)&Header, NULL, sizeof (Header) / ctx->sector_size)) goto Return_Cleanup; CheckSum1 = ODSChecksum (&Header, 255); if (CheckSum1 != *(((uint16 *)&Header)+255)) /* Verify Checksum on BITMAP.SYS file header */ goto Return_Cleanup; Retr = (ODS1_Retreval *)(((uint16*)(&Header))+Header.fh1_b_mpoffset); ScbLbn = (Retr->fm1_pointers[0].fm1_s_fm1def1.fm1_b_highlbn<<16)+Retr->fm1_pointers[0].fm1_s_fm1def1.fm1_w_lowlbn; if (sim_disk_rdsect (uptr, ScbLbn * (512 / ctx->sector_size), (uint8 *)Scb, NULL, 512 / ctx->sector_size)) goto Return_Cleanup; if (Scb->scb_b_bitmapblks < 127) ret_val = (((t_offset)Scb->scb_r_blocks[Scb->scb_b_bitmapblks].scb_w_freeblks << 16) + Scb->scb_r_blocks[Scb->scb_b_bitmapblks].scb_w_freeptr) * 512; else ret_val = (((t_offset)Scb->scb_r_blocks[0].scb_w_freeblks << 16) + Scb->scb_r_blocks[0].scb_w_freeptr) * 512; if (!sim_quiet) { sim_printf ("%s%d: '%s' Contains an ODS1 File system\n", sim_dname (dptr), (int)(uptr-dptr->units), uptr->filename); sim_printf ("%s%d: Volume Name: %12.12s ", sim_dname (dptr), (int)(uptr-dptr->units), Home.hm1_t_volname); sim_printf ("Format: %12.12s ", Home.hm1_t_format); sim_printf ("Sectors In Volume: %u\n", (uint32)(ret_val / 512)); } Return_Cleanup: uptr->capac = saved_capac; return ret_val; } typedef struct ultrix_disklabel { uint32 pt_magic; /* magic no. indicating part. info exits */ uint32 pt_valid; /* set by driver if pt is current */ struct pt_info { uint32 pi_nblocks; /* no. of sectors */ uint32 pi_blkoff; /* block offset for start */ } pt_part[8]; } ultrix_disklabel; #define PT_MAGIC 0x032957 /* Partition magic number */ #define PT_VALID 1 /* Indicates if struct is valid */ static t_offset get_ultrix_filesystem_size (UNIT *uptr) { DEVICE *dptr; t_addr saved_capac; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; t_offset temp_capac = 512 * (t_offset)0xFFFFFFFFu; /* Make sure we can access the largest sector */ uint32 capac_factor; uint8 sector_buf[512]; ultrix_disklabel *Label = (ultrix_disklabel *)(sector_buf + sizeof (sector_buf) - sizeof (ultrix_disklabel)); t_offset ret_val = (t_offset)-1; int i; uint32 max_lbn = 0, max_lbn_partnum = 0; 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, 31 * (512 / ctx->sector_size), sector_buf, NULL, 512 / ctx->sector_size)) goto Return_Cleanup; if ((Label->pt_magic != PT_MAGIC) || (Label->pt_valid != PT_VALID)) goto Return_Cleanup; for (i = 0; i < 8; i++) { uint32 end_lbn = Label->pt_part[i].pi_blkoff + Label->pt_part[i].pi_nblocks; if (end_lbn > max_lbn) { max_lbn = end_lbn; max_lbn_partnum = i; } } if (!sim_quiet) { sim_printf ("%s%d: '%s' Contains Ultrix partitions\n", sim_dname (dptr), (int)(uptr-dptr->units), uptr->filename); sim_printf ("Partition with highest sector: %c, Sectors On Disk: %u\n", 'a' + max_lbn_partnum, max_lbn); } ret_val = ((t_offset)max_lbn) * 512; Return_Cleanup: uptr->capac = saved_capac; return ret_val; } typedef t_offset (*FILESYSTEM_CHECK)(UNIT *uptr); static t_offset get_filesystem_size (UNIT *uptr) { static FILESYSTEM_CHECK checks[] = { &get_ods2_filesystem_size, &get_ods1_filesystem_size, &get_ultrix_filesystem_size, NULL }; t_offset ret_val; int i; for (i = 0; checks[i] != NULL; i++) { ret_val = checks[i] (uptr); if (ret_val != (t_offset)-1) break; } 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); } sim_messagef (SCPE_OK, "%s%d: creating new virtual disk '%s'\n", sim_dname (dptr), (int)(uptr-dptr->units), gbuf); capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */ vhd = sim_vhd_disk_create (gbuf, ((t_offset)uptr->capac)*capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)); if (!vhd) { return sim_messagef (r, "%s%d: can't create virtual disk '%s'\n", sim_dname (dptr), (int)(uptr-dptr->units), gbuf); } else { uint8 *copy_buf = (uint8*) malloc (1024*1024); t_lba lba; t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size); t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1))); t_seccnt sects = sectors_per_buffer; if (!copy_buf) { sim_vhd_disk_close(vhd); (void)remove (gbuf); return SCPE_MEM; } for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) { sim_messagef (SCPE_OK, "%s%d: Copied %dMB. %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors)); sects = sectors_per_buffer; if (lba + sects > total_sectors) sects = total_sectors - lba; r = sim_disk_rdsect (uptr, lba, copy_buf, NULL, sects); if (r == SCPE_OK) { uint32 saved_unit_flags = uptr->flags; FILE *save_unit_fileref = uptr->fileref; sim_disk_set_fmt (uptr, 0, "VHD", NULL); uptr->fileref = vhd; r = sim_disk_wrsect (uptr, lba, copy_buf, NULL, sects); uptr->fileref = save_unit_fileref; uptr->flags = saved_unit_flags; } } if (r == SCPE_OK) sim_messagef (SCPE_OK, "\n%s%d: Copied %dMB. Done.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_offset)lba*sector_size)/1000000)); else sim_messagef (r, "\n%s%d: Error copying: %s.\n", sim_dname (dptr), (int)(uptr-dptr->units), sim_error_text (r)); if ((r == SCPE_OK) && (sim_switches & SWMASK ('V'))) { uint8 *verify_buf = (uint8*) malloc (1024*1024); if (!verify_buf) { sim_vhd_disk_close(vhd); (void)remove (gbuf); free (copy_buf); return SCPE_MEM; } for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) { sim_messagef (SCPE_OK, "%s%d: Verified %dMB. %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors)); sects = sectors_per_buffer; if (lba + sects > total_sectors) sects = total_sectors - lba; r = sim_disk_rdsect (uptr, lba, copy_buf, NULL, sects); if (r == SCPE_OK) { uint32 saved_unit_flags = uptr->flags; FILE *save_unit_fileref = uptr->fileref; 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_messagef (r, "\n%s%d: Verified %dMB. Done.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_offset)lba*sector_size)/1000000)); else { t_lba i; uint32 save_dctrl = dptr->dctrl; FILE *save_sim_deb = sim_deb; for (i = 0; i < (1024*1024/sector_size); ++i) if (0 != memcmp (copy_buf+i*sector_size, verify_buf+i*sector_size, sector_size)) 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 */ sim_messagef (SCPE_OK, "%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units)); } else { /* normal */ uptr->fileref = open_function (cptr, "rb+"); /* open r/w */ if (uptr->fileref == NULL) { /* open fail? */ if ((errno == EROFS) || (errno == EACCES)) { /* read only? */ if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ return _err_return (uptr, SCPE_NORO); /* no error */ uptr->fileref = open_function (cptr, "rb"); /* open rd only */ if (uptr->fileref == NULL) /* open fail? */ return _err_return (uptr, SCPE_OPENERR);/* yes, error */ uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ sim_messagef (SCPE_OK, "%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units)); } else { /* doesn't exist */ if (sim_switches & SWMASK ('E')) /* must exist? */ return _err_return (uptr, SCPE_OPENERR); /* yes, error */ if (create_function) uptr->fileref = create_function (cptr, ((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1));/* create new file */ else uptr->fileref = open_function (cptr, "wb+");/* open new file */ if (uptr->fileref == NULL) /* open fail? */ return _err_return (uptr, SCPE_OPENERR);/* yes, error */ sim_messagef (SCPE_OK, "%s%d: creating new file\n", sim_dname (dptr), (int)(uptr-dptr->units)); created = TRUE; } } /* end if null */ } /* end else */ if (DK_GET_FMT (uptr) == DKUF_F_VHD) { if ((created) && dtype) sim_vhd_disk_set_dtype (uptr->fileref, dtype); 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 (128, 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) { /* Write all blocks */ t_lba lba; t_lba total_lbas = (t_lba)((((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))/ctx->sector_size); for (lba = 0; (r == SCPE_OK) && (lba < total_lbas); lba += 128) { t_seccnt sectors = ((lba + 128) <= total_lbas) ? 128 : total_lbas - lba; r = sim_disk_wrsect (uptr, lba, secbuf, NULL, sectors); } } free (secbuf); if (r != SCPE_OK) { sim_disk_detach (uptr); /* report error now */ (void)remove (cptr); /* remove the created 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 */ (void)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 */ (void)remove (cptr); /* remove the created file */ return SCPE_OPENERR; } sim_messagef (SCPE_OK, "%s%d: Initialized To Sector Address %dMB. %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors)); } sim_messagef (SCPE_OK, "%s%d: Initialized To Sector Address %dMB. 100%% complete.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000)); free (init_buf); } if (pdp11tracksize) sim_disk_pdp11_bad_block (uptr, pdp11tracksize, sector_size/sizeof(uint16)); } if (sim_switches & SWMASK ('K')) { 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; } } } sim_messagef (SCPE_OK, "%s%d: Verified containing Sector Address %dMB. %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors)); } sim_messagef (SCPE_OK, "%s%d: Verified containing Sector Address %dMB. 100%% complete.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000)); free (verify_buf); uptr->dynflags |= UNIT_DISK_CHK; } filesystem_capac = get_filesystem_size (uptr); capac = size_function (uptr->fileref); if (capac && (capac != (t_offset)-1)) { 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 wds = number of words per sector Outputs: sta = status code */ t_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; int32 i; t_addr da; uint16 *buf; DEVICE *dptr; char *namebuf, *c; uint32 packid; t_stat stat = SCPE_OK; 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 (!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; (stat == SCPE_OK) && (i < sec) && (i < 10); i++, da += wds) if (ctx) stat = sim_disk_wrsect (uptr, (t_lba)(da/wds), (uint8 *)buf, NULL, 1); else { if (sim_fseek (uptr->fileref, da, SEEK_SET)) { stat = SCPE_IOERR; break; } if (wds != sim_fwrite (buf, sizeof (uint16), wds, uptr->fileref)) stat = SCPE_IOERR; } free (buf); return stat; } void sim_disk_data_trace(UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason) { DEVICE *dptr = find_dev_from_unit (uptr); if (sim_deb && (dptr->dctrl & reason)) { char pos[32]; sprintf (pos, "lbn: %08X ", (unsigned int)lba); sim_data_trace(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__) && defined(HAVE_NTDDDISK_H) #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); if (pos != (t_offset)-1) (void)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 (uint32)l[3] | ((uint32)l[2]<<8) | ((uint32)l[1]<<16) | ((uint32)l[0]<<24); } static uint64 NtoHll(uint64 value) { uint8 *l = (uint8 *)&value; uint64 highresult = (uint64)l[3] | ((uint64)l[2]<<8) | ((uint64)l[1]<<16) | ((uint64)l[0]<<24); uint32 lowresult = (uint64)l[7] | ((uint64)l[6]<<8) | ((uint64)l[5]<<16) | ((uint64)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; 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; } sim_messagef (SCPE_OK, "Merging %s\ninto %s\n", szVHDPath, hVHD->ParentVHDPath); for (BlockNumber=NeededBlock=0; BlockNumber < NtoHl (hVHD->Dynamic.MaxTableEntries); ++BlockNumber) { uint32 BlockSectors = SectorsPerBlock; if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) continue; ++NeededBlock; BlockOffset = SectorSize*((uint64)(NtoHl (hVHD->BAT[BlockNumber]) + BitMapSectors)); if (((uint64)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; sim_messagef (SCPE_OK, "Merged %dMB. %d%% complete.\r", (int)((((float)NeededBlock)*SectorsPerBlock)*SectorSize/1000000), (int)((((float)NeededBlock)*100)/BlocksToMerge)); hVHD->BAT[BlockNumber] = VHD_BAT_FREE_ENTRY; } if (BlockNumber < NtoHl (hVHD->Dynamic.MaxTableEntries)) { Status = errno; } else { Status = 0; sim_messagef (SCPE_OK, "Merged %dMB. 100%% complete.\n", (int)((((float)NeededBlock)*SectorsPerBlock)*SectorSize/1000000)); fclose (hVHD->File); hVHD->File = NULL; (void)remove (szVHDPath); *ParentVHD = (char*) malloc (strlen (hVHD->ParentVHDPath)+1); strcpy (*ParentVHD, hVHD->ParentVHDPath); } Cleanup_Return: 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) (void)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, "]/"))) memmove (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, "\\.\\")) memmove (c, c+2, strlen(c+2)+1); for (c = strstr (szVhdPath, "\\\\"); c; c = strstr (szVhdPath, "\\\\")) memmove (c, c+1, strlen(c+1)+1); while ((c = strstr (szVhdPath, "\\..\\"))) { *c = '\0'; d = strrchr (szVhdPath, '\\'); if (d) memmove (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)) (void)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/SIMH/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 102 | /* 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_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds); 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/SIMH/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 4013 4014 4015 4016 4017 4018 4019 4020 4021 | /* 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 #define MAX(a,b) (((a) > (b)) ? (a) : (b)) /* 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)-1); cptr = strchr (strmac, '>'); if (cptr) { state.file[sizeof(state.file)-1] = '\0'; strncpy (state.file, cptr + 1, sizeof(state.file)-1); 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)) && (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)) && (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]\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 (MAX (len, crc_len) <= sizeof (item->packet.msg)) { memcpy(item->packet.msg, data, ((len > crc_len) ? len : crc_len)); if (crc_data && (crc_len > len)) memcpy(&item->packet.msg[len], crc_data, ETH_CRC_SIZE); } else { item->packet.oversize = (uint8 *)realloc (item->packet.oversize, ((len > crc_len) ? len : crc_len)); memcpy(item->packet.oversize, data, ((len > crc_len) ? len : crc_len)); 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) { #if defined (USE_READER_THREAD) return "Threaded " #else return "Polled " #endif "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\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(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA"); p_GetSystemDirectory = (UINT(WINAPI *)(LPTSTR, UINT)) GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetSystemDirectoryA"); if (p_SetDllDirectory && p_GetSystemDirectory) { char npcap_path[512] = ""; if (p_GetSystemDirectory (npcap_path, sizeof(npcap_path) - 7)) strlcat (npcap_path, "\\Npcap", sizeof(npcap_path)); if (p_SetDllDirectory(npcap_path)) hLib = LoadLibraryA(lib_name); p_SetDllDirectory (NULL); } if (hLib == NULL) hLib = LoadLibraryA(lib_name); } #else hLib = dlopen(lib_name, RTLD_NOW); #endif if (hLib == 0) { /* failed to load DLL */ sim_printf ("Eth: Failed to load %s\n", lib_name); #ifdef _WIN32 sim_printf ("Eth: You must install Npcap or WinPcap 4.x to use networking\n"); #else sim_printf ("Eth: You must install libpcap to use networking\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, ®hnd)) != ERROR_SUCCESS) return -1; reglen = buf_size; if ((status = RegQueryValueExA (reghnd, "MachineGuid", NULL, ®type, buf, ®len)) != 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) 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\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")) return sim_messagef (SCPE_OPENERR, "Eth: Must specify actual tap device name (i.e. tap:tap0)\n"); #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) char vdeswitch_s[CBUFSIZE]; /* VDE switch name */ char vdeport_s[CBUFSIZE]; /* VDE switch port (optional), numeric */ struct vde_open_args voa; const char *devname = savname + 4; memset(&voa, 0, sizeof(voa)); if (!strcmp(savname, "vde:vdedevice")) return sim_messagef (SCPE_OPENERR, "Eth: Must specify actual vde device name (i.e. vde:/tmp/switch)\n"); while (isspace(*devname)) ++devname; devname = get_glyph_nc (devname, vdeswitch_s, ':'); /* Extract switch name */ devname = get_glyph_nc (devname, vdeport_s, 0); /* Extract optional port number */ if (vdeport_s[0]) { /* port provided? */ t_stat r; voa.port = (int)get_uint (vdeport_s, 10, 255, &r); if (r != SCPE_OK) return sim_messagef (SCPE_OPENERR, "Eth: Invalid vde port number: %s in %s\n", vdeport_s, savname); } if (!(*handle = (void*) vde_open((char *)vdeswitch_s, (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")) return sim_messagef (SCPE_OPENERR, "Eth: Must specify actual udp host and ports(i.e. udp:1224:somehost.com:2234)\n"); 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))) return sim_messagef (SCPE_OPENERR, "Eth: Must specify different udp localhost ports\n"); *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 */ return sim_messagef (SCPE_OPENERR, "Eth: pcap_open_live error - %s\n", errbuf); *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\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\n", errbuf); /* show erroneous BPF string */ sim_printf ("Eth: BPF string is: |%s|\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\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]) return sim_messagef (SCPE_OPENERR, "Eth: open error - %s\n", errbuf); if (r != SCPE_OK) return r; if (!strcmp (desc, "No description available")) strcpy (desc, ""); sim_printf ("Eth: opened OS device %s%s%s\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\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_VDE_NETWORK) fprintf (st, " eth2 vde:device{:switch-port-number} (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\n" "You may need to run as root, or install a libpcap version\n" "which is at least 0.9 from your OS vendor or www.tcpdump.org\n" : "Eth: Error Transmitting packet: %s\n" "You may need to run as root.\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 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) { char errbuf[PCAP_ERRBUF_SIZE]; bpf_u_int32 bpf_subnet, bpf_netmask; if (pcap_lookupnet(dev->name, &bpf_subnet, &bpf_netmask, errbuf)<0) bpf_netmask = 0; /* compile filter string */ if ((status = pcap_compile((pcap_t*)dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { sprintf(errbuf, "%s", pcap_geterr((pcap_t*)dev->handle)); sim_printf("Eth: pcap_compile error: %s\n", errbuf); /* show erroneous BPF string */ sim_printf ("Eth: BPF string is: |%s|\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\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; for (i=0; i<used; ++i) { /* Cull any non-ethernet interface types */ #if defined(HAVE_PCAP_NETWORK) char errbuf[PCAP_ERRBUF_SIZE]; conn = pcap_open_live(list[i].name, ETH_MAX_PACKET, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); if (NULL != conn) datalink = pcap_datalink(conn), pcap_close(conn); list[i].eth_api = ETH_API_PCAP; #endif if ((NULL == conn) || (datalink != DLT_EN10MB)) { for (j=i; j<used-1; ++j) 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, ®hnd)) != 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, ®type, regval, ®len)) != 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{:switch-port-number}"); 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\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\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/SIMH/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 383 384 385 | /* 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) #include <pthread.h> #if defined (USE_SETNONBLOCK) #undef USE_SETNONBLOCK #endif /* USE_SETNONBLOCK */ #undef PCAP_READ_TIMEOUT #define PCAP_READ_TIMEOUT 15 #if (!defined (xBSD) && !defined(_WIN32) && !defined(VMS) && !defined(__CYGWIN__)) || defined (HAVE_TAP_NETWORK) || defined (HAVE_VDE_NETWORK) #define MUST_DO_SELECT 1 #endif #else #include <time.h> #endif /* USE_READER_THREAD */ /* give priority to USE_NETWORK over USE_SHARED */ #if defined(USE_NETWORK) && defined(USE_SHARED) #undef USE_SHARED #endif /* USE_SHARED only works on Windows or if HAVE_DLOPEN */ #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/SIMH/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 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 | /* 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) static const char * GetErrorText(DWORD dwError) { static char szMsgBuffer[2048]; DWORD dwStatus; dwStatus = FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM| FORMAT_MESSAGE_IGNORE_INSERTS, // __in DWORD dwFlags, NULL, // __in_opt LPCVOID lpSource, dwError, // __in DWORD dwMessageId, 0, // __in DWORD dwLanguageId, szMsgBuffer, // __out LPTSTR lpBuffer, sizeof (szMsgBuffer) -1, // __in DWORD nSize, NULL); // __in_opt va_list *Arguments if (0 == dwStatus) snprintf(szMsgBuffer, sizeof(szMsgBuffer) - 1, "Error Code: 0x%lX", dwError); while (sim_isspace (szMsgBuffer[strlen (szMsgBuffer)-1])) szMsgBuffer[strlen (szMsgBuffer) - 1] = '\0'; return szMsgBuffer; } t_stat sim_copyfile (const char *source_file, const char *dest_file, t_bool overwrite_existing) { if (CopyFileA (source_file, dest_file, !overwrite_existing)) return SCPE_OK; return sim_messagef (SCPE_ARG, "Error Copying '%s' to '%s': %s\n", source_file, dest_file, GetErrorText (GetLastError ())); } #include <io.h> int sim_set_fsize (FILE *fptr, t_addr size) { return _chsize(_fileno(fptr), (long)size); } int sim_set_fifo_nonblock (FILE *fptr) { 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> #if HAVE_UTIME #include <utime.h> #endif t_stat sim_copyfile (const char *source_file, const char *dest_file, t_bool overwrite_existing) { FILE *fIn = NULL, *fOut = NULL; t_stat st = SCPE_OK; char *buf = NULL; size_t bytes; fIn = sim_fopen (source_file, "rb"); if (!fIn) { st = sim_messagef (SCPE_ARG, "Can't open '%s' for input: %s\n", source_file, strerror (errno)); goto Cleanup_Return; } fOut = sim_fopen (dest_file, "wb"); if (!fOut) { st = sim_messagef (SCPE_ARG, "Can't open '%s' for output: %s\n", dest_file, strerror (errno)); goto Cleanup_Return; } buf = (char *)malloc (BUFSIZ); while ((bytes = fread (buf, 1, BUFSIZ, fIn))) fwrite (buf, 1, bytes, fOut); Cleanup_Return: free (buf); if (fIn) fclose (fIn); if (fOut) fclose (fOut); #if defined(HAVE_UTIME) if (st == SCPE_OK) { struct stat statb; if (!stat (source_file, &statb)) { struct utimbuf utim; utim.actime = statb.st_atime; utim.modtime = statb.st_mtime; if (utime (dest_file, &utim)) st = SCPE_IOERR; } else st = SCPE_IOERR; } #endif return st; } int sim_set_fifo_nonblock (FILE *fptr) { struct stat stbuf; if (!fptr || fstat (fileno(fptr), &stbuf)) return -1; #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 #if defined(__VAX) /* * We privide a 'basic' snprintf, which 'might' overrun a buffer, but * the actual use cases don't on other platforms and none of the callers * care about the function return value. */ int sim_vax_snprintf(char *buf, size_t buf_size, const char *fmt, ...) { va_list arglist; va_start (arglist, fmt); vsprintf (buf, fmt, arglist); va_end (arglist); return 0; } #endif |
Added src/SIMH/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 84 | /* 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); t_stat sim_copyfile (const char *source_file, const char *dest_file, t_bool overwrite_existing); void sim_buf_swap_data (void *bptr, size_t size, size_t count); void sim_buf_copy_swapped (void *dptr, const void *bptr, size_t size, size_t count); typedef struct SHMEM SHMEM; t_stat sim_shmem_open (const char *name, size_t size, SHMEM **shmem, void **addr); void sim_shmem_close (SHMEM *shmem); extern t_bool sim_taddr_64; /* t_addr is > 32b and Large File Support available */ 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/SIMH/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 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 | /* 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 12 /** 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 merely the simulator name if the simulator binary is located in the current PATH. - The simulator binary must be built from the same version simh source code that the frontpanel API was acquired fron (the API and the simh framework must speak the same language) Note 2: - Configuration file specified should contain device setup statements (enable, disable, CPU types and attach commands). 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 specified by the application when it calls: sim_panel_add_register sim_panel_add_register_bits sim_panel_add_register_array and sim_panel_add_register_indirect sim_panel_add_register_indirect_bits 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 bit_width the number of values to populate in the bits array bits an array of integers which is bit_width long that will receive each bit's current accumulated value. The accumulated value will range from 0 thru the the sample_depth specified when calling sim_panel_set_sampling_parameters(). */ int sim_panel_add_register (PANEL *panel, const char *name, const char *device_name, size_t size, void *addr); int sim_panel_add_register_bits (PANEL *panel, const char *name, const char *device_name, size_t bit_width, int *bits); 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); int sim_panel_add_register_indirect_bits (PANEL *panel, const char *name, const char *device_name, size_t bit_width, int *bits); /** A panel application has a choice of two different methods of getting the values contained in the set of registers it has declared interest in via the sim_panel_add_register APIs. 1) The values can be polled (whenever it is desired) by calling sim_panel_get_registers(). 2) The panel can call sim_panel_set_display_callback_interval() to specify a callback routine and a periodic rate that the callback routine should be called. The panel API will make a best effort to deliver the current register state at the desired rate. 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_interval (PANEL *panel, PANEL_DISPLAY_PCALLBACK callback, void *context, int usecs_between_callbacks); /** When a front panel application wants to get averaged bit sample values, it must first declare the sampling parameters that will be used while collecting the bit values. The dithering percentage must be 25% or less and when non 0 causes the sample frequency to vary by plus or minus a random percentage value up to the specified value. sim_panel_set_sampling_parameters sim_panel_set_sampling_parameters_ex sample_frequency cycles/instructions between sample captures sample_dither_pct percentage of sample_frequency to vary randomly sample_depth how many samples to accumulate in the rolling average for each bit sample. Returned bit sample values will range from 0 thru this value. */ int sim_panel_set_sampling_parameters_ex (PANEL *panel, unsigned int sample_frequency, unsigned int sample_dither_pct, unsigned int sample_depth); int sim_panel_set_sampling_parameters (PANEL *panel, unsigned int sample_frequency, unsigned int sample_depth); /** When a front panel application needs to change the running state of a simulator one of the following routines should be called: sim_panel_exec_halt - Stop instruction execution sim_panel_exec_boot - Boot a simulator from a specific device sim_panel_exec_run - Start/Resume a simulator running instructions sim_panel_exec_start - Start a simulator running instructions after resetting all devices sim_panel_exec_step - Have a simulator execute a single step */ int sim_panel_exec_halt (PANEL *panel); int sim_panel_exec_boot (PANEL *panel, const char *device); int sim_panel_exec_start (PANEL *panel); int sim_panel_exec_run (PANEL *panel); int sim_panel_exec_step (PANEL *panel); /** A simulator often displays some useful information as it stops executing instructions. sim_panel_halt_text - Returns the simulator output immediately prior to the most recent transition to the Halt state. */ const char * sim_panel_halt_text (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_mem_deposit_instruction - Deposit instruction 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_mem_deposit_instruction 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 instruction a pointer to the buffer that contains the mnemonic instruction to be deposited at the indicated address */ int sim_panel_mem_deposit_instruction (PANEL *panel, size_t addr_size, const void *addr, const char *instruction); /** 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); /** A front panel application might want to have access to the instruction execution history that a simulator may be capable of providing. If this functionality is desired, enabling of recording instruction history should be explicitly enabled in the sim_config file that the simulator is started with. */ /** sim_panel_get_history count the number of instructions to return size the size (in local storage) of the buffer which will receive the data returned when examining the simulator buffer a pointer to the buffer which will be loaded with the instruction history returned from the simulator */ int sim_panel_get_history (PANEL *panel, int count, size_t size, char *buffer); /** A front panel application might want some details of simulator and/or device behavior that is provided by a particular simulator via debug information. Debugging for particular device(s) and/or simulator debug settings can be controlled via the sim_panel_device_debug_mode API. */ /** sim_panel_device_debug_mode device the device whose debug mode is to change set_untset 1 to set debug flags, 0 to clear debug flags mode_bits character string with different debug mode bits to enable or disable. An empty string will enable or disable all mode bits for the specified device */ int sim_panel_device_debug_mode (PANEL *panel, const char *device, int set_unset, const char *mode_bits); /** When a front panel application 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 API 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 except to possibly set the panel state to Error if the panel condition is no longer useful. sim_panel_get_error - the details of the most recent error sim_panel_clear_error - clears the error buffer */ const char *sim_panel_get_error (void); void sim_panel_clear_error (void); /** The panek<->simulator wire protocol can be traced if protocol problems arise. sim_panel_set_debug_mode - Specifies the debug detail to be recorded sim_panel_flush_debug - Flushes debug output to disk sim_panel_debug - Write message to the debug file */ #define DBG_XMT 1 /* Transmit Data */ #define DBG_RCV 2 /* Receive Data */ #define DBG_REQ 4 /* Request Data */ #define DBG_RSP 8 /* Response Data */ #define DBG_THR 16 /* Thread Activities */ #define DBG_APP 32 /* Application Activities */ void sim_panel_set_debug_mode (PANEL *panel, int debug_bits); void sim_panel_debug (PANEL *panel, const char *fmt, ...); void sim_panel_flush_debug (PANEL *panel); #endif /* !defined(__VAX) */ #ifdef __cplusplus } #endif #endif /* SIM_FRONTPANEL_H_ */ |
Added src/SIMH/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/SIMH/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 | /* sim_serial.c: OS-dependent serial port routines Copyright (c) 2008, J. David Bryan, Mark Pizzolato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 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 22-Apr-12 MP Adapted from code originally written by J. David Bryan This module provides OS-dependent routines to access serial ports on the host machine. The terminal multiplexer library uses these routines to provide serial connections to simulated terminal interfaces. Currently, the module supports Windows and UNIX. Use on other systems 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)-1); 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)) && (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)) && (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 ((!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 */ } 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$qio SYS$QIO #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/SIMH/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 104 105 | /* sim_serial.h: OS-dependent serial port routines header file Copyright (c) 2008, J. David Bryan, Mark Pizzolato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 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 22-Apr-12 MP Adapted from code originally written by J. David Bryan */ #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/SIMH/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) (void)select ((int) sock + 1, rw_p, NULL, er_p, &zero); else (void)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); (void)getsockname (sock, (struct sockaddr *)&sockname, &socknamesize); (void)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/SIMH/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/SIMH/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 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 | /* 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_errecf erase record forward sim_tape_errecr erase record reverse sim_tape_sprecsf space records forward sim_tape_spfilef space files forward sim_tape_sprecsr space records reverse sim_tape_spfiler space files reverse sim_tape_position generalized position sim_tape_rewind rewind sim_tape_reset reset unit 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); static t_stat tape_erase_fwd (UNIT *uptr, t_mtrlnt gap_size); static t_stat tape_erase_rev (UNIT *uptr, t_mtrlnt gap_size); 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->asynch_io) pthread_mutex_lock (&ctx->io_lock); if (ctx->callback) { ctx->callback = NULL; if (ctx->asynch_io) pthread_mutex_unlock (&ctx->io_lock); callback (uptr, ctx->io_status); } else { if (ctx->asynch_io) pthread_mutex_unlock (&ctx->io_lock); } } static t_bool _tape_is_active (UNIT *uptr) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; if (ctx) { 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) return sim_messagef (SCPE_NOFNC, "Tape: can't operate asynchronously\r\n"); #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) || (dptr->debflags)) ? 0xFFFFFFFF : 0, 0); } t_stat sim_tape_attach_ex (UNIT *uptr, const char *cptr, uint32 dbit, int completion_delay) { struct tape_context *ctx; uint32 objc; DEVICE *dptr; 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; uint32 f; t_stat r; t_bool auto_format = FALSE; if (uptr == NULL) return SCPE_IERR; ctx = (struct tape_context *)uptr->tape_ctx; f = MT_GET_FMT (uptr); if ((ctx == 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.22, X3.39, and X3.54) and the equivalent ECMA standard (ECMA-62) specify a maximum erase gap length of 25 feet (7.6 meters). While gaps of any length may be written, gaps longer than this are non-standard and may indicate that an unrecorded or erased tape is being read. If the tape density has been set via a previous "sim_tape_set_dens" call, then the length is monitored when skipping over erase gaps. If the length reaches 25 feet, motion is terminated, and MTSE_RUNAWAY status is returned. Runaway status is also returned if an end-of-medium marker or the physical end of file is encountered while spacing over a gap; however, MTSE_EOM is returned if the tape is positioned at the EOM or EOF on entry. If the density has not been set, then a gap of any length is skipped, and MTSE_RUNAWAY status is never returned. In effect, erase gaps present in the tape image file will be transparent to the caller. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format. Because gaps may be partially overwritten with data records, gap metadata must be examined marker-by-marker. To reduce the number of file read calls, a buffer of metadata elements is used. The buffer size is initially established at 256 elements but may be set to any size desired. To avoid a large read for the typical case where an erase gap is not present, the first read is of a single metadatum marker. If that is a gap marker, then additional buffered reads are performed. See the notes at "tape_erase_fwd" regarding the erase gap implementation. Implementation notes: 1. For programming convenience, erase gap processing is performed for both SIMH standard and E11 tape formats, although the latter will never contain erase gaps, as the "tape_erase_fwd" call takes no action for the E11 format. 2. The "feof" call cannot return a non-zero value on the first pass through the loop, because the "sim_fseek" call resets the internal end-of-file indicator. Subsequent passes only occur if an erase gap is present, so a non-zero return indicates an EOF was seen while reading through a gap. 3. The "runaway_counter" cannot decrement to zero (or below) in the presence of an error that terminates the gap-search loop. Therefore, the test after the loop exit need not check for error status. 4. The dynamic start/stop test of the HP 3000 magnetic tape diagnostic heavily exercises the erase gap scanning code. Sample test execution times for various buffer sizes on a 2 GHz host platform are: buffer size execution time (elements) (CPU seconds) ----------- -------------- 1 7200 32 783 128 237 256 203 512 186 1024 171 5. Because an erase gap may precede the logical end-of-medium, represented either by the physical end-of-file or by an EOM marker, the "position not updated" flag is set only if the tape is positioned at the EOM when the routine is entered. If at least one gap marker precedes the EOM, then the PNU flag is not set. This ensures that a backspace-and-retry sequence will work correctly in both cases. */ static t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc) { 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 status = MTSE_OK; MT_CLR_PNU (uptr); /* clear the position-not-updated flag */ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */ return MTSE_UNATT; /* then quit with an error */ if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* set the initial tape position; if it fails */ MT_SET_PNU (uptr); /* then set position not updated */ status = sim_tape_ioerr (uptr); /* and quit with I/O error status */ } else switch (f) { /* otherwise the read method depends on the tape format */ case MTUF_F_STD: case MTUF_F_E11: runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set the largest legal gap size in bytes */ if (runaway_counter == 0) { /* if tape density has not been not set */ sizeof_gap = 0; /* then disable runaway detection */ runaway_counter = INT_MAX; /* to allow gaps of any size */ } else /* otherwise */ sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */ bufcntr = 0; /* force an initial read */ bufcap = 0; /* but of just one metadata marker */ do { /* loop until a record, gap, or error is seen */ if (bufcntr == bufcap) { /* if the buffer is empty then refill it */ if (feof (uptr->fileref)) { /* if we hit the EOF while reading a gap */ if (sizeof_gap > 0) /* then if detection is enabled */ status = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ status = MTSE_EOM; /* as the end-of-medium */ break; } else if (bufcap == 0) /* otherwise if this is the initial read */ bufcap = 1; /* then start with just one marker */ else /* otherwise reset the capacity */ bufcap = sizeof (buffer) /* to the full size of the buffer */ / sizeof (buffer [0]); bufcap = sim_fread (buffer, /* fill the buffer */ sizeof (t_mtrlnt), /* with tape metadata */ bufcap, uptr->fileref); if (ferror (uptr->fileref)) { /* if a file I/O error occurred */ if (bufcntr == 0) /* then if this is the initial read */ MT_SET_PNU (uptr); /* then set position not updated */ status = sim_tape_ioerr (uptr); /* report the error and quit */ break; } else if (bufcap == 0 /* otherwise if positioned at the physical EOF */ || buffer [0] == MTR_EOM) /* or at the logical EOM */ if (bufcntr == 0) { /* then if this is the initial read */ MT_SET_PNU (uptr); /* then set position not updated */ status = MTSE_EOM; /* and report the end-of-medium and quit */ break; } else { /* otherwise some gap has already been skipped */ if (sizeof_gap > 0) /* so if detection is enabled */ status = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ status = MTSE_EOM; /* as the end-of-medium */ break; } else /* otherwise reset the index */ bufcntr = 0; /* to the start of the buffer */ } *bc = buffer [bufcntr++]; /* store the metadata marker value */ if (*bc == MTR_EOM) { /* if an end-of-medium marker is seen */ if (sizeof_gap > 0) /* then if detection is enabled */ status = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ status = MTSE_EOM; /* as the end-of-medium */ break; } uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* space over the marker */ if (*bc == MTR_TMK) { /* if the value is a tape mark */ status = MTSE_TMK; /* then quit with tape mark status */ break; } else if (*bc == MTR_GAP) /* otherwise if the value is a full gap */ runaway_counter -= sizeof_gap; /* then decrement the gap counter */ else if (*bc == MTR_FHGAP) { /* otherwise if the value if a half gap */ uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2; /* then back up and resync */ if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* set the tape position; if it fails */ status = sim_tape_ioerr (uptr); /* then quit with I/O error status */ break; } bufcntr = bufcap; /* mark the buffer as invalid to force a read */ *bc = MTR_GAP; /* reset the marker */ runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */ } else { /* otherwise it's a record marker */ if (bufcntr < bufcap /* if the position is within the buffer */ && sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* then seek to the data area; if it fails */ status = sim_tape_ioerr (uptr); /* then quit with I/O error status */ break; } sbc = MTR_L (*bc); /* extract the record length */ uptr->pos = uptr->pos + sizeof (t_mtrlnt) /* position to the start */ + (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */ } } while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */ if (runaway_counter <= 0) /* if a tape runaway occurred */ status = MTSE_RUNAWAY; /* then report it */ break; /* otherwise the operation succeeded */ case MTUF_F_TPC: sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); *bc = tpcbc; /* save rec lnt */ if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); /* pos not upd */ status = sim_tape_ioerr (uptr); } else if (feof (uptr->fileref)) { /* eof? */ MT_SET_PNU (uptr); /* pos not upd */ status = MTSE_EOM; } else { uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */ if (tpcbc == TPC_TMK) /* tape mark? */ status = MTSE_TMK; else uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */ } break; case MTUF_F_P7B: for (sbc = 0, all_eof = 1; ; sbc++) { /* loop thru record */ sim_fread (&c, sizeof (uint8), 1, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); /* pos not upd */ status = sim_tape_ioerr (uptr); break; } else if (feof (uptr->fileref)) { /* eof? */ if (sbc == 0) /* no data? eom */ status = MTSE_EOM; break; /* treat like eor */ } else if ((sbc != 0) && (c & P7B_SOR)) /* next record? */ break; else if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0; } if (status == MTSE_OK) { *bc = sbc; /* save rec lnt */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ uptr->pos = uptr->pos + sbc; /* spc over record */ if (all_eof) /* tape mark? */ status = MTSE_TMK; } break; default: status = MTSE_FMT; } return status; } static t_stat sim_tape_rdrlfwd (UNIT *uptr, t_mtrlnt *bc) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat status; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ status = sim_tape_rdlntf (uptr, bc); /* read the record length */ sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", status, *bc, uptr->pos); return status; } /* Read record length reverse (internal routine). 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. Implementation notes: 1. The "sim_fread" call cannot return 0 in the absence of an error condition. The preceding "sim_tape_bot" test ensures that "pos" >= 4, so "sim_fseek" will back up at least that far, so "sim_fread" will read at least one element. If the call returns zero, an error must have occurred, so the "ferror" call must succeed. 2. See the notes at "sim_tape_rdlntf" and "tape_erase_fwd" regarding tape runaway and the erase gap implementation, respectively. */ static t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc) { 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 status = MTSE_OK; MT_CLR_PNU (uptr); /* clear the position-not-updated flag */ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */ return MTSE_UNATT; /* then quit with an error */ if (sim_tape_bot (uptr)) /* if the unit is positioned at the BOT */ status = MTSE_BOT; /* then reading backward is not possible */ else switch (f) { /* otherwise the read method depends on the tape format */ case MTUF_F_STD: case MTUF_F_E11: runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set the largest legal gap size in bytes */ if (runaway_counter == 0) { /* if tape density has not been not set */ sizeof_gap = 0; /* then disable runaway detection */ runaway_counter = INT_MAX; /* to allow gaps of any size */ } else /* otherwise */ sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */ bufcntr = 0; /* force an initial read */ bufcap = 0; /* but of just one metadata marker */ do { /* loop until a record, gap, or error is seen */ if (bufcntr == 0) { /* if the buffer is empty then refill it */ if (sim_tape_bot (uptr)) { /* if the search has backed into the BOT */ status = MTSE_BOT; /* then quit with an error */ break; } else if (bufcap == 0) /* otherwise if this is the initial read */ bufcap = 1; /* then start with just one marker */ else if (uptr->pos < sizeof (buffer)) /* otherwise if less than a full buffer remains */ bufcap = (uint32) uptr->pos /* then reduce the capacity accordingly */ / sizeof (t_mtrlnt); else /* otherwise reset the capacity */ bufcap = sizeof (buffer) /* to the full size of the buffer */ / sizeof (buffer [0]); if (sim_fseek (uptr->fileref, /* seek back to the location */ uptr->pos - bufcap * sizeof (t_mtrlnt), /* corresponding to the start */ SEEK_SET)) { /* of the buffer; if it fails */ status = sim_tape_ioerr (uptr); /* and fail with I/O error status */ break; } bufcntr = sim_fread (buffer, sizeof (t_mtrlnt), /* fill the buffer */ bufcap, uptr->fileref); /* with tape metadata */ if (ferror (uptr->fileref)) { /* if a file I/O error occurred */ status = sim_tape_ioerr (uptr); /* then report the error and quit */ break; } } *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 */ status = MTSE_TMK; /* then quit with tape mark status */ break; } else if (*bc == MTR_GAP) /* otherwise if the marker is a full gap */ runaway_counter -= sizeof_gap; /* then decrement the gap counter */ else if ((*bc & MTR_M_RHGAP) == MTR_RHGAP /* otherwise if the marker */ || *bc == MTR_RRGAP) { /* is a half gap */ uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* then position forward to resync */ bufcntr = 0; /* mark the buffer as invalid to force a read */ *bc = MTR_GAP; /* reset the marker */ runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */ } else { /* otherwise it's a record marker */ sbc = MTR_L (*bc); /* extract the record length */ uptr->pos = uptr->pos - sizeof (t_mtrlnt) /* position to the start */ - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */ if (sim_fseek (uptr->fileref, /* seek to the start of the data area; if it fails */ uptr->pos + sizeof (t_mtrlnt), /* then return with I/O error status */ SEEK_SET)) { status = sim_tape_ioerr (uptr); break; } } } while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */ if (runaway_counter <= 0) /* if a tape runaway occurred */ status = MTSE_RUNAWAY; /* then report it */ break; /* otherwise the operation succeeded */ case MTUF_F_TPC: ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */ sim_fseek (uptr->fileref, ppos, SEEK_SET); /* position */ sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); *bc = tpcbc; /* save rec lnt */ if (ferror (uptr->fileref)) /* error? */ status = sim_tape_ioerr (uptr); else if (feof (uptr->fileref)) /* eof? */ status = MTSE_EOM; else { uptr->pos = ppos; /* spc over record */ if (*bc == MTR_TMK) /* tape mark? */ status = MTSE_TMK; else 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? */ status = sim_tape_ioerr (uptr); break; } else if (feof (uptr->fileref)) { /* eof? */ status = MTSE_EOM; break; } else { if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0; if (c & P7B_SOR) /* start of record? */ break; } } if (status == MTSE_OK) { uptr->pos = uptr->pos - sbc; /* update position */ *bc = sbc; /* save rec lnt */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ if (all_eof) /* tape mark? */ status = MTSE_TMK; } break; default: status = MTSE_FMT; } return status; } static t_stat sim_tape_rdrlrev (UNIT *uptr, t_mtrlnt *bc) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat status; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ status = sim_tape_rdlntr (uptr, bc); /* read the record length */ sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", status, *bc, uptr->pos); return status; } /* 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 */ st = sim_tape_rdrlfwd (uptr, &tbc); /* read rec lnt */ if (st != MTSE_OK) return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) { /* rec out of range? */ MT_SET_PNU (uptr); uptr->pos = opos; return MTSE_INVRL; } i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ 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); st = sim_tape_rdrlrev (uptr, &tbc); /* read rec lnt */ if (st != MTSE_OK) return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) /* rec out of range? */ return MTSE_INVRL; i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ 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; } /* Erase a gap in the forward direction (internal routine). An erase gap is written in the forward direction on the tape unit specified by "uptr" for the number of bytes specified by "bc". The status of the operation is returned, and the file position is altered as follows: Exit Condition File Position ------------------ ------------------ unit unattached unchanged unsupported format unchanged write protected unchanged read error unchanged, PNU set write error unchanged, PNU set gap written updated If the requested byte count equals the metadatum size, then the routine succeeds only if it can overlay a single metadatum (i.e., a tape mark, an end-of-medium marker, or an existing erase gap marker); otherwise, the file position is not altered, PNU is set, and MTSE_INVRL (invalid record length) status is returned. An erase gap is represented in the tape image file by a special metadata value repeated throughout the gap. The value is chosen so that it is still recognizable even if it has been "cut in half" by a subsequent data overwrite that does not end on a metadatum-sized boundary. In addition, a range of metadata values are reserved for detection in the reverse direction. 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. 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 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 current tape format supports erase gaps, then this routine will write a gap of the requested size. If the format does not, then no action will be taken, and MTSE_OK status will be returned. This allows a device simulator that supports writing erase gaps to use the same code without worrying about the tape format currently selected by the user. A request for an erase gap of zero length also succeeds with no action taken. Implementation notes: 1. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format. */ static t_stat tape_erase_fwd (UNIT *uptr, t_mtrlnt gap_size) { size_t xfer; t_stat st; t_mtrlnt meta, sbc, new_len, rec_size; uint32 file_size, marker_count; int32 gap_needed = (int32) gap_size; /* the gap remaining to be allocated from the tape */ uint32 gap_alloc = 0; /* the gap currently allocated from the tape */ const t_addr gap_pos = uptr->pos; /* the file position where the gap will start */ const uint32 format = MT_GET_FMT (uptr); /* the tape format */ const uint32 meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */ const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* the smallest data record size */ 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 */ else if (gap_size == 0 || format != MTUF_F_STD) /* otherwise if zero length or gaps aren't supported */ return MTSE_OK; /* then take no action */ file_size = sim_fsize (uptr->fileref); /* get the file size */ if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* position the tape; if it fails */ MT_SET_PNU (uptr); /* then set position not updated */ return sim_tape_ioerr (uptr); /* and quit with I/O error status */ } /* Read tape records and allocate them to the gap until the amount required is consumed. Read the next metadatum from tape: - EOF or EOM: allocate remainder of bytes needed. - TMK or GAP: allocate sizeof(metadatum) bytes. - Reverse GAP: allocate sizeof(metadatum) / 2 bytes. - Data record: see below. Loop until the bytes needed = 0. */ do { xfer = sim_fread (&meta, meta_size, 1, uptr->fileref); /* read a metadatum */ if (ferror (uptr->fileref)) { /* read error? */ uptr->pos = gap_pos; /* restore original position */ MT_SET_PNU (uptr); /* position not updated */ return sim_tape_ioerr (uptr); /* translate error */ } else if (xfer != 1 && feof (uptr->fileref) == 0) { /* otherwise if a partial metadatum was read */ uptr->pos = gap_pos; /* then restore the original position */ MT_SET_PNU (uptr); /* set the position-not-updated flag */ return MTSE_INVRL; /* and return an invalid record length error */ } else /* otherwise we had a good read */ uptr->pos = uptr->pos + meta_size; /* so move the tape over the datum */ if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */ gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ gap_needed = 0; } else if ((meta == MTR_GAP) || (meta == MTR_TMK)) { /* gap or tape mark? */ gap_alloc = gap_alloc + meta_size; /* allocate marker space */ gap_needed = gap_needed - meta_size; /* reduce requirement */ } else if (gap_size == meta_size) { /* otherwise if the request is for a single metadatum */ uptr->pos = gap_pos; /* then restore the original position */ MT_SET_PNU (uptr); /* set the position-not-updated flag */ return MTSE_INVRL; /* and return an invalid record length error */ } else if (meta == MTR_FHGAP) { /* half gap? */ uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */ if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */ return sim_tape_ioerr (uptr); /* then quit with I/O error status */ gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */ gap_needed = gap_needed - meta_size / 2; /* reduce requirement */ } else if (uptr->pos + MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */ gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */ gap_needed = 0; /* allocate remainder */ } /* 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 */ if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */ return sim_tape_ioerr (uptr); /* then quit with I/O error status */ gap_alloc = gap_alloc + rec_size; /* allocate record */ gap_needed = gap_needed - rec_size; /* reduce requirement */ } else { /* record size OK */ uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */ new_len = MTR_F (meta) | (sbc - gap_needed); /* truncate to new len */ 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); /* loop until all of the gap has been allocated */ uptr->pos = gap_pos; /* reposition to gap start */ if (gap_alloc & (meta_size - 1)) { /* gap size "odd?" */ st = sim_tape_wrdata (uptr, MTR_FHGAP); /* write half gap marker */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } uptr->pos = uptr->pos - meta_size / 2; /* realign position */ gap_alloc = gap_alloc - 2; /* decrease gap to write */ } marker_count = gap_alloc / meta_size; /* count of gap markers */ do { st = sim_tape_wrdata (uptr, MTR_GAP); /* write gap markers */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } } while (--marker_count > 0); return MTSE_OK; } /* Erase a gap in the reverse direction (internal routine). An erase gap is written in the reverse direction on the tape unit specified by "uptr" for the number of bytes specified by "bc". The status of the operation is returned, and the file position is altered as follows: Exit Condition File Position ------------------ ------------------ unit unattached unchanged unsupported format unchanged write protected unchanged read error unchanged, PNU set write error unchanged, PNU set gap written updated If the requested byte count equals the metadatum size, then the routine succeeds only if it can overlay a single metadatum (i.e., a tape mark or an existing erase gap marker); otherwise, the file position is not altered, and MTSE_INVRL (invalid record length) status is returned. Implementation notes: 1. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format. 2. Erasing a record in the reverse direction currently succeeds only if the gap requested occupies the same space as the record located immediately before the current file position. This limitation may be lifted in a future update. 3. The "sim_fread" call cannot return 0 in the absence of an error condition. The preceding "sim_tape_bot" test ensures that "pos" >= 4, so "sim_fseek" will back up at least that far, so "sim_fread" will read at least one element. If the call returns zero, an error must have occurred, so the "ferror" call must succeed. */ static t_stat tape_erase_rev (UNIT *uptr, t_mtrlnt gap_size) { const uint32 format = MT_GET_FMT (uptr); /* the tape format */ const uint32 meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */ t_stat status; t_mtrlnt rec_size, metadatum; t_addr gap_pos; size_t xfer; MT_CLR_PNU (uptr); /* clear the position-not-updated flag */ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */ return MTSE_UNATT; /* then we cannot proceed */ else if (sim_tape_wrp (uptr)) /* otherwise if the unit is write protected */ return MTSE_WRP; /* then we cannot write */ else if (gap_size == 0 || format != MTUF_F_STD) /* otherwise if the gap length is zero or unsupported */ return MTSE_OK; /* then take no action */ gap_pos = uptr->pos; /* save the starting position */ if (gap_size == meta_size) { /* if the request is for a single metadatum */ if (sim_tape_bot (uptr)) /* then if the unit is positioned at the BOT */ return MTSE_BOT; /* then erasing backward is not possible */ else /* otherwise */ uptr->pos -= meta_size; /* back up the file pointer */ if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */ return sim_tape_ioerr (uptr); /* then quit with I/O error status */ sim_fread (&metadatum, meta_size, 1, uptr->fileref); /* read a metadatum */ if (ferror (uptr->fileref)) /* if a file I/O error occurred */ return sim_tape_ioerr (uptr); /* then report the error and quit */ else if (metadatum == MTR_TMK) /* otherwise if a tape mark is present */ if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* then reposition the tape; if it fails */ return sim_tape_ioerr (uptr); /* then quit with I/O error status */ else { /* otherwise */ metadatum = MTR_GAP; /* replace it with an erase gap marker */ xfer = sim_fwrite (&metadatum, meta_size, /* write the gap marker */ 1, uptr->fileref); if (ferror (uptr->fileref) || xfer == 0) /* if a file I/O error occurred */ return sim_tape_ioerr (uptr); /* report the error and quit */ else /* otherwise the write succeeded */ status = MTSE_OK; /* so return success */ } else if (metadatum == MTR_GAP) /* otherwise if a gap already exists */ status = MTSE_OK; /* then take no additional action */ else { /* otherwise a data record is present */ uptr->pos = gap_pos; /* so restore the starting position */ return MTSE_INVRL; /* and fail with invalid record length status */ } } else { /* otherwise it's an erase record request */ status = sim_tape_rdlntr (uptr, &rec_size); /* so get the length of the preceding record */ if (status == MTSE_OK /* if the read succeeded */ && gap_size == rec_size + 2 * meta_size) { /* and the gap will exactly overlay the record */ gap_pos = uptr->pos; /* then save the gap start position */ status = tape_erase_fwd (uptr, gap_size); /* erase the record */ if (status == MTSE_OK) /* if the gap write succeeded */ uptr->pos = gap_pos; /* the reposition back to the start of the gap */ } else { /* otherwise the read failed or is the wrong size */ uptr->pos = gap_pos; /* so restore the starting position */ if (status != MTSE_OK) /* if the record was not found */ return status; /* then return the failure reason */ else /* otherwise the record is the wrong size */ return MTSE_INVRL; /* so report an invalid record length */ } } return status; /* return the status of the erase operation */ } /* Write an erase gap. An erase gap is written in on the tape unit specified by "uptr" for the length specified by "gap_size" in tenths of an inch, and the status of the operation is returned. The tape density must have been set via a previous sim_tape_set_dens call; if it has not, then no action is taken, and MTSE_IOERR is returned. If the requested gap length is zero, or the tape format currently selected does not support erase gaps, the call succeeds with no action taken. This allows a device simulator that supports writing erase gaps to use the same code without worrying about the tape format currently selected by the user. Because SIMH tape images do not carry physical parameters (e.g., recording density), overwriting a tape image file containing a gap is problematic if the density setting is not the same as that used during recording. There is no way to establish a gap of a certain length unequivocally in an image file, so this implementation establishes a gap of a certain number of bytes that reflect the desired gap length at the tape density in bits per inch used during writing. */ t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; const uint32 density = bpi [MT_DENS (uptr->dynflags)]; /* the tape density in bits per inch */ const uint32 byte_length = (gaplen * density) / 10; /* the size of the requested gap in bytes */ if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen); if (density == 0) /* if the density has not been set */ return MTSE_IOERR; /* then report an I/O error */ else /* otherwise */ return tape_erase_fwd (uptr, byte_length); /* erase the requested gap size in bytes */ } t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_wrgap (uptr, gaplen); AIO_CALL(TOP_RDRR, NULL, NULL, NULL, 0, 0, gaplen, 0, NULL, callback); return r; } /* Erase a record forward. An erase gap is written in the forward direction on the tape unit specified by "uptr" for a length corresponding to a record containing the number of bytes specified by "bc", and the status of the operation is returned. The resulting gap will occupy "bc" bytes plus the size of the record length metadata. This function may be used to erase a record of length "n" in place by requesting a gap of length "n". After erasure, the tape will be positioned at the end of the gap. If a length of 0 is specified, then the metadatum marker at the current tape position will be erased. If the tape is not positioned at a metadatum marker, the routine fails with MTSE_INVRL, and the tape position is unchanged. */ t_stat sim_tape_errecf (UNIT *uptr, t_mtrlnt bc) { const t_mtrlnt meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */ const t_mtrlnt gap_size = bc + 2 * meta_size; /* the requested gap size in bytes */ if (bc == 0) /* if a zero-length erase is requested */ return tape_erase_fwd (uptr, meta_size); /* then erase a metadatum marker */ else /* otherwise */ return tape_erase_fwd (uptr, gap_size); /* erase the requested gap */ } /* Erase a record reverse. An erase gap is written in the reverse direction on the tape unit specified by "uptr" for a length corresponding to a record containing the number of bytes specified by "bc", and the status of the operation is returned. The resulting gap will occupy "bc" bytes plus the size of the record length metadata. This function may be used to erase a record of length "n" in place by requesting a gap of length "n". After erasure, the tape will be positioned at the start of the gap. If a length of 0 is specified, then the metadatum marker preceding the current tape position will be erased. If the tape is not positioned after a metadatum marker, the routine fails with MTSE_INVRL, and the tape position is unchanged. */ t_stat sim_tape_errecr (UNIT *uptr, t_mtrlnt bc) { const t_mtrlnt meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */ const t_mtrlnt gap_size = bc + 2 * meta_size; /* the requested gap size in bytes */ if (bc == 0) /* if a zero-length erase is requested */ return tape_erase_rev (uptr, meta_size); /* then erase a metadatum marker */ else /* otherwise */ return tape_erase_rev (uptr, gap_size); /* erase the requested gap */ } /* Space record forward Inputs: uptr = pointer to tape unit bc = pointer to size of record skipped 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_rdrlfwd (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; 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_rdrlrev (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; 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_rdrlrev (uptr, &rbc); last_tapemark = (MTSE_TMK == st); if ((st == MTSE_OK) || (st == MTSE_TMK)) sim_tape_rdrlfwd (uptr, &rbc); } *skipped = 0; *recsskipped = 0; while (*skipped < count) { /* loopo */ while (1) { st = sim_tape_sprecsf (uptr, 0x1ffffff, &filerecsskipped);/* spc recs */ *recsskipped += filerecsskipped; 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 == NULL) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; 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/SIMH/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 211 212 213 214 215 216 217 | /* 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. 18-Jul-16 JDB Added sim_tape_errecf, sim_tape_errecr functions 15-Dec-14 JDB Added tape density validity flags 04-Nov-14 JDB Added tape density flags 11-Oct-14 JDB Added reverse read half gap, set/show density 22-Sep-14 JDB Added tape runaway support 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_errecf (UNIT *uptr, t_mtrlnt bc); t_stat sim_tape_errecr (UNIT *uptr, t_mtrlnt bc); 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/SIMH/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 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 | /* 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 double sim_timer_stop_time = 0; static uint32 sim_rom_delay = 0; static uint32 sim_throt_ms_start = 0; static uint32 sim_throt_ms_stop = 0; static uint32 sim_throt_type = 0; static uint32 sim_throt_val = 0; static uint32 sim_throt_drift_pct = SIM_THROT_DRIFT_PCT_DFLT; static uint32 sim_throt_state = SIM_THROT_STATE_INIT; static double sim_throt_cps; static double sim_throt_peak_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_stop_unit; /* Stop unit */ UNIT sim_internal_timer_unit; /* Internal calibration timer */ UNIT sim_throttle_unit; /* one for throttle */ t_stat sim_throt_svc (UNIT *uptr); t_stat sim_timer_tick_svc (UNIT *uptr); t_stat sim_timer_stop_svc (UNIT *uptr); #define DBG_IDL TIMER_DBG_IDLE /* idling */ #define DBG_QUE TIMER_DBG_QUEUE /* queue activities */ #define DBG_MUX TIMER_DBG_MUX /* tmxr queue activities */ #define DBG_TRC 0x008 /* tracing */ #define DBG_CAL 0x010 /* calibration activities */ #define DBG_TIM 0x020 /* timer thread activities */ #define DBG_THR 0x040 /* throttle activities */ #define DBG_ACK 0x080 /* interrupt acknowledgement activities */ #define DBG_CHK 0x100 /* check scheduled activation time*/ #define DBG_INT 0x200 /* internal timer activities */ DEBTAB sim_timer_debug[] = { {"TRACE", DBG_TRC, "Trace routine calls"}, {"IDLE", DBG_IDL, "Idling activities"}, {"QUEUE", DBG_QUE, "Event queuing activities"}, {"IACK", DBG_ACK, "interrupt acknowledgement activities"}, {"CALIB", DBG_CAL, "Calibration activities"}, {"TIME", DBG_TIM, "Activation and scheduling activities"}, {"INTER", DBG_INT, "Internal timer activities"}, {"THROT", DBG_THR, "Throttling activities"}, {"MUX", DBG_MUX, "Tmxr scheduling activities"}, {"CHECK", DBG_CHK, "Check scheduled activation time"}, {0} }; /* Forward device declarations */ extern DEVICE sim_timer_dev; extern DEVICE sim_throttle_dev; extern DEVICE sim_stop_dev; void sim_rtcn_init_all (void) { int32 tmr; for (tmr = 0; tmr <= SIM_NTIMERS; tmr++) 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", uptr ? 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 (!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 tmr=%d against internal system tmr=%d, tickper=%d (result: %d)\n", tmr, sim_calb_tmr, ticksper, rtc_currd[tmr]); return rtc_currd[tmr]; } new_rtime = sim_os_msec (); /* wall time */ ++rtc_calibrations[tmr]; /* count calibrations */ sim_debug (DBG_TRC, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d, tmr=%d)\n", ticksper, tmr); if (new_rtime < rtc_rtime[tmr]) { /* time running backwards? */ /* This happens when the value returned by sim_os_msec wraps (as an uint32) */ /* 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_clock_cosched_queue[tmr] = QUEUE_LIST_END; } sim_stop_unit.action = &sim_timer_stop_svc; SIM_INTERNAL_UNIT.flags = UNIT_IDLE; sim_register_internal_device (&sim_timer_dev); /* Register Clock Assist device */ sim_throttle_unit.action = &sim_throt_svc; sim_register_clock_unit_tmr (&SIM_INTERNAL_UNIT, SIM_INTERNAL_CLK); sim_idle_enab = FALSE; /* init idle off */ sim_idle_rate_ms = sim_os_ms_sleep_init (); /* get OS timer rate */ sim_set_rom_delay_factor (sim_get_rom_delay_factor ()); /* initialize ROM delay factor */ 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 cycles/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}, { DRDATAD (THROT_DELAY, sim_idle_stable, 32, "Seconds before throttling starts"), PV_RSPC}, { DRDATAD (THROT_DRIFT_PCT, sim_throt_drift_pct, 32, "Percent of throttle drift before correction"), PV_RSPC}, { 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 stop time */ t_stat sim_timer_set_stop (int32 flag, CONST char *cptr) { t_stat r; t_value stop_time; if (cptr == NULL) return SCPE_ARG; stop_time = get_uint (cptr, 10, T_VALUE_MAX, &r); if (r != SCPE_OK) return r; if (stop_time <= (t_value)sim_gtime()) return SCPE_ARG; sim_register_internal_device (&sim_stop_dev); /* Register Stop Device */ sim_timer_stop_time = (double)stop_time; sim_activate_abs (&sim_stop_unit, (int32)(sim_timer_stop_time - sim_gtime())); return SCPE_OK; } /* Set/Clear asynch */ t_stat sim_timer_set_async (int32 flag, CONST char *cptr) { if (flag) { if (sim_asynch_enabled && (!sim_asynch_timer)) { 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 }, { "STOP", &sim_timer_set_stop, 0 }, { NULL, NULL, 0 } }; MTAB sim_timer_mod[] = { { 0 }, }; static t_stat sim_timer_clock_reset (DEVICE *dptr); static const char *sim_timer_description (DEVICE *dptr) { return "Clock Assist facilities"; } static const char *sim_int_timer_description (DEVICE *dptr) { return "Internal Timer"; } static const char *sim_int_stop_description (DEVICE *dptr) { return "Stop facility"; } static const char *sim_throttle_description (DEVICE *dptr) { return "Throttle facility"; } DEVICE sim_timer_dev = { "INT-CLOCK", sim_timer_units, sim_timer_reg, sim_timer_mod, SIM_NTIMERS+1, 0, 0, 0, 0, 0, NULL, NULL, &sim_timer_clock_reset, NULL, NULL, NULL, NULL, DEV_DEBUG | DEV_NOSAVE, 0, sim_timer_debug}; DEVICE sim_int_timer_dev = { "INT-TIMER", &sim_internal_timer_unit, NULL, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, DEV_NOSAVE}; DEVICE sim_stop_dev = { "INT-STOP", &sim_stop_unit, NULL, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, DEV_NOSAVE, 0, NULL, NULL, NULL, NULL, NULL, NULL, sim_int_stop_description}; DEVICE sim_throttle_dev = { "INT-THROTTLE", &sim_throttle_unit, sim_throttle_reg, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, DEV_NOSAVE}; /* SET CLOCK command */ t_stat sim_set_timers (int32 arg, CONST char *cptr) { char *cvptr, gbuf[CBUFSIZE]; CTAB *ctptr; 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_throt_state = SIM_THROT_STATE_THROTTLE; /* force state */ sim_throt_wait = sim_throt_val; } } sim_register_internal_device (&sim_throttle_dev); /* Register Throttle Device */ if (sim_throt_type == SIM_THROT_SPC) /* Set initial value while correct one is determined */ sim_throt_cps = (int32)((1000.0 * sim_throt_val) / (double)sim_throt_sleep_time); else sim_throt_cps = SIM_INITIAL_IPS; 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: if (sim_throt_wait) { fprintf (st, "Throttle: %d%% of %s cycles per second\n", sim_throt_val, sim_fmt_numeric (sim_throt_peak_cps)); fprintf (st, "Throttling by sleeping for: %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_wait); } else fprintf (st, "Throttle: %d%%\n", sim_throt_val); break; case SIM_THROT_SPC: fprintf (st, "Throttle: %d/%d\n", sim_throt_val, sim_throt_sleep_time); fprintf (st, "Throttling by sleeping for: %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_val); break; default: fprintf (st, "Throttling: Disabled\n"); break; } if (sim_throt_type != SIM_THROT_NONE) { 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) { if (sim_throt_type != SIM_THROT_NONE) { if (sim_throt_state == SIM_THROT_STATE_THROTTLE) { /* Previously calibrated? */ /* Reset recalibration reference times */ sim_throt_ms_start = sim_os_msec (); sim_throt_inst_start = sim_gtime (); /* Start with prior calibrated delay */ sim_activate (&sim_throttle_unit, sim_throt_wait); } else { /* Start calibration initially */ sim_throt_state = SIM_THROT_STATE_INIT; 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, delta_inst; switch (sim_throt_state) { case SIM_THROT_STATE_INIT: /* take initial reading */ if ((sim_calb_tmr != -1) && (rtc_hz[sim_calb_tmr] != 0)) { if (rtc_calibrations[sim_calb_tmr] < sim_idle_stable) { sim_throt_ms_start = sim_os_msec (); sim_throt_inst_start = sim_gtime (); sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc(INIT) Deferring until stable (%d more seconds)\n", (int)(sim_idle_stable - rtc_calibrations[sim_calb_tmr])); return sim_activate (uptr, rtc_hz[sim_calb_tmr]*rtc_currd[sim_calb_tmr]); } sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc(INIT) Computing Throttling values based on the last second's execution rate\n"); sim_throt_state = SIM_THROT_STATE_TIME; if (sim_throt_peak_cps < (double)(rtc_hz[sim_calb_tmr] * rtc_currd[sim_calb_tmr])) sim_throt_peak_cps = (double)rtc_hz[sim_calb_tmr] * rtc_currd[sim_calb_tmr]; return sim_throt_svc (uptr); } else sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc(INIT) Calibrated timer not available. Falling back to legacy method\n"); sim_idle_ms_sleep (sim_idle_rate_ms); /* start on a tick boundary to calibrate */ sim_throt_ms_start = sim_os_msec (); sim_throt_inst_start = sim_gtime (); if (sim_throt_type != SIM_THROT_SPC) { /* dynamic? */ switch (sim_throt_type) { case SIM_THROT_PCT: sim_throt_wait = (int32)((sim_throt_peak_cps * sim_throt_val) / 100.0); break; case SIM_THROT_KCYC: sim_throt_wait = sim_throt_val * 1000; break; case SIM_THROT_MCYC: sim_throt_wait = sim_throt_val * 1000000; break; } sim_throt_state = SIM_THROT_STATE_TIME; /* next state */ } else { /* Non dynamic? */ sim_throt_wait = sim_throt_val; sim_throt_state = SIM_THROT_STATE_THROTTLE; /* force state */ sim_throt_cps = (int32)((1000.0 * sim_throt_val) / (double)sim_throt_sleep_time); } sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc(INIT) Starting. Values wait = %d\n", sim_throt_wait); break; /* reschedule */ case SIM_THROT_STATE_TIME: /* take final reading */ sim_throt_ms_stop = sim_os_msec (); delta_ms = sim_throt_ms_stop - sim_throt_ms_start; delta_inst = sim_gtime () - sim_throt_inst_start; if (delta_ms < SIM_THROT_MSMIN) { /* not enough time? */ if (delta_inst >= 100000000.0) { /* 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_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Not enough time. %d ms executing %.f instructions.\n", (int)delta_ms, delta_inst); sim_throt_wait = (int32)(delta_inst * SIM_THROT_WMUL); sim_throt_inst_start = sim_gtime(); sim_idle_ms_sleep (sim_idle_rate_ms); /* start on a tick boundart to calibrate */ sim_throt_ms_start = sim_os_msec (); } else { /* long enough */ a_cps = (((double) delta_inst) * 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 = (sim_throt_peak_cps * sim_throt_val) / 100.0; if (d_cps > a_cps) { sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() CPU too slow. Values a_cps = %f, d_cps = %f\n", a_cps, d_cps); sim_throt_state = SIM_THROT_STATE_INIT; sim_printf ("*********** WARNING ***********\n"); sim_printf ("Host CPU is too slow to simulate %s instructions per second\n", sim_fmt_numeric(d_cps)); sim_printf ("Host CPU can only simulate %s instructions per second\n", sim_fmt_numeric(sim_throt_peak_cps)); sim_printf ("Throttling disabled.\n"); sim_set_throt (0, NULL); return SCPE_OK; } while (1) { sim_throt_wait = (int32) /* cycles between sleeps */ ((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_currd[tmr] = (int32)(sim_throt_cps / rtc_hz[tmr]);/* use throttle calibration */ rtc_ticks[tmr] = rtc_hz[tmr] - 1; /* force clock calibration on next tick */ rtc_rtime[tmr] = sim_throt_ms_start - 1000 + 1000/rtc_hz[tmr];/* adjust calibration parameters to reflect throttled rate */ rtc_gtime[tmr] = sim_throt_inst_start - sim_throt_cps + sim_throt_cps/rtc_hz[tmr]; rtc_nxintv[tmr] = 1000; rtc_based[tmr] = rtc_currd[tmr]; if (sim_clock_unit[tmr]) sim_activate_abs (sim_clock_unit[tmr], rtc_currd[tmr]);/* reschedule next tick */ } } } 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 (delta_ms >= 10000) { /* recompute every 10 sec */ double delta_insts = sim_gtime() - sim_throt_inst_start; a_cps = (delta_insts * 1000.0) / (double) delta_ms; if (sim_throt_type != SIM_THROT_SPC) { /* when not dynamic throttling */ if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */ d_cps = (double) sim_throt_val * 1000000.0; else if (sim_throt_type == SIM_THROT_KCYC) d_cps = (double) sim_throt_val * 1000.0; else d_cps = (sim_throt_peak_cps * sim_throt_val) / 100.0; if (fabs(100.0 * (d_cps - a_cps) / d_cps) > (double)sim_throt_drift_pct) { sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Recalibrating throttle based on values a_cps = %f, d_cps = %f deviating by %.2f%% from the desired value\n", a_cps, d_cps, fabs(100.0 * (d_cps - a_cps) / d_cps)); if ((a_cps > d_cps) && /* too fast? */ ((100.0 * (a_cps - d_cps) / d_cps) > (100 - sim_throt_drift_pct))) { sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Restarting calibrating throttle going too fast: a_cps = %f, d_cps = %f deviating by %.2f%% from the desired value\n", a_cps, d_cps, fabs(100.0 * (d_cps - a_cps) / d_cps)); while (1) { sim_throt_wait = (int32) /* cycles between sleeps */ ((sim_throt_peak_cps * d_cps * ((double) sim_throt_sleep_time)) / (1000.0 * (sim_throt_peak_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, sim_throt_peak_cps, d_cps, sim_throt_wait); } } else { /* slow or within reasonable range */ sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Adjusting wait before sleep interval by %d\n", (int32)(((d_cps - a_cps) * (double)sim_throt_wait) / d_cps)); sim_throt_wait += (int32)(((d_cps - a_cps) * (double)sim_throt_wait) / d_cps); } 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 */ sim_throt_ms_start = sim_os_msec (); sim_throt_inst_start = sim_gtime(); } } else { /* record instruction rate */ sim_throt_cps = (int32)a_cps; sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Recalibrating Special %d/%u Cycles Per Second of %f\n", sim_throt_wait, sim_throt_sleep_time, sim_throt_cps); sim_throt_inst_start = sim_gtime(); sim_throt_ms_start = sim_os_msec (); } } 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)) { UNIT *sptr = sim_clock_cosched_queue[tmr]; UNIT *cptr = QUEUE_LIST_END; if (rtc_clock_catchup_eligible[tmr]) { /* calibration started? */ struct timespec now; double skew; clock_gettime(CLOCK_REALTIME, &now); skew = (_timespec_to_double(&now) - (rtc_calib_tick_time[tmr]+rtc_clock_catchup_base_time[tmr])); if (fabs(skew) > fabs(rtc_clock_skew_max[tmr])) rtc_clock_skew_max[tmr] = skew; } /* First gather the queued events that are scheduled for now */ do { cptr = sim_clock_cosched_queue[tmr]; sim_clock_cosched_queue[tmr] = cptr->next; if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) { sim_clock_cosched_queue[tmr]->time += sim_cosched_interval[tmr]; sim_cosched_interval[tmr] = sim_clock_cosched_queue[tmr]->time; } else sim_cosched_interval[tmr] = 0; } while ((sim_cosched_interval[tmr] <= 0) && (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END)); if (cptr != QUEUE_LIST_END) cptr->next = QUEUE_LIST_END; /* Now dispatch that list (in order). */ while (sptr != QUEUE_LIST_END) { cptr = sptr; sptr = sptr->next; cptr->next = NULL; cptr->cancel = NULL; cptr->time = 0; if (cptr->usecs_remaining) { sim_debug (DBG_QUE, &sim_timer_dev, " remnant: %.0f - next %s after cosched interval: %d ticks\n", cptr->usecs_remaining, (sptr != QUEUE_LIST_END) ? sim_uname (sptr) : "", sim_cosched_interval[tmr]); sim_timer_activate_after (cptr, cptr->usecs_remaining); } else { sim_debug (DBG_QUE, &sim_timer_dev, " - next %s after cosched interval: %d ticks\n", (sptr != QUEUE_LIST_END) ? sim_uname (sptr) : "", sim_cosched_interval[tmr]); _sim_activate (cptr, 0); } } } return stat; } t_stat sim_timer_stop_svc (UNIT *uptr) { return SCPE_STOP; } void sim_rtcn_get_time (struct timespec *now, int tmr) { sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_get_time(tmr=%d)\n", tmr); clock_gettime (CLOCK_REALTIME, now); } /* * 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); sim_timer_thread_running = TRUE; 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 */ } } sim_timer_thread_running = FALSE; 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_debug(DBG_INT, &sim_timer_dev, "sim_timer_clock_tick_svc()\n"); sim_rtcn_calb (sim_int_clk_tps, SIM_INTERNAL_CLK); sim_activate_after (uptr, 1000000/sim_int_clk_tps); /* reactivate unit */ return SCPE_OK; } /* This routine exists to assure that there is a single reliably calibrated 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) && (sim_clock_unit[tmr])) 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|DBG_INT, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Starting Internal Calibrated Timer at %dHz\n", newtmr, sim_int_clk_tps); SIM_INTERNAL_UNIT.action = &sim_timer_clock_tick_svc; SIM_INTERNAL_UNIT.flags = UNIT_IDLE; sim_register_internal_device (&sim_int_timer_dev); /* Register Internal timer device */ sim_rtcn_init_unit (&SIM_INTERNAL_UNIT, (CLK_INIT*CLK_TPS)/sim_int_clk_tps, SIM_INTERNAL_CLK); SIM_INTERNAL_UNIT.action (&SIM_INTERNAL_UNIT); /* Force tick to activate timer */ } return; } if ((tmr == newtmr) && (sim_calb_tmr == newtmr)) /* already set? */ return; if (sim_calb_tmr == SIM_NTIMERS) { /* was old the internal timer? */ sim_debug (DBG_CAL|DBG_INT, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Stopping Internal Calibrated Timer, New Timer = %d (%dHz)\n", newtmr, tmr, rtc_hz[tmr]); rtc_initd[SIM_NTIMERS] = 0; rtc_hz[SIM_NTIMERS] = 0; sim_register_clock_unit_tmr (NULL, SIM_INTERNAL_CLK); sim_cancel (&SIM_INTERNAL_UNIT); sim_cancel (&sim_timer_units[SIM_NTIMERS]); } else { 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|DBG_INT, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Changing Calibrated Timer from %d (%dHz) to %d (%dHz)\n", newtmr, sim_calb_tmr, rtc_hz[sim_calb_tmr], tmr, rtc_hz[tmr]); sim_calb_tmr = tmr; } sim_calb_tmr = tmr; } static t_stat sim_timer_clock_reset (DEVICE *dptr) { sim_debug (DBG_TRC, &sim_timer_dev, "sim_timer_clock_reset()\n"); _rtcn_configure_calibrated_clock (sim_calb_tmr); sim_timer_dev.description = &sim_timer_description; sim_throttle_dev.description = &sim_throttle_description; sim_int_timer_dev.description = &sim_int_timer_description; sim_stop_dev.description = &sim_int_stop_description; if (sim_switches & SWMASK ('P')) { sim_cancel (&SIM_INTERNAL_UNIT); sim_calb_tmr = -1; } return SCPE_OK; } void sim_start_timer_services (void) { sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services()\n"); _rtcn_configure_calibrated_clock (sim_calb_tmr); if (sim_timer_stop_time > sim_gtime()) sim_activate_abs (&sim_stop_unit, (int32)(sim_timer_stop_time - sim_gtime())); #if defined(SIM_ASYNCH_CLOCKS) pthread_mutex_lock (&sim_timer_lock); if (sim_asynch_timer) { pthread_attr_t attr; sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services() - starting\n"); pthread_cond_init (&sim_timer_startup_cond, NULL); 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); } 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 */ if (sim_is_active (&sim_timer_units[tmr])) { sim_cancel (&sim_timer_units[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); /* 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 */ if (sim_timer_thread_running) { 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/SIMH/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_DFLT 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/SIMH/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 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 | /* 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 = lp->txstall = 0; tmxr_set_get_modem_bits (lp, 0, 0, NULL); if ((!lp->mp->buffered) && (!lp->txbfd)) { lp->txbfd = 0; lp->txbsz = TMXR_MAXBUF; lp->txb = (char *)realloc (lp->txb, lp->txbsz); lp->rxbsz = TMXR_MAXBUF; lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz); 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 = lp->txstall = 0;/* init counters */ lp->rxpcnt = lp->txpcnt = 0; } else if (lp->txcnt > lp->txbsz) lp->txbpr = (lp->txbpi + 1) % lp->txbsz; else lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf)); psave = lp->txbpi; /* save insertion pointer */ lp->txbpi = lp->txbpr; /* insert connection message */ if ((lp->serport) && (!sim_is_running)) { sim_os_ms_sleep (TMXR_DTR_DROP_TIME); /* Wait for DTR to be noticed */ lp->ser_connect_pending = FALSE; /* Mark line as ready for action */ lp->conn = TRUE; } tmxr_linemsg (lp, msgbuf); /* beginning of buffer */ lp->txbpi = psave; /* restore insertion pointer */ unwritten = tmxr_send_buffered_data (lp); /* send the message */ if ((lp->serport) && (!sim_is_running)) { lp->ser_connect_pending = TRUE; /* Mark line as not yet ready for action */ lp->conn = FALSE; } if (unwritten == 0) /* buffer now empty? */ lp->xmte = 1; /* reenable transmission if paused */ lp->txcnt -= (int32)strlen (msgbuf); /* adjust statistics */ return; } /* 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) && (sim_is_running)) return 0; written = sim_write_serial (lp->serport, &(lp->txb[i]), length); if ((written > 0) && (sim_is_running)) lp->txnexttime = floor (sim_gtime () + (written * lp->txdelta * sim_timer_inst_per_sec ())); return written; } else { /* Telnet connection */ written = sim_write_sock (lp->sock, &(lp->txb[i]), length); if (written == SOCKET_ERROR) /* did an error occur? */ if (lp->datagram) 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 == ' ')) memmove (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 == ' ')) memmove (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); if (lp->conn) /* Still connected ? */ 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 */ if ((sim_interval > 0) && /* not called within sim_process_event? */ (lp->txbps) && (lp->txdelta > 1000)) { /* and rate limiting output slower than 1000 cps */ tmxr_send_buffered_data (lp); /* put data on wire */ sim_os_ms_sleep((lp->txdelta - 1000) / 1000); /* wait an approximate character delay */ } return SCPE_OK; /* char sent */ } ++lp->txstall; lp->xmte = 0; /* no room, dsbl line */ return SCPE_STALL; /* char not sent */ } /* Store packet in line buffer Inputs: *lp = pointer to line descriptor *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; 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"); strlcpy(logfiletmpl, cptr, sizeof(logfiletmpl)); continue; } if (0 == MATCH_CMD (gbuf, "LOOPBACK")) { if ((NULL != cptr) && ('\0' != *cptr)) return sim_messagef (SCPE_2MARG, "Unexpected Loopback Specifier: %s\n", cptr); loopback = TRUE; continue; } 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"); strlcpy (destination, cptr, sizeof(destination)); continue; } if (0 == MATCH_CMD (gbuf, "SPEED")) { if ((NULL == cptr) || ('\0' == *cptr) || (_tmln_speed_delta (cptr) < 0)) return sim_messagef (SCPE_ARG, "Invalid Speed Specifier: %s\n", (cptr ? cptr : "")); strlcpy (speed, cptr, sizeof(speed)); continue; } cptr = get_glyph (gbuf, port, ';'); if (sim_parse_addr (port, NULL, 0, NULL, NULL, 0, NULL, NULL)) return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port); if (cptr) { char *tptr = gbuf + (cptr - gbuf); 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)); strlcpy (hostport, destination, sizeof(hostport)); if ((eptr = strchr (hostport, ';'))) *(eptr++) = '\0'; if (eptr) { get_glyph (eptr, eptr, 0); /* upcase this string */ if (0 == MATCH_CMD (eptr, "NOTELNET")) notelnet = TRUE; else 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]) { strlcpy(mp->logfiletmpl, logfiletmpl, sizeof(mp->logfiletmpl)); for (i = 0; i < mp->lines; i++) { lp = mp->ldsc + i; sim_close_logfile (&lp->txlogref); lp->txlog = NULL; lp->txlogname = (char *)realloc(lp->txlogname, CBUFSIZE); lp->txlogname[CBUFSIZE-1] = '\0'; if (mp->lines > 1) snprintf(lp->txlogname, CBUFSIZE-1, "%s_%d", mp->logfiletmpl, i); else strlcpy (lp->txlogname, mp->logfiletmpl, CBUFSIZE); r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref); if (r != SCPE_OK) { free (lp->txlogname); lp->txlogname = NULL; break; } } } 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; } sim_messagef (SCPE_OK, "Listening on port %s\n", listen); mp->port = (char *)realloc (mp->port, 1 + strlen (listen)); strcpy (mp->port, listen); /* save port */ mp->master = sock; /* save master socket */ mp->ring_sock = INVALID_SOCKET; free (mp->ring_ipad); mp->ring_ipad = NULL; mp->ring_start_time = 0; 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"); sim_messagef (SCPE_OK, "Operating in loopback mode\n"); for (i = 0; i < mp->lines; i++) { lp = mp->ldsc + i; tmxr_set_line_loopback (lp, loopback); if (speed[0]) tmxr_set_line_speed (lp, speed); } } 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 */ 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); sim_messagef (SCPE_OK, "Line %d Listening on port %s\n", line, listen); lp->port = (char *)realloc (lp->port, 1 + strlen (listen)); strcpy (lp->port, listen); /* save port */ lp->master = sock; /* save master socket */ if (listennotelnet != mp->notelnet) lp->notelnet = listennotelnet; else lp->notelnet = mp->notelnet; } 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 */ 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); sim_messagef (SCPE_OK, "Line %d operating in loopback mode\n", line); } lp->modem_control = modem_control; if (speed[0] && (!datagram) && (!lp->serport)) tmxr_set_line_speed (lp, speed); r = SCPE_OK; } } 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++) mux->ldsc[i].send.after = mux->ldsc[i].send.delay = 0; } #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); } static const char *_tmxr_send_expect_line_name (const SEND *snd, const EXPECT *exp) { static char line_name[CBUFSIZE]; int i, j; strcpy (line_name, ""); for (i=0; i<tmxr_open_device_count; ++i) for (j=0; j<tmxr_open_devices[i]->lines; ++j) if ((snd == &tmxr_open_devices[i]->ldsc[j].send) || (exp == &tmxr_open_devices[i]->ldsc[j].expect)) { if (tmxr_open_devices[i]->lines > 1) snprintf (line_name, sizeof (line_name), "%s:%d", tmxr_open_devices[i]->ldsc[j].send.dptr->name, j); else strlcpy (line_name, tmxr_open_devices[i]->ldsc[j].send.dptr->name, sizeof (line_name)); break; } return line_name; } const char *tmxr_send_line_name (const SEND *snd) { if (snd == sim_cons_get_send ()) return "CONSOLE"; else return _tmxr_send_expect_line_name (snd, NULL); } const char *tmxr_expect_line_name (const EXPECT *exp) { if (exp == sim_cons_get_expect ()) return "CONSOLE"; else return _tmxr_send_expect_line_name (NULL, exp); } t_stat tmxr_change_async (void) { #if defined(SIM_ASYNCH_IO) if (sim_asynch_enabled) tmxr_start_poll (); else tmxr_stop_poll (); #endif return SCPE_OK; } /* Attach unit to master socket */ t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, CONST char *cptr, t_bool async) { t_stat r; int32 i; if (mp->dptr == NULL) /* has device been set? */ mp->dptr = find_dev_from_unit (uptr); /* no, so set device now */ r = tmxr_open_master (mp, cptr); /* open master socket */ if (r != SCPE_OK) /* error? */ return r; mp->uptr = uptr; /* save unit for polling */ uptr->filename = tmxr_mux_attach_string (uptr->filename, mp);/* save */ uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */ uptr->tmxr = (void *)mp; if ((mp->lines > 1) || ((mp->master == 0) && (mp->ldsc[0].connecting == 0) && (mp->ldsc[0].serport == 0))) uptr->dynflags = uptr->dynflags | UNIT_ATTMULT; /* allow multiple attach commands */ #if defined(SIM_ASYNCH_MUX) if (!async || (uptr->flags & TMUF_NOASYNCH)) /* if asynch disabled */ uptr->dynflags |= TMUF_NOASYNCH; /* tag as no asynch */ #else uptr->dynflags |= TMUF_NOASYNCH; /* tag as no asynch */ #endif if (mp->dptr) { 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); if (lp->txstall) fprintf (st, " stalled = %d\n", lp->txstall); return; } /* Disconnect a line. Disconnect a line of the multiplexer associated with descriptor "desc" from a 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 */ if (lp->serport && (sim_switches & SWMASK ('C'))) return tmxr_detach_ln (lp); return tmxr_reset_ln_ex (lp, FALSE); /* drop the line */ } return SCPE_OK; } /* Enable logging for line */ 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; strlcpy (lp->txlogname, cptr, CBUFSIZE); /* save file name */ sim_open_logfile (cptr, TRUE, &lp->txlog, &lp->txlogref);/* open log */ if (lp->txlog == NULL) { /* error? */ free (lp->txlogname); /* free buffer */ return SCPE_OPENERR; } if (mp->uptr) /* attached?, then update attach string */ lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp); 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/SIMH/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 328 329 330 | /* 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 txstall; /* xmt stall count */ int32 txbsz; /* xmt buffer size */ int32 txbfd; /* xmt buffered flag */ t_bool modem_control; /* line supports modem control behaviors */ int32 modembits; /* modem bits which are currently set */ FILE *txlog; /* xmt log file */ FILEREF *txlogref; /* xmt log file reference */ char *txlogname; /* xmt log file name */ 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); const char *tmxr_send_line_name (const SEND *snd); const char *tmxr_expect_line_name (const EXPECT *exp); t_stat tmxr_startup (void); t_stat tmxr_shutdown (void); t_stat tmxr_start_poll (void); t_stat tmxr_stop_poll (void); void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize); #define tmxr_debug(dbits, lp, msg, buf, bufsize) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) _tmxr_debug (dbits, lp, msg, buf, bufsize); } while (0) #define tmxr_debug_msg(dbits, lp, msg) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) sim_debug (dbits, (lp)->mp->dptr, "%s", msg); } while (0) #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/SIMH/sim_video.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 | /* sim_video.c: Bitmap video output Copyright (c) 2011-2013, Matt Burke 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. 08-Nov-2013 MB Added globals for current mouse status 11-Jun-2013 MB First version */ #include "sim_video.h" #include "scp.h" t_bool vid_active = FALSE; int32 vid_cursor_x; int32 vid_cursor_y; t_bool vid_mouse_b1 = FALSE; t_bool vid_mouse_b2 = FALSE; t_bool vid_mouse_b3 = FALSE; static VID_QUIT_CALLBACK vid_quit_callback = NULL; t_stat vid_register_quit_callback (VID_QUIT_CALLBACK callback) { vid_quit_callback = callback; return SCPE_OK; } t_stat vid_show (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc) { return vid_show_video (st, uptr, val, desc); } #if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL) char vid_release_key[64] = "Ctrl-Right-Shift"; #include <SDL.h> #include <SDL_thread.h> static const char *key_names[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "BACKQUOTE", "MINUS", "EQUALS", "LEFT_BRACKET", "RIGHT_BRACKET", "SEMICOLON", "SINGLE_QUOTE", "BACKSLASH", "LEFT_BACKSLASH", "COMMA", "PERIOD", "SLASH", "PRINT", "SCRL_LOCK", "PAUSE", "ESC", "BACKSPACE", "TAB", "ENTER", "SPACE", "INSERT", "DELETE", "HOME", "END", "PAGE_UP", "PAGE_DOWN", "UP", "DOWN", "LEFT", "RIGHT", "CAPS_LOCK", "NUM_LOCK", "ALT_L", "ALT_R", "CTRL_L", "CTRL_R", "SHIFT_L", "SHIFT_R", "WIN_L", "WIN_R", "MENU", "KP_ADD", "KP_SUBTRACT", "KP_END", "KP_DOWN", "KP_PAGE_DOWN", "KP_LEFT", "KP_RIGHT", "KP_HOME", "KP_UP", "KP_PAGE_UP", "KP_INSERT", "KP_DELETE", "KP_5", "KP_ENTER", "KP_MULTIPLY", "KP_DIVIDE" }; const char *vid_key_name (int32 key) { static char tmp_key_name[40]; if (key < sizeof(key_names)/sizeof(key_names[0])) sprintf (tmp_key_name, "SIM_KEY_%s", key_names[key]); else sprintf (tmp_key_name, "UNKNOWN KEY: %d", key); return tmp_key_name; } #if defined(HAVE_LIBPNG) /* From: https://github.com/driedfruit/SDL_SavePNG */ /* * Save an SDL_Surface as a PNG file. * * Returns 0 success or -1 on failure, the error message is then retrievable * via SDL_GetError(). */ #define SDL_SavePNG(surface, file) \ SDL_SavePNG_RW(surface, SDL_RWFromFile(file, "wb"), 1) /* * SDL_SavePNG -- libpng-based SDL_Surface writer. * * This code is free software, available under zlib/libpng license. * http://www.libpng.org/pub/png/src/libpng-LICENSE.txt */ #include <SDL.h> #include <png.h> #define SUCCESS 0 #define ERROR -1 #define USE_ROW_POINTERS #if SDL_BYTEORDER == SDL_BIG_ENDIAN #define rmask 0xFF000000 #define gmask 0x00FF0000 #define bmask 0x0000FF00 #define amask 0x000000FF #else #define rmask 0x000000FF #define gmask 0x0000FF00 #define bmask 0x00FF0000 #define amask 0xFF000000 #endif /* libpng callbacks */ static void png_error_SDL(png_structp ctx, png_const_charp str) { SDL_SetError("libpng: %s\n", str); } static void png_write_SDL(png_structp png_ptr, png_bytep data, png_size_t length) { SDL_RWops *rw = (SDL_RWops*)png_get_io_ptr(png_ptr); SDL_RWwrite(rw, data, sizeof(png_byte), length); } static SDL_Surface *SDL_PNGFormatAlpha(SDL_Surface *src) { SDL_Surface *surf; SDL_Rect rect = { 0 }; /* NO-OP for images < 32bpp and 32bpp images that already have Alpha channel */ if (src->format->BitsPerPixel <= 24 || src->format->Amask) { src->refcount++; return src; } /* Convert 32bpp alpha-less image to 24bpp alpha-less image */ rect.w = src->w; rect.h = src->h; surf = SDL_CreateRGBSurface(src->flags, src->w, src->h, 24, src->format->Rmask, src->format->Gmask, src->format->Bmask, 0); SDL_LowerBlit(src, &rect, surf, &rect); return surf; } static int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst) { png_structp png_ptr; png_infop info_ptr; png_colorp pal_ptr; SDL_Palette *pal; int i, colortype; #ifdef USE_ROW_POINTERS png_bytep *row_pointers; #endif /* Initialize and do basic error checking */ if (!dst) { SDL_SetError("Argument 2 to SDL_SavePNG_RW can't be NULL, expecting SDL_RWops*\n"); return (ERROR); } if (!surface) { SDL_SetError("Argument 1 to SDL_SavePNG_RW can't be NULL, expecting SDL_Surface*\n"); if (freedst) SDL_RWclose(dst); return (ERROR); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_SDL, NULL); /* err_ptr, err_fn, warn_fn */ if (!png_ptr) { SDL_SetError("Unable to png_create_write_struct on %s\n", PNG_LIBPNG_VER_STRING); if (freedst) SDL_RWclose(dst); return (ERROR); } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { SDL_SetError("Unable to png_create_info_struct\n"); png_destroy_write_struct(&png_ptr, NULL); if (freedst) SDL_RWclose(dst); return (ERROR); } if (setjmp(png_jmpbuf(png_ptr))) /* All other errors, see also "png_error_SDL" */ { png_destroy_write_struct(&png_ptr, &info_ptr); if (freedst) SDL_RWclose(dst); return (ERROR); } /* Setup our RWops writer */ png_set_write_fn(png_ptr, dst, png_write_SDL, NULL); /* w_ptr, write_fn, flush_fn */ /* Prepare chunks */ colortype = PNG_COLOR_MASK_COLOR; if (surface->format->BytesPerPixel > 0 && surface->format->BytesPerPixel <= 8 && (pal = surface->format->palette)) { colortype |= PNG_COLOR_MASK_PALETTE; pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color)); for (i = 0; i < pal->ncolors; i++) { pal_ptr[i].red = pal->colors[i].r; pal_ptr[i].green = pal->colors[i].g; pal_ptr[i].blue = pal->colors[i].b; } png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors); free(pal_ptr); } else if (surface->format->BytesPerPixel > 3 || surface->format->Amask) colortype |= PNG_COLOR_MASK_ALPHA; png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8, colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // png_set_packing(png_ptr); /* Allow BGR surfaces */ if (surface->format->Rmask == bmask && surface->format->Gmask == gmask && surface->format->Bmask == rmask) png_set_bgr(png_ptr); /* Write everything */ png_write_info(png_ptr, info_ptr); #ifdef USE_ROW_POINTERS row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*surface->h); for (i = 0; i < surface->h; i++) row_pointers[i] = (png_bytep)(Uint8*)surface->pixels + i * surface->pitch; png_write_image(png_ptr, row_pointers); free(row_pointers); #else for (i = 0; i < surface->h; i++) png_write_row(png_ptr, (png_bytep)(Uint8*)surface->pixels + i * surface->pitch); #endif png_write_end(png_ptr, info_ptr); /* Done */ png_destroy_write_struct(&png_ptr, &info_ptr); if (freedst) SDL_RWclose(dst); return (SUCCESS); } #endif /* defined(HAVE_LIBPNG) */ /* Some platforms (OS X), require that ALL input event processing be performed by the main thread of the process. To satisfy this requirement, we leverage the SDL_MAIN functionality which does: #defines main SDL_main and we define the main() entry point here. Locally, we run the application's SDL_main in a separate thread, and while that thread is running, the main thread performs event handling and dispatch. */ #define EVENT_REDRAW 1 /* redraw event for SDL */ #define EVENT_CLOSE 2 /* close event for SDL */ #define EVENT_CURSOR 3 /* new cursor for SDL */ #define EVENT_WARP 4 /* warp mouse position for SDL */ #define EVENT_DRAW 5 /* draw/blit region for SDL */ #define EVENT_SHOW 6 /* show SDL capabilities */ #define EVENT_OPEN 7 /* vid_open request */ #define EVENT_EXIT 8 /* program exit */ #define EVENT_SCREENSHOT 9 /* produce screenshot of video window */ #define EVENT_BEEP 10 /* audio beep */ #define MAX_EVENTS 20 /* max events in queue */ typedef struct { SIM_KEY_EVENT events[MAX_EVENTS]; SDL_sem *sem; int32 head; int32 tail; int32 count; } KEY_EVENT_QUEUE; typedef struct { SIM_MOUSE_EVENT events[MAX_EVENTS]; SDL_sem *sem; int32 head; int32 tail; int32 count; } MOUSE_EVENT_QUEUE; int vid_thread (void* arg); int vid_video_events (void); void vid_show_video_event (void); void vid_screenshot_event (void); void vid_beep_event (void); /* libSDL and libSDL2 have significantly different APIs. The consequence is that this code has significant #ifdef sections. The current structure is to implement the API differences in each routine that has a difference. This allows the decision and flow logic to exist once and thus to allow logic changes to be implemented in one place. */ t_bool vid_mouse_captured; int32 vid_flags; /* Open Flags */ int32 vid_width; int32 vid_height; t_bool vid_ready; char vid_title[128]; static void vid_beep_setup (int duration_ms, int tone_frequency); static void vid_beep_cleanup (void); #if SDL_MAJOR_VERSION == 1 /* Some platforms that use X11 display technology have libSDL environments which need to call XInitThreads when libSDL is used in multi-threaded programs. This routine attempts to locate the X11 shareable library and if it is found loads it and calls the XInitThreads routine to meet this requirement. */ #ifdef HAVE_DLOPEN #include <dlfcn.h> #endif static void _XInitThreads (void) { #ifdef HAVE_DLOPEN static void *hLib = NULL; /* handle to Library */ #define __STR_QUOTE(tok) #tok #define __STR(tok) __STR_QUOTE(tok) static const char* lib_name = "libX11." __STR(HAVE_DLOPEN); typedef int (*_func)(); _func _func_ptr = NULL; if (!hLib) hLib = dlopen(lib_name, RTLD_NOW); if (hLib) _func_ptr = (_func)((size_t)dlsym(hLib, "XInitThreads")); if (_func_ptr) _func_ptr(); #endif } t_bool vid_key_state[SDLK_LAST]; SDL_Surface *vid_image; /* video buffer */ SDL_Surface *vid_window; /* window handle */ #else t_bool vid_key_state[SDL_NUM_SCANCODES]; SDL_Texture *vid_texture; /* video buffer in GPU */ SDL_Renderer *vid_renderer; SDL_Window *vid_window; /* window handle */ uint32 vid_windowID; #endif SDL_Thread *vid_thread_handle = NULL; /* event thread handle */ SDL_Cursor *vid_cursor = NULL; /* current cursor */ t_bool vid_cursor_visible = FALSE; /* cursor visibility state */ uint32 vid_mono_palette[2]; /* Monochrome Color Map */ SDL_Color vid_colors[256]; KEY_EVENT_QUEUE vid_key_events; /* keyboard events */ MOUSE_EVENT_QUEUE vid_mouse_events; /* mouse events */ DEVICE *vid_dev; #if defined (SDL_MAIN_AVAILABLE) #if defined (main) #undef main #endif static int main_argc; static char **main_argv; static SDL_Thread *vid_main_thread_handle; int main_thread (void *arg) { SDL_Event user_event; int stat; stat = SDL_main (main_argc, main_argv); user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_EXIT; user_event.user.data1 = NULL; user_event.user.data2 = NULL; while (SDL_PushEvent (&user_event) < 0) sim_os_ms_sleep (10); return stat; } int main (int argc, char *argv[]) { SDL_Event event; int status; main_argc = argc; main_argv = argv; #if SDL_MAJOR_VERSION == 1 _XInitThreads(); SDL_Init (SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE); vid_main_thread_handle = SDL_CreateThread (main_thread , NULL); #else SDL_SetHint (SDL_HINT_RENDER_DRIVER, "software"); SDL_Init (SDL_INIT_VIDEO); vid_main_thread_handle = SDL_CreateThread (main_thread , "simh-main", NULL); #endif sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); vid_beep_setup (400, 660); while (1) { int status = SDL_WaitEvent (&event); if (status == 1) { if (event.type == SDL_USEREVENT) { if (event.user.code == EVENT_EXIT) break; if (event.user.code == EVENT_OPEN) vid_video_events (); else { if (event.user.code == EVENT_SHOW) vid_show_video_event (); else { if (event.user.code == EVENT_SCREENSHOT) vid_screenshot_event (); else { if (event.user.code == EVENT_BEEP) vid_beep_event (); else { sim_printf ("main(): Unexpected User event: %d\n", event.user.code); break; } } } } } else { // sim_printf ("main(): Ignoring unexpected event: %d\n", event.type); } } else { if (status < 0) sim_printf ("main() - ` error: %s\n", SDL_GetError()); } } SDL_WaitThread (vid_main_thread_handle, &status); vid_beep_cleanup (); SDL_Quit (); return status; } static t_stat vid_create_window () { int wait_count = 0; SDL_Event user_event; vid_ready = FALSE; user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_OPEN; user_event.user.data1 = NULL; user_event.user.data2 = NULL; SDL_PushEvent (&user_event); while ((!vid_ready) && (++wait_count < 20)) sim_os_ms_sleep (100); if (!vid_ready) { vid_close (); return SCPE_OPENERR; } return SCPE_OK; } #else static int vid_create_window () { int wait_count = 0; #if SDL_MAJOR_VERSION == 1 vid_thread_handle = SDL_CreateThread (vid_thread, NULL); #else vid_thread_handle = SDL_CreateThread (vid_thread, "vid-thread", NULL); #endif if (vid_thread_handle == NULL) { vid_close (); return SCPE_OPENERR; } while ((!vid_ready) && (++wait_count < 20)) sim_os_ms_sleep (100); if (!vid_ready) { vid_close (); return SCPE_OPENERR; } return SCPE_OK; } #endif t_stat vid_open (DEVICE *dptr, const char *title, uint32 width, uint32 height, int flags) { if (!vid_active) { int wait_count = 0; t_stat stat; if ((strlen(sim_name) + 7 + (dptr ? strlen (dptr->name) : 0) + (title ? strlen (title) : 0)) < sizeof (vid_title)) sprintf (vid_title, "%s%s%s%s%s", sim_name, dptr ? " - " : "", dptr ? dptr->name : "", title ? " - " : "", title ? title : ""); else sprintf (vid_title, "%s", sim_name); vid_flags = flags; vid_active = TRUE; vid_width = width; vid_height = height; vid_mouse_captured = FALSE; vid_cursor_visible = (vid_flags & SIM_VID_INPUTCAPTURED); vid_key_events.head = 0; vid_key_events.tail = 0; vid_key_events.count = 0; vid_key_events.sem = SDL_CreateSemaphore (1); vid_mouse_events.head = 0; vid_mouse_events.tail = 0; vid_mouse_events.count = 0; vid_mouse_events.sem = SDL_CreateSemaphore (1); vid_dev = dptr; stat = vid_create_window (); if (stat != SCPE_OK) return stat; sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_open() - Success\n"); } return SCPE_OK; } t_stat vid_close (void) { if (vid_active) { SDL_Event user_event; int status; vid_active = FALSE; if (vid_ready) { sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_close()\n"); user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_CLOSE; user_event.user.data1 = NULL; user_event.user.data2 = NULL; while (SDL_PushEvent (&user_event) < 0) sim_os_ms_sleep (10); if (vid_thread_handle) { SDL_WaitThread (vid_thread_handle, &status); vid_thread_handle = NULL; } vid_dev = NULL; } while (vid_ready) sim_os_ms_sleep (10); if (vid_mouse_events.sem) { SDL_DestroySemaphore(vid_mouse_events.sem); vid_mouse_events.sem = NULL; } if (vid_key_events.sem) { SDL_DestroySemaphore(vid_key_events.sem); vid_key_events.sem = NULL; } } return SCPE_OK; } t_stat vid_poll_kb (SIM_KEY_EVENT *ev) { if (SDL_SemTryWait (vid_key_events.sem) == 0) { /* get lock */ if (vid_key_events.count > 0) { /* events in queue? */ *ev = vid_key_events.events[vid_key_events.head++]; vid_key_events.count--; if (vid_key_events.head == MAX_EVENTS) vid_key_events.head = 0; SDL_SemPost (vid_key_events.sem); return SCPE_OK; } SDL_SemPost (vid_key_events.sem); } return SCPE_EOF; } t_stat vid_poll_mouse (SIM_MOUSE_EVENT *ev) { t_stat stat = SCPE_EOF; SIM_MOUSE_EVENT *nev; if (SDL_SemTryWait (vid_mouse_events.sem) == 0) { if (vid_mouse_events.count > 0) { stat = SCPE_OK; *ev = vid_mouse_events.events[vid_mouse_events.head++]; vid_mouse_events.count--; if (vid_mouse_events.head == MAX_EVENTS) vid_mouse_events.head = 0; nev = &vid_mouse_events.events[vid_mouse_events.head]; if ((vid_mouse_events.count > 0) && (0 == (ev->x_rel + nev->x_rel)) && (0 == (ev->y_rel + nev->y_rel)) && (ev->b1_state == nev->b1_state) && (ev->b2_state == nev->b2_state) && (ev->b3_state == nev->b3_state)) { if ((++vid_mouse_events.head) == MAX_EVENTS) vid_mouse_events.head = 0; vid_mouse_events.count--; stat = SCPE_EOF; sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "vid_poll_mouse: ignoring bouncing events\n"); } } if (SDL_SemPost (vid_mouse_events.sem)) sim_printf ("%s: vid_poll_mouse(): SDL_SemPost error: %s\n", sim_dname(vid_dev), SDL_GetError()); } return stat; } void vid_draw (int32 x, int32 y, int32 w, int32 h, uint32 *buf) { #if SDL_MAJOR_VERSION == 1 int32 i; uint32* pixels; sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "vid_draw(%d, %d, %d, %d)\n", x, y, w, h); pixels = (uint32 *)vid_image->pixels; for (i = 0; i < h; i++) memcpy (pixels + ((i + y) * vid_width) + x, buf + w*i, w*sizeof(*pixels)); #else SDL_Event user_event; SDL_Rect *vid_dst; uint32 *vid_data; sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "vid_draw(%d, %d, %d, %d)\n", x, y, w, h); vid_dst = (SDL_Rect *)malloc (sizeof(*vid_dst)); if (!vid_dst) { sim_printf ("%s: vid_draw() memory allocation error\n", vid_dev ? sim_dname(vid_dev) : "Video Device"); return; } vid_dst->x = x; vid_dst->y = y; vid_dst->w = w; vid_dst->h = h; vid_data = (uint32 *)malloc (w*h*sizeof(*buf)); if (!vid_data) { sim_printf ("%s: vid_draw() memory allocation error\n", vid_dev ? sim_dname(vid_dev) : "Video Device"); free (vid_dst); return; } memcpy (vid_data, buf, w*h*sizeof(*buf)); user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_DRAW; user_event.user.data1 = (void *)vid_dst; user_event.user.data2 = (void *)vid_data; if (SDL_PushEvent (&user_event) < 0) { sim_printf ("%s: vid_draw() SDL_PushEvent error: %s\n", vid_dev ? sim_dname(vid_dev) : "Video Device", SDL_GetError()); free (vid_dst); free (vid_data); } #endif } t_stat vid_set_cursor (t_bool visible, uint32 width, uint32 height, uint8 *data, uint8 *mask, uint32 hot_x, uint32 hot_y) { SDL_Cursor *cursor = SDL_CreateCursor (data, mask, width, height, hot_x, hot_y); SDL_Event user_event; sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "vid_set_cursor(%s, %d, %d) Setting New Cursor\n", visible ? "visible" : "invisible", width, height); if (sim_deb) { uint32 i, j; for (i=0; i<height; i++) { sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "Cursor: "); for (j=0; j<width; j++) { int byte = (j + i*width) >> 3; int bit = 7 - ((j + i*width) & 0x7); static char mode[] = "TWIB"; sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "%c", mode[(((data[byte]>>bit)&1)<<1)|(mask[byte]>>bit)&1]); } sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "\n"); } } user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_CURSOR; user_event.user.data1 = cursor; user_event.user.data2 = (void *)((size_t)visible); if (SDL_PushEvent (&user_event) < 0) { sim_printf ("%s: vid_set_cursor() SDL_PushEvent error: %s\n", vid_dev ? sim_dname(vid_dev) : "Video Device", SDL_GetError()); SDL_FreeCursor (cursor); } return SCPE_OK; } void vid_set_cursor_position (int32 x, int32 y) { int32 x_delta = vid_cursor_x - x; int32 y_delta = vid_cursor_y - y; if (vid_flags & SIM_VID_INPUTCAPTURED) return; if ((x_delta) || (y_delta)) { sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "vid_set_cursor_position(%d, %d) - Cursor position changed\n", x, y); /* Any queued mouse motion events need to have their relative positions adjusted since they were queued based on different info. */ if (SDL_SemWait (vid_mouse_events.sem) == 0) { int32 i; SIM_MOUSE_EVENT *ev; for (i=0; i<vid_mouse_events.count; i++) { ev = &vid_mouse_events.events[(vid_mouse_events.head + i)%MAX_EVENTS]; sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "Pending Mouse Motion Event Adjusted from: (%d, %d) to (%d, %d)\n", ev->x_rel, ev->y_rel, ev->x_rel + x_delta, ev->y_rel + y_delta); ev->x_rel += x_delta; ev->y_rel += y_delta; } if (SDL_SemPost (vid_mouse_events.sem)) sim_printf ("%s: vid_set_cursor_position(): SDL_SemPost error: %s\n", vid_dev ? sim_dname(vid_dev) : "Video Device", SDL_GetError()); } else { sim_printf ("%s: vid_set_cursor_position(): SDL_SemWait error: %s\n", vid_dev ? sim_dname(vid_dev) : "Video Device", SDL_GetError()); } vid_cursor_x = x; vid_cursor_y = y; if (vid_cursor_visible) { SDL_Event user_event; user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_WARP; user_event.user.data1 = NULL; user_event.user.data2 = NULL; if (SDL_PushEvent (&user_event) < 0) sim_printf ("%s: vid_set_cursor_position() SDL_PushEvent error: %s\n", sim_dname(vid_dev), SDL_GetError()); sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "vid_set_cursor_position() - Warp Queued\n"); } else { sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "vid_set_cursor_position() - Warp Skipped\n"); } } } void vid_refresh (void) { SDL_Event user_event; sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "vid_refresh() - Queueing Refresh Event\n"); user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_REDRAW; user_event.user.data1 = NULL; user_event.user.data2 = NULL; if (SDL_PushEvent (&user_event) < 0) sim_printf ("%s: vid_refresh() SDL_PushEvent error: %s\n", sim_dname(vid_dev), SDL_GetError()); } int vid_map_key (int key) { switch (key) { case SDLK_BACKSPACE: return SIM_KEY_BACKSPACE; case SDLK_TAB: return SIM_KEY_TAB; case SDLK_RETURN: return SIM_KEY_ENTER; case SDLK_ESCAPE: return SIM_KEY_ESC; case SDLK_SPACE: return SIM_KEY_SPACE; case SDLK_QUOTE: return SIM_KEY_SINGLE_QUOTE; case SDLK_COMMA: return SIM_KEY_COMMA; case SDLK_MINUS: return SIM_KEY_MINUS; case SDLK_PERIOD: return SIM_KEY_PERIOD; case SDLK_SLASH: return SIM_KEY_SLASH; case SDLK_0: return SIM_KEY_0; case SDLK_1: return SIM_KEY_1; case SDLK_2: return SIM_KEY_2; case SDLK_3: return SIM_KEY_3; case SDLK_4: return SIM_KEY_4; case SDLK_5: return SIM_KEY_5; case SDLK_6: return SIM_KEY_6; case SDLK_7: return SIM_KEY_7; case SDLK_8: return SIM_KEY_8; case SDLK_9: return SIM_KEY_9; case SDLK_SEMICOLON: return SIM_KEY_SEMICOLON; case SDLK_EQUALS: return SIM_KEY_EQUALS; case SDLK_LEFTBRACKET: return SIM_KEY_LEFT_BRACKET; case SDLK_BACKSLASH: return SIM_KEY_BACKSLASH; case SDLK_RIGHTBRACKET: return SIM_KEY_RIGHT_BRACKET; case SDLK_BACKQUOTE: return SIM_KEY_BACKQUOTE; case SDLK_a: return SIM_KEY_A; case SDLK_b: return SIM_KEY_B; case SDLK_c: return SIM_KEY_C; case SDLK_d: return SIM_KEY_D; case SDLK_e: return SIM_KEY_E; case SDLK_f: return SIM_KEY_F; case SDLK_g: return SIM_KEY_G; case SDLK_h: return SIM_KEY_H; case SDLK_i: return SIM_KEY_I; case SDLK_j: return SIM_KEY_J; case SDLK_k: return SIM_KEY_K; case SDLK_l: return SIM_KEY_L; case SDLK_m: return SIM_KEY_M; case SDLK_n: return SIM_KEY_N; case SDLK_o: return SIM_KEY_O; case SDLK_p: return SIM_KEY_P; case SDLK_q: return SIM_KEY_Q; case SDLK_r: return SIM_KEY_R; case SDLK_s: return SIM_KEY_S; case SDLK_t: return SIM_KEY_T; case SDLK_u: return SIM_KEY_U; case SDLK_v: return SIM_KEY_V; case SDLK_w: return SIM_KEY_W; case SDLK_x: return SIM_KEY_X; case SDLK_y: return SIM_KEY_Y; case SDLK_z: return SIM_KEY_Z; case SDLK_DELETE: return SIM_KEY_DELETE; #if SDL_MAJOR_VERSION == 1 case SDLK_KP0: return SIM_KEY_KP_INSERT; case SDLK_KP1: return SIM_KEY_KP_END; case SDLK_KP2: return SIM_KEY_KP_DOWN; case SDLK_KP3: return SIM_KEY_KP_PAGE_DOWN; case SDLK_KP4: return SIM_KEY_KP_LEFT; case SDLK_KP5: return SIM_KEY_KP_5; case SDLK_KP6: return SIM_KEY_KP_RIGHT; case SDLK_KP7: return SIM_KEY_KP_HOME; case SDLK_KP8: return SIM_KEY_KP_UP; case SDLK_KP9: return SIM_KEY_KP_PAGE_UP; #else case SDLK_KP_0: return SIM_KEY_KP_INSERT; case SDLK_KP_1: return SIM_KEY_KP_END; case SDLK_KP_2: return SIM_KEY_KP_DOWN; case SDLK_KP_3: return SIM_KEY_KP_PAGE_DOWN; case SDLK_KP_4: return SIM_KEY_KP_LEFT; case SDLK_KP_5: return SIM_KEY_KP_5; case SDLK_KP_6: return SIM_KEY_KP_RIGHT; case SDLK_KP_7: return SIM_KEY_KP_HOME; case SDLK_KP_8: return SIM_KEY_KP_UP; case SDLK_KP_9: return SIM_KEY_KP_PAGE_UP; #endif case SDLK_KP_PERIOD: return SIM_KEY_KP_DELETE; case SDLK_KP_DIVIDE: return SIM_KEY_KP_DIVIDE; case SDLK_KP_MULTIPLY: return SIM_KEY_KP_MULTIPLY; case SDLK_KP_MINUS: return SIM_KEY_KP_SUBTRACT; case SDLK_KP_PLUS: return SIM_KEY_KP_ADD; case SDLK_KP_ENTER: return SIM_KEY_KP_ENTER; case SDLK_UP: return SIM_KEY_UP; case SDLK_DOWN: return SIM_KEY_DOWN; case SDLK_RIGHT: return SIM_KEY_RIGHT; case SDLK_LEFT: return SIM_KEY_LEFT; case SDLK_INSERT: return SIM_KEY_INSERT; case SDLK_HOME: return SIM_KEY_HOME; case SDLK_END: return SIM_KEY_END; case SDLK_PAGEUP: return SIM_KEY_PAGE_UP; case SDLK_PAGEDOWN: return SIM_KEY_PAGE_DOWN; case SDLK_F1: return SIM_KEY_F1; case SDLK_F2: return SIM_KEY_F2; case SDLK_F3: return SIM_KEY_F3; case SDLK_F4: return SIM_KEY_F4; case SDLK_F5: return SIM_KEY_F5; case SDLK_F6: return SIM_KEY_F6; case SDLK_F7: return SIM_KEY_F7; case SDLK_F8: return SIM_KEY_F8; case SDLK_F9: return SIM_KEY_F9; case SDLK_F10: return SIM_KEY_F10; case SDLK_F11: return SIM_KEY_F11; case SDLK_F12: return SIM_KEY_F12; #if SDL_MAJOR_VERSION != 1 case SDLK_NUMLOCKCLEAR: return SIM_KEY_NUM_LOCK; #endif case SDLK_CAPSLOCK: return SIM_KEY_CAPS_LOCK; #if SDL_MAJOR_VERSION == 1 case SDLK_SCROLLOCK: return SIM_KEY_SCRL_LOCK; #else case SDLK_SCROLLLOCK: return SIM_KEY_SCRL_LOCK; #endif case SDLK_RSHIFT: return SIM_KEY_SHIFT_R; case SDLK_LSHIFT: return SIM_KEY_SHIFT_L; case SDLK_RCTRL: return SIM_KEY_CTRL_R; case SDLK_LCTRL: return SIM_KEY_CTRL_L; case SDLK_RALT: return SIM_KEY_ALT_R; case SDLK_LALT: return SIM_KEY_ALT_L; #if SDL_MAJOR_VERSION == 1 case SDLK_RMETA: return SIM_KEY_ALT_R; case SDLK_LMETA: return SIM_KEY_WIN_L; #else case SDLK_LGUI: return SIM_KEY_WIN_L; case SDLK_RGUI: return SIM_KEY_WIN_R; #endif #if SDL_MAJOR_VERSION == 1 case SDLK_PRINT: return SIM_KEY_PRINT; #else case SDLK_PRINTSCREEN: return SIM_KEY_PRINT; #endif case SDLK_PAUSE: return SIM_KEY_PAUSE; case SDLK_MENU: return SIM_KEY_MENU; default: return SIM_KEY_UNKNOWN; } } void vid_key (SDL_KeyboardEvent *event) { SIM_KEY_EVENT ev; if (vid_mouse_captured) { static const Uint8 *KeyStates = NULL; static int numkeys; if (!KeyStates) #if SDL_MAJOR_VERSION == 1 KeyStates = SDL_GetKeyState(&numkeys); if ((vid_flags & SIM_VID_INPUTCAPTURED) && (event->state == SDL_PRESSED) && KeyStates[SDLK_RSHIFT] && (KeyStates[SDLK_LCTRL] || KeyStates[SDLK_RCTRL])) { #else KeyStates = SDL_GetKeyboardState(&numkeys); if ((vid_flags & SIM_VID_INPUTCAPTURED) && (event->state == SDL_PRESSED) && KeyStates[SDL_SCANCODE_RSHIFT] && (KeyStates[SDL_SCANCODE_LCTRL] || KeyStates[SDL_SCANCODE_RCTRL])) { #endif sim_debug (SIM_VID_DBG_KEY, vid_dev, "vid_key() - Cursor Release\n"); #if SDL_MAJOR_VERSION == 1 if (SDL_WM_GrabInput (SDL_GRAB_OFF) < 0) /* relese cursor */ sim_printf ("%s: vid_key(): SDL_WM_GrabInput error: %s\n", sim_dname(vid_dev), SDL_GetError()); if (SDL_ShowCursor (SDL_ENABLE) < 0) /* show cursor */ sim_printf ("%s: vid_key(): SDL_ShowCursor error: %s\n", sim_dname(vid_dev), SDL_GetError()); #else if (SDL_SetRelativeMouseMode(SDL_FALSE) < 0) /* release cursor, show cursor */ sim_printf ("%s: vid_key(): SDL_SetRelativeMouseMode error: %s\n", sim_dname(vid_dev), SDL_GetError()); #endif vid_mouse_captured = FALSE; return; } } if (!sim_is_running) return; if (SDL_SemWait (vid_key_events.sem) == 0) { if (vid_key_events.count < MAX_EVENTS) { ev.key = vid_map_key (event->keysym.sym); sim_debug (SIM_VID_DBG_KEY, vid_dev, "Keyboard Event: State: %s, Keysym(scancode,sym): (%d,%d) - %s\n", (event->state == SDL_PRESSED) ? "PRESSED" : "RELEASED", event->keysym.scancode, event->keysym.sym, vid_key_name(ev.key)); if (event->state == SDL_PRESSED) { #if SDL_MAJOR_VERSION == 1 if (!vid_key_state[event->keysym.sym]) { /* Key was not down before */ vid_key_state[event->keysym.sym] = TRUE; #else if (!vid_key_state[event->keysym.scancode]) {/* Key was not down before */ vid_key_state[event->keysym.scancode] = TRUE; #endif ev.state = SIM_KEYPRESS_DOWN; } else ev.state = SIM_KEYPRESS_REPEAT; } else { #if SDL_MAJOR_VERSION == 1 vid_key_state[event->keysym.sym] = FALSE; #else vid_key_state[event->keysym.scancode] = FALSE; #endif ev.state = SIM_KEYPRESS_UP; } vid_key_events.events[vid_key_events.tail++] = ev; vid_key_events.count++; if (vid_key_events.tail == MAX_EVENTS) vid_key_events.tail = 0; } else { sim_debug (SIM_VID_DBG_KEY, vid_dev, "Keyboard Event DISCARDED: State: %s, Keysym: Scancode: %d, Keysym: %d\n", (event->state == SDL_PRESSED) ? "PRESSED" : "RELEASED", event->keysym.scancode, event->keysym.sym); } if (SDL_SemPost (vid_key_events.sem)) sim_printf ("%s: vid_key(): SDL_SemPost error: %s\n", sim_dname(vid_dev), SDL_GetError()); } } void vid_mouse_move (SDL_MouseMotionEvent *event) { SDL_Event dummy_event; SDL_MouseMotionEvent *dev = (SDL_MouseMotionEvent *)&dummy_event; SIM_MOUSE_EVENT ev; if ((!vid_mouse_captured) && (vid_flags & SIM_VID_INPUTCAPTURED)) return; if (!sim_is_running) return; if (!vid_cursor_visible) return; sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: pos:(%d,%d) rel:(%d,%d) buttons:(%d,%d,%d)\n", event->x, event->y, event->xrel, event->yrel, (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? 1 : 0); #if SDL_MAJOR_VERSION == 1 while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_MOUSEMOTIONMASK)) { #else while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)) { #endif /* Coalesce motion activity to avoid thrashing */ event->xrel += dev->xrel; event->yrel += dev->yrel; event->x = dev->x; event->y = dev->y; event->state = dev->state; sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: Additional Event Coalesced:pos:(%d,%d) rel:(%d,%d) buttons:(%d,%d,%d)\n", dev->x, dev->y, dev->xrel, dev->yrel, (dev->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? 1 : 0, (dev->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? 1 : 0, (dev->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? 1 : 0); }; if (SDL_SemWait (vid_mouse_events.sem) == 0) { if (!vid_mouse_captured) { event->xrel = (event->x - vid_cursor_x); event->yrel = (event->y - vid_cursor_y); } vid_mouse_b1 = (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? TRUE : FALSE; vid_mouse_b2 = (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? TRUE : FALSE; vid_mouse_b3 = (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? TRUE : FALSE; sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: pos:(%d,%d) rel:(%d,%d) buttons:(%d,%d,%d) - Count: %d vid_cursor:(%d,%d)\n", event->x, event->y, event->xrel, event->yrel, (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? 1 : 0, vid_mouse_events.count, vid_cursor_x, vid_cursor_y); if (vid_mouse_events.count < MAX_EVENTS) { SIM_MOUSE_EVENT *tail = &vid_mouse_events.events[(vid_mouse_events.tail+MAX_EVENTS-1)%MAX_EVENTS]; ev.x_rel = event->xrel; ev.y_rel = event->yrel; ev.b1_state = vid_mouse_b1; ev.b2_state = vid_mouse_b2; ev.b3_state = vid_mouse_b3; ev.x_pos = event->x; ev.y_pos = event->y; if ((vid_mouse_events.count > 0) && /* Is there a tail event? */ (ev.b1_state == tail->b1_state) && /* With the same button state? */ (ev.b2_state == tail->b2_state) && (ev.b3_state == tail->b3_state)) { /* Merge the motion */ tail->x_rel += ev.x_rel; tail->y_rel += ev.y_rel; tail->x_pos = ev.x_pos; tail->y_pos = ev.y_pos; sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: Coalesced into pending event: (%d,%d)\n", tail->x_rel, tail->y_rel); } else { /* Add a new event */ vid_mouse_events.events[vid_mouse_events.tail++] = ev; vid_mouse_events.count++; if (vid_mouse_events.tail == MAX_EVENTS) vid_mouse_events.tail = 0; } } else { sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event Discarded: Count: %d\n", vid_mouse_events.count); } if (SDL_SemPost (vid_mouse_events.sem)) sim_printf ("%s: vid_mouse_move(): SDL_SemPost error: %s\n", sim_dname(vid_dev), SDL_GetError()); } } void vid_mouse_button (SDL_MouseButtonEvent *event) { SDL_Event dummy_event; SIM_MOUSE_EVENT ev; t_bool state; if ((!vid_mouse_captured) && (vid_flags & SIM_VID_INPUTCAPTURED)) { if ((event->state == SDL_PRESSED) && (event->button == SDL_BUTTON_LEFT)) { /* left click and cursor not captured? */ sim_debug (SIM_VID_DBG_KEY, vid_dev, "vid_mouse_button() - Cursor Captured\n"); #if SDL_MAJOR_VERSION == 1 SDL_WM_GrabInput (SDL_GRAB_ON); /* lock cursor to window */ SDL_ShowCursor (SDL_DISABLE); /* hide cursor */ SDL_WarpMouse (vid_width/2, vid_height/2); /* back to center */ SDL_PumpEvents (); while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_MOUSEMOTIONMASK)) {}; #else if (SDL_SetRelativeMouseMode (SDL_TRUE) < 0) /* lock cursor to window, hide cursor */ sim_printf ("%s: vid_mouse_button(): SDL_SetRelativeMouseMode error: %s\n", sim_dname(vid_dev), SDL_GetError()); SDL_WarpMouseInWindow (NULL, vid_width/2, vid_height/2);/* back to center */ SDL_PumpEvents (); while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)) {}; #endif vid_mouse_captured = TRUE; } return; } if (!sim_is_running) return; state = (event->state == SDL_PRESSED) ? TRUE : FALSE; if (SDL_SemWait (vid_mouse_events.sem) == 0) { switch (event->button) { case SDL_BUTTON_LEFT: vid_mouse_b1 = state; break; case SDL_BUTTON_MIDDLE: vid_mouse_b2 = state; break; case SDL_BUTTON_RIGHT: vid_mouse_b3 = state; break; } sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Button Event: State: %d, Button: %d, (%d,%d)\n", event->state, event->button, event->x, event->y); if (vid_mouse_events.count < MAX_EVENTS) { ev.x_rel = 0; ev.y_rel = 0; ev.x_pos = event->x; ev.y_pos = event->y; ev.b1_state = vid_mouse_b1; ev.b2_state = vid_mouse_b2; ev.b3_state = vid_mouse_b3; vid_mouse_events.events[vid_mouse_events.tail++] = ev; vid_mouse_events.count++; if (vid_mouse_events.tail == MAX_EVENTS) vid_mouse_events.tail = 0; } else { sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Button Event Discarded: Count: %d\n", vid_mouse_events.count); } if (SDL_SemPost (vid_mouse_events.sem)) sim_printf ("%s: Mouse Button Event: SDL_SemPost error: %s\n", sim_dname(vid_dev), SDL_GetError()); } } void vid_update (void) { SDL_Rect vid_dst; vid_dst.x = 0; vid_dst.y = 0; vid_dst.w = vid_width; vid_dst.h = vid_height; sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "Video Update Event: \n"); if (sim_deb) fflush (sim_deb); #if SDL_MAJOR_VERSION == 1 if (SDL_BlitSurface (vid_image, NULL, vid_window, &vid_dst) < 0) sim_printf ("%s: vid_update(): SDL_BlitSurface error: %s\n", sim_dname(vid_dev), SDL_GetError()); SDL_UpdateRects (vid_window, 1, &vid_dst); #else if (SDL_RenderClear (vid_renderer)) sim_printf ("%s: Video Update Event: SDL_RenderClear error: %s\n", sim_dname(vid_dev), SDL_GetError()); if (SDL_RenderCopy (vid_renderer, vid_texture, NULL, NULL)) sim_printf ("%s: Video Update Event: SDL_RenderCopy error: %s\n", sim_dname(vid_dev), SDL_GetError()); SDL_RenderPresent (vid_renderer); #endif } void vid_update_cursor (SDL_Cursor *cursor, t_bool visible) { if (!cursor) return; sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "Cursor Update Event: Previously %s, Now %s, New Cursor object at: %p, Old Cursor object at: %p\n", SDL_ShowCursor(-1) ? "visible" : "invisible", visible ? "visible" : "invisible", cursor, vid_cursor); SDL_SetCursor (cursor); #if SDL_MAJOR_VERSION == 1 if (visible) SDL_WarpMouse (vid_cursor_x, vid_cursor_y);/* sync position */ #else if ((vid_window == SDL_GetMouseFocus ()) && visible) SDL_WarpMouseInWindow (NULL, vid_cursor_x, vid_cursor_y);/* sync position */ #endif if ((vid_cursor != cursor) && (vid_cursor)) SDL_FreeCursor (vid_cursor); vid_cursor = cursor; SDL_ShowCursor (visible); vid_cursor_visible = visible; } void vid_warp_position (void) { sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "Mouse Warp Event: Warp to: (%d,%d)\n", vid_cursor_x, vid_cursor_y); SDL_PumpEvents (); #if SDL_MAJOR_VERSION == 1 SDL_WarpMouse (vid_cursor_x, vid_cursor_y); #else SDL_WarpMouseInWindow (NULL, vid_cursor_x, vid_cursor_y); #endif SDL_PumpEvents (); } void vid_draw_region (SDL_UserEvent *event) { SDL_Rect *vid_dst = (SDL_Rect *)event->data1; uint32 *buf = (uint32 *)event->data2; sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "Draw Region Event: (%d,%d,%d,%d)\n", vid_dst->x, vid_dst->x, vid_dst->w, vid_dst->h); #if SDL_MAJOR_VERSION == 1 if (1) { int32 i; uint32* pixels; pixels = (uint32 *)vid_image->pixels; for (i = 0; i < vid_dst->h; i++) memcpy (pixels + ((i + vid_dst->y) * vid_width) + vid_dst->x, buf + vid_dst->w*i, vid_dst->w*sizeof(*pixels)); } #else if (SDL_UpdateTexture(vid_texture, vid_dst, buf, vid_dst->w*sizeof(*buf))) sim_printf ("%s: vid_draw() - SDL_UpdateTexture error: %s\n", sim_dname(vid_dev), SDL_GetError()); #endif free (vid_dst); free (buf); event->data1 = NULL; } int vid_video_events (void) { SDL_Event event; #if SDL_MAJOR_VERSION == 1 static const char *eventtypes[] = { "NOEVENT", /**< Unused (do not remove) */ "ACTIVEEVENT", /**< Application loses/gains visibility */ "KEYDOWN", /**< Keys pressed */ "KEYUP", /**< Keys released */ "MOUSEMOTION", /**< Mouse moved */ "MOUSEBUTTONDOWN", /**< Mouse button pressed */ "MOUSEBUTTONUP", /**< Mouse button released */ "JOYAXISMOTION", /**< Joystick axis motion */ "JOYBALLMOTION", /**< Joystick trackball motion */ "JOYHATMOTION", /**< Joystick hat position change */ "JOYBUTTONDOWN", /**< Joystick button pressed */ "JOYBUTTONUP", /**< Joystick button released */ "QUIT", /**< User-requested quit */ "SYSWMEVENT", /**< System specific event */ "EVENT_RESERVEDA", /**< Reserved for future use.. */ "EVENT_RESERVEDB", /**< Reserved for future use.. */ "VIDEORESIZE", /**< User resized video mode */ "VIDEOEXPOSE", /**< Screen needs to be redrawn */ "EVENT_RESERVED2", /**< Reserved for future use.. */ "EVENT_RESERVED3", /**< Reserved for future use.. */ "EVENT_RESERVED4", /**< Reserved for future use.. */ "EVENT_RESERVED5", /**< Reserved for future use.. */ "EVENT_RESERVED6", /**< Reserved for future use.. */ "EVENT_RESERVED7", /**< Reserved for future use.. */ "USEREVENT", /** Events SDL_USEREVENT(24) through SDL_MAXEVENTS-1(31) are for your use */ "", "", "", "", "", "", "" }; #else static const char *eventtypes[SDL_LASTEVENT]; #endif static const char *windoweventtypes[256]; static t_bool initialized = FALSE; if (!initialized) { initialized = TRUE; #if SDL_MAJOR_VERSION != 1 eventtypes[SDL_QUIT] = "QUIT"; /**< User-requested quit */ /* These application events have special meaning on iOS, see README-ios.txt for details */ eventtypes[SDL_APP_TERMINATING] = "APP_TERMINATING"; /**< The application is being terminated by the OS Called on iOS in applicationWillTerminate() Called on Android in onDestroy() */ eventtypes[SDL_APP_LOWMEMORY] = "APP_LOWMEMORY"; /**< The application is low on memory, free memory if possible. Called on iOS in applicationDidReceiveMemoryWarning() Called on Android in onLowMemory() */ eventtypes[SDL_APP_WILLENTERBACKGROUND] = "APP_WILLENTERBACKGROUND"; /**< The application is about to enter the background Called on iOS in applicationWillResignActive() Called on Android in onPause() */ eventtypes[SDL_APP_DIDENTERBACKGROUND] = "APP_DIDENTERBACKGROUND"; /**< The application did enter the background and may not get CPU for some time Called on iOS in applicationDidEnterBackground() Called on Android in onPause() */ eventtypes[SDL_APP_WILLENTERFOREGROUND] = "APP_WILLENTERFOREGROUND"; /**< The application is about to enter the foreground Called on iOS in applicationWillEnterForeground() Called on Android in onResume() */ eventtypes[SDL_APP_DIDENTERFOREGROUND] = "APP_DIDENTERFOREGROUND"; /**< The application is now interactive Called on iOS in applicationDidBecomeActive() Called on Android in onResume() */ /* Window events */ eventtypes[SDL_WINDOWEVENT] = "WINDOWEVENT"; /**< Window state change */ eventtypes[SDL_SYSWMEVENT] = "SYSWMEVENT"; /**< System specific event */ windoweventtypes[SDL_WINDOWEVENT_NONE] = "NONE"; /**< Never used */ windoweventtypes[SDL_WINDOWEVENT_SHOWN] = "SHOWN"; /**< Window has been shown */ windoweventtypes[SDL_WINDOWEVENT_HIDDEN] = "HIDDEN"; /**< Window has been hidden */ windoweventtypes[SDL_WINDOWEVENT_EXPOSED] = "EXPOSED"; /**< Window has been exposed and should be redrawn */ windoweventtypes[SDL_WINDOWEVENT_MOVED] = "MOVED"; /**< Window has been moved to data1, data2 */ windoweventtypes[SDL_WINDOWEVENT_RESIZED] = "RESIZED"; /**< Window has been resized to data1xdata2 */ windoweventtypes[SDL_WINDOWEVENT_SIZE_CHANGED] = "SIZE_CHANGED";/**< The window size has changed, either as a result of an API call or through the system or user changing the window size. */ windoweventtypes[SDL_WINDOWEVENT_MINIMIZED] = "MINIMIZED"; /**< Window has been minimized */ windoweventtypes[SDL_WINDOWEVENT_MAXIMIZED] = "MAXIMIZED"; /**< Window has been maximized */ windoweventtypes[SDL_WINDOWEVENT_RESTORED] = "RESTORED"; /**< Window has been restored to normal size and position */ windoweventtypes[SDL_WINDOWEVENT_ENTER] = "ENTER"; /**< Window has gained mouse focus */ windoweventtypes[SDL_WINDOWEVENT_LEAVE] = "LEAVE"; /**< Window has lost mouse focus */ windoweventtypes[SDL_WINDOWEVENT_FOCUS_GAINED] = "FOCUS_GAINED";/**< Window has gained keyboard focus */ windoweventtypes[SDL_WINDOWEVENT_FOCUS_LOST] = "FOCUS_LOST"; /**< Window has lost keyboard focus */ windoweventtypes[SDL_WINDOWEVENT_CLOSE] = "CLOSE"; /**< The window manager requests that the window be closed */ /* Keyboard events */ eventtypes[SDL_KEYDOWN] = "KEYDOWN"; /**< Key pressed */ eventtypes[SDL_KEYUP] = "KEYUP"; /**< Key released */ eventtypes[SDL_TEXTEDITING] = "TEXTEDITING"; /**< Keyboard text editing (composition) */ eventtypes[SDL_TEXTINPUT] = "TEXTINPUT"; /**< Keyboard text input */ /* Mouse events */ eventtypes[SDL_MOUSEMOTION] = "MOUSEMOTION"; /**< Mouse moved */ eventtypes[SDL_MOUSEBUTTONDOWN] = "MOUSEBUTTONDOWN"; /**< Mouse button pressed */ eventtypes[SDL_MOUSEBUTTONUP] = "MOUSEBUTTONUP"; /**< Mouse button released */ eventtypes[SDL_MOUSEWHEEL] = "MOUSEWHEEL"; /**< Mouse wheel motion */ /* Joystick events */ eventtypes[SDL_JOYAXISMOTION] = "JOYAXISMOTION"; /**< Joystick axis motion */ eventtypes[SDL_JOYBALLMOTION] = "JOYBALLMOTION"; /**< Joystick trackball motion */ eventtypes[SDL_JOYHATMOTION] = "JOYHATMOTION"; /**< Joystick hat position change */ eventtypes[SDL_JOYBUTTONDOWN] = "JOYBUTTONDOWN"; /**< Joystick button pressed */ eventtypes[SDL_JOYBUTTONUP] = "JOYBUTTONUP"; /**< Joystick button released */ eventtypes[SDL_JOYDEVICEADDED] = "JOYDEVICEADDED"; /**< A new joystick has been inserted into the system */ eventtypes[SDL_JOYDEVICEREMOVED] = "JOYDEVICEREMOVED"; /**< An opened joystick has been removed */ /* Game controller events */ eventtypes[SDL_CONTROLLERAXISMOTION] = "CONTROLLERAXISMOTION"; /**< Game controller axis motion */ eventtypes[SDL_CONTROLLERBUTTONDOWN] = "CONTROLLERBUTTONDOWN"; /**< Game controller button pressed */ eventtypes[SDL_CONTROLLERBUTTONUP] = "CONTROLLERBUTTONUP"; /**< Game controller button released */ eventtypes[SDL_CONTROLLERDEVICEADDED] = "CONTROLLERDEVICEADDED"; /**< A new Game controller has been inserted into the system */ eventtypes[SDL_CONTROLLERDEVICEREMOVED] = "CONTROLLERDEVICEREMOVED"; /**< An opened Game controller has been removed */ eventtypes[SDL_CONTROLLERDEVICEREMAPPED] = "CONTROLLERDEVICEREMAPPED"; /**< The controller mapping was updated */ /* Touch events */ eventtypes[SDL_FINGERDOWN] = "FINGERDOWN"; eventtypes[SDL_FINGERUP] = "FINGERUP"; eventtypes[SDL_FINGERMOTION] = "FINGERMOTION"; /* Gesture events */ eventtypes[SDL_DOLLARGESTURE] = "DOLLARGESTURE"; eventtypes[SDL_DOLLARRECORD] = "DOLLARRECORD"; eventtypes[SDL_MULTIGESTURE] = "MULTIGESTURE"; /* Clipboard events */ eventtypes[SDL_CLIPBOARDUPDATE] = "CLIPBOARDUPDATE"; /**< The clipboard changed */ /* Drag and drop events */ eventtypes[SDL_DROPFILE] = "DROPFILE"; /**< The system requests a file open */ #if (SDL_MINOR_VERSION > 0) || (SDL_PATCHLEVEL >= 3) /* Render events */ eventtypes[SDL_RENDER_TARGETS_RESET] = "RENDER_TARGETS_RESET"; /**< The render targets have been reset */ #endif #if (SDL_MINOR_VERSION > 0) || (SDL_PATCHLEVEL >= 4) /* Render events */ eventtypes[SDL_RENDER_DEVICE_RESET] = "RENDER_DEVICE_RESET"; /**< The render device has been reset */ #endif /** Events ::SDL_USEREVENT through ::SDL_LASTEVENT are for your use, * and should be allocated with SDL_RegisterEvents() */ eventtypes[SDL_USEREVENT] = "USEREVENT"; #endif /* SDL_MAJOR_VERSION != 1 */ } sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - Starting\n"); vid_mono_palette[0] = sim_end ? 0xFF000000 : 0x000000FF; /* Black */ vid_mono_palette[1] = 0xFFFFFFFF; /* White */ memset (&vid_key_state, 0, sizeof(vid_key_state)); #if SDL_MAJOR_VERSION == 1 vid_window = SDL_SetVideoMode (vid_width, vid_height, 8, 0); SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); if (sim_end) vid_image = SDL_CreateRGBSurface (SDL_SWSURFACE, vid_width, vid_height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); else vid_image = SDL_CreateRGBSurface (SDL_SWSURFACE, vid_width, vid_height, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); #else SDL_CreateWindowAndRenderer (vid_width, vid_height, SDL_WINDOW_SHOWN, &vid_window, &vid_renderer); if ((vid_window == NULL) || (vid_renderer == NULL)) { sim_printf ("%s: Error Creating Video Window: %s\n", sim_dname(vid_dev), SDL_GetError()); SDL_Quit (); return 0; } SDL_SetRenderDrawColor (vid_renderer, 0, 0, 0, 255); SDL_RenderClear (vid_renderer); SDL_RenderPresent (vid_renderer); vid_texture = SDL_CreateTexture (vid_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, vid_width, vid_height); if (!vid_texture) { sim_printf ("%s: Error configuring Video environment: %s\n", sim_dname(vid_dev), SDL_GetError()); SDL_DestroyRenderer(vid_renderer); vid_renderer = NULL; SDL_DestroyWindow(vid_window); vid_window = NULL; SDL_Quit (); return 0; } SDL_StopTextInput (); vid_windowID = SDL_GetWindowID (vid_window); #endif if (vid_flags & SIM_VID_INPUTCAPTURED) { char title[150]; memset (title, 0, sizeof(title)); strncpy (title, vid_title, sizeof(title)-1); strncat (title, " ReleaseKey=", sizeof(title)-(1+strlen(title))); strncat (title, vid_release_key, sizeof(title)-(1+strlen(title))); #if SDL_MAJOR_VERSION == 1 SDL_WM_SetCaption (title, title); #else SDL_SetWindowTitle (vid_window, title); #endif } else #if SDL_MAJOR_VERSION == 1 SDL_WM_SetCaption (vid_title, sim_name); #else SDL_SetWindowTitle (vid_window, vid_title); #endif vid_ready = TRUE; sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE|SIM_VID_DBG_CURSOR, vid_dev, "vid_thread() - Started\n"); while (vid_active) { int status = SDL_WaitEvent (&event); if (status == 1) { switch (event.type) { case SDL_KEYDOWN: case SDL_KEYUP: vid_key ((SDL_KeyboardEvent*)&event); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: vid_mouse_button ((SDL_MouseButtonEvent*)&event); break; case SDL_MOUSEMOTION: vid_mouse_move ((SDL_MouseMotionEvent*)&event); break; #if SDL_MAJOR_VERSION != 1 case SDL_WINDOWEVENT: if (event.window.windowID == vid_windowID) { sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE|SIM_VID_DBG_CURSOR, vid_dev, "vid_thread() - Window Event: %d - %s\n", event.window.event, windoweventtypes[event.window.event]); switch (event.window.event) { case SDL_WINDOWEVENT_ENTER: if (vid_flags & SIM_VID_INPUTCAPTURED) SDL_WarpMouseInWindow (NULL, vid_width/2, vid_height/2); /* center position */ break; case SDL_WINDOWEVENT_EXPOSED: vid_update (); break; } } break; #endif case SDL_USEREVENT: /* There are 6 user events generated */ /* EVENT_REDRAW to update the display */ /* EVENT_DRAW to update a region in the display texture */ /* EVENT_SHOW to display the current SDL video capabilities */ /* EVENT_CURSOR to change the current cursor */ /* EVENT_WARP to warp the cursor position */ /* EVENT_CLOSE to wake up this thread and let */ /* it notice vid_active has changed */ while (vid_active && event.user.code) { if (event.user.code == EVENT_REDRAW) { vid_update (); event.user.code = 0; /* Mark as done */ #if SDL_MAJOR_VERSION == 1 if (0) while (SDL_PeepEvents (&event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_USEREVENT))) { #else if (0) while (SDL_PeepEvents (&event, 1, SDL_GETEVENT, SDL_USEREVENT, SDL_USEREVENT)) { #endif if (event.user.code == EVENT_REDRAW) { /* Only do a single video update between waiting for events */ sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "vid_thread() - Ignored extra REDRAW Event\n"); event.user.code = 0; /* Mark as done */ continue; } break; } } if (event.user.code == EVENT_CURSOR) { vid_update_cursor ((SDL_Cursor *)(event.user.data1), (t_bool)((size_t)event.user.data2)); event.user.data1 = NULL; event.user.code = 0; /* Mark as done */ } if (event.user.code == EVENT_WARP) { vid_warp_position (); event.user.code = 0; /* Mark as done */ } if (event.user.code == EVENT_CLOSE) { event.user.code = 0; /* Mark as done */ } if (event.user.code == EVENT_DRAW) { vid_draw_region ((SDL_UserEvent*)&event); event.user.code = 0; /* Mark as done */ } if (event.user.code == EVENT_SHOW) { vid_show_video_event (); event.user.code = 0; /* Mark as done */ } if (event.user.code == EVENT_SCREENSHOT) { vid_screenshot_event (); event.user.code = 0; /* Mark as done */ } if (event.user.code == EVENT_BEEP) { vid_beep_event (); event.user.code = 0; /* Mark as done */ } if (event.user.code != 0) { sim_printf ("vid_thread(): Unexpected user event code: %d\n", event.user.code); } } break; case SDL_QUIT: sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE|SIM_VID_DBG_CURSOR, vid_dev, "vid_thread() - QUIT Event - %s\n", vid_quit_callback ? "Signaled" : "Ignored"); if (vid_quit_callback) vid_quit_callback (); break; default: sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE|SIM_VID_DBG_CURSOR, vid_dev, "vid_thread() - Ignored Event: Type: %s(%d)\n", eventtypes[event.type], event.type); break; } } else { if (status < 0) sim_printf ("%s: vid_thread() - SDL_WaitEvent error: %s\n", sim_dname(vid_dev), SDL_GetError()); } } vid_ready = FALSE; if (vid_cursor) { SDL_FreeCursor (vid_cursor); vid_cursor = NULL; } #if SDL_MAJOR_VERSION != 1 SDL_DestroyTexture(vid_texture); vid_texture = NULL; SDL_DestroyRenderer(vid_renderer); vid_renderer = NULL; SDL_DestroyWindow(vid_window); #endif /* SDL_MAJOR_VERSION != 1 */ vid_window = NULL; sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE|SIM_VID_DBG_CURSOR, vid_dev, "vid_thread() - Exiting\n"); return 0; } int vid_thread (void *arg) { #if SDL_MAJOR_VERSION == 1 _XInitThreads(); SDL_Init (SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE); #else SDL_SetHint (SDL_HINT_RENDER_DRIVER, "software"); SDL_Init (SDL_INIT_VIDEO); #endif vid_beep_setup (400, 660); vid_video_events (); vid_beep_cleanup (); SDL_Quit (); return 0; } const char *vid_version(void) { static char SDLVersion[80]; SDL_version compiled, running; #if SDL_MAJOR_VERSION == 1 const SDL_version *ver = SDL_Linked_Version(); running.major = ver->major; running.minor = ver->minor; running.patch = ver->patch; #else SDL_GetVersion(&running); #endif SDL_VERSION(&compiled); if ((compiled.major == running.major) && (compiled.minor == running.minor) && (compiled.patch == running.patch)) sprintf(SDLVersion, "SDL Version %d.%d.%d", compiled.major, compiled.minor, compiled.patch); else sprintf(SDLVersion, "SDL Version (Compiled: %d.%d.%d, Runtime: %d.%d.%d)", compiled.major, compiled.minor, compiled.patch, running.major, running.minor, running.patch); return (const char *)SDLVersion; } t_stat vid_set_release_key (FILE* st, UNIT* uptr, int32 val, CONST void* desc) { return SCPE_NOFNC; } t_stat vid_show_release_key (FILE* st, UNIT* uptr, int32 val, CONST void* desc) { if (vid_flags & SIM_VID_INPUTCAPTURED) fprintf (st, "ReleaseKey=%s", vid_release_key); return SCPE_OK; } static t_stat _vid_show_video (FILE* st, UNIT* uptr, int32 val, CONST void* desc) { int i; fprintf (st, "Video support using SDL: %s\n", vid_version()); #if defined (SDL_MAIN_AVAILABLE) fprintf (st, " SDL Events being processed on the main process thread\n"); #endif if (!vid_active) { #if !defined (SDL_MAIN_AVAILABLE) SDL_Init(SDL_INIT_VIDEO); #endif } else { fprintf (st, " Currently Active Video Window: (%d by %d pixels)\n", vid_width, vid_height); fprintf (st, " "); vid_show_release_key (st, uptr, val, desc); fprintf (st, "\n"); #if SDL_MAJOR_VERSION != 1 fprintf (st, " SDL Video Driver: %s\n", SDL_GetCurrentVideoDriver()); #endif } #if SDL_MAJOR_VERSION == 1 if (1) { char driver_name[64]; const SDL_VideoInfo *info = SDL_GetVideoInfo(); fprintf (st, " Video Driver: %s\n", SDL_VideoDriverName(driver_name, sizeof(driver_name))); fprintf (st, " hardware surfaces available: %s\n", info->hw_available ? "Yes" : "No"); fprintf (st, " window manager available: %s\n", info->wm_available ? "Yes" : "No"); fprintf (st, " hardware to hardware blits accelerated: %s\n", info->blit_hw ? "Yes" : "No"); fprintf (st, " hardware to hardware colorkey blits accelerated: %s\n", info->blit_hw_CC ? "Yes" : "No"); fprintf (st, " hardware to hardware alpha blits accelerated: %s\n", info->blit_hw_A ? "Yes" : "No"); fprintf (st, " software to hardware blits accelerated: %s\n", info->blit_sw ? "Yes" : "No"); fprintf (st, " software to hardware colorkey blits accelerated: %s\n", info->blit_sw_CC ? "Yes" : "No"); fprintf (st, " software to hardware alpha blits accelerated: %s\n", info->blit_sw_A ? "Yes" : "No"); fprintf (st, " color fills accelerated: %s\n", info->blit_fill ? "Yes" : "No"); fprintf (st, " Video Memory: %dKb\n", info->video_mem); } #else for (i = 0; i < SDL_GetNumVideoDisplays(); ++i) { SDL_DisplayMode display; if (SDL_GetCurrentDisplayMode(i, &display)) { fprintf (st, "Could not get display mode for video display #%d: %s", i, SDL_GetError()); } else { fprintf (st, " Display %s(#%d): current display mode is %dx%dpx @ %dhz. \n", SDL_GetDisplayName(i), i, display.w, display.h, display.refresh_rate); } } fprintf (st, " Available SDL Renderers:\n"); for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) { SDL_RendererInfo info; if (SDL_GetRenderDriverInfo (i, &info)) { fprintf (st, "Could not get render driver info for driver #%d: %s", i, SDL_GetError()); } else { uint32 j, k; static struct {uint32 format; const char *name;} PixelFormats[] = { {SDL_PIXELFORMAT_INDEX1LSB, "Index1LSB"}, {SDL_PIXELFORMAT_INDEX1MSB, "Index1MSB"}, {SDL_PIXELFORMAT_INDEX4LSB, "Index4LSB"}, {SDL_PIXELFORMAT_INDEX4MSB, "Index4MSB"}, {SDL_PIXELFORMAT_INDEX8, "Index8"}, {SDL_PIXELFORMAT_RGB332, "RGB332"}, {SDL_PIXELFORMAT_RGB444, "RGB444"}, {SDL_PIXELFORMAT_RGB555, "RGB555"}, {SDL_PIXELFORMAT_BGR555, "BGR555"}, {SDL_PIXELFORMAT_ARGB4444, "ARGB4444"}, {SDL_PIXELFORMAT_RGBA4444, "RGBA4444"}, {SDL_PIXELFORMAT_ABGR4444, "ABGR4444"}, {SDL_PIXELFORMAT_BGRA4444, "BGRA4444"}, {SDL_PIXELFORMAT_ARGB1555, "ARGB1555"}, {SDL_PIXELFORMAT_RGBA5551, "RGBA5551"}, {SDL_PIXELFORMAT_ABGR1555, "ABGR1555"}, {SDL_PIXELFORMAT_BGRA5551, "BGRA5551"}, {SDL_PIXELFORMAT_RGB565, "RGB565"}, {SDL_PIXELFORMAT_BGR565, "BGR565"}, {SDL_PIXELFORMAT_RGB24, "RGB24"}, {SDL_PIXELFORMAT_BGR24, "BGR24"}, {SDL_PIXELFORMAT_RGB888, "RGB888"}, {SDL_PIXELFORMAT_RGBX8888, "RGBX8888"}, {SDL_PIXELFORMAT_BGR888, "BGR888"}, {SDL_PIXELFORMAT_BGRX8888, "BGRX8888"}, {SDL_PIXELFORMAT_ARGB8888, "ARGB8888"}, {SDL_PIXELFORMAT_RGBA8888, "RGBA8888"}, {SDL_PIXELFORMAT_ABGR8888, "ABGR8888"}, {SDL_PIXELFORMAT_BGRA8888, "BGRA8888"}, {SDL_PIXELFORMAT_ARGB2101010, "ARGB2101010"}, {SDL_PIXELFORMAT_YV12, "YV12"}, {SDL_PIXELFORMAT_IYUV, "IYUV"}, {SDL_PIXELFORMAT_YUY2, "YUY2"}, {SDL_PIXELFORMAT_UYVY, "UYVY"}, {SDL_PIXELFORMAT_YVYU, "YVYU"}, {SDL_PIXELFORMAT_UNKNOWN, "Unknown"}}; fprintf (st, " Render #%d - %s\n", i, info.name); fprintf (st, " Flags: 0x%X - ", info.flags); if (info.flags & SDL_RENDERER_SOFTWARE) fprintf (st, "Software|"); if (info.flags & SDL_RENDERER_ACCELERATED) fprintf (st, "Accelerated|"); if (info.flags & SDL_RENDERER_PRESENTVSYNC) fprintf (st, "PresentVSync|"); if (info.flags & SDL_RENDERER_TARGETTEXTURE) fprintf (st, "TargetTexture|"); fprintf (st, "\n"); if ((info.max_texture_height != 0) || (info.max_texture_width != 0)) fprintf (st, " Max Texture: %d by %d\n", info.max_texture_height, info.max_texture_width); fprintf (st, " Pixel Formats:\n"); for (j=0; j<info.num_texture_formats; j++) { for (k=0; 1; k++) { if (PixelFormats[k].format == info.texture_formats[j]) { fprintf (st, " %s\n", PixelFormats[k].name); break; } if (PixelFormats[k].format == SDL_PIXELFORMAT_UNKNOWN) { fprintf (st, " %s - 0x%X\n", PixelFormats[k].name, info.texture_formats[j]); break; } } } } } if (vid_active) { SDL_RendererInfo info; SDL_GetRendererInfo (vid_renderer, &info); fprintf (st, " Currently Active Renderer: %s\n", info.name); } if (1) { static const char *hints[] = { #if defined (SDL_HINT_FRAMEBUFFER_ACCELERATION) SDL_HINT_FRAMEBUFFER_ACCELERATION , #endif #if defined (SDL_HINT_RENDER_DRIVER) SDL_HINT_RENDER_DRIVER , #endif #if defined (SDL_HINT_RENDER_OPENGL_SHADERS) SDL_HINT_RENDER_OPENGL_SHADERS , #endif #if defined (SDL_HINT_RENDER_DIRECT3D_THREADSAFE) SDL_HINT_RENDER_DIRECT3D_THREADSAFE , #endif #if defined (SDL_HINT_RENDER_DIRECT3D11_DEBUG) SDL_HINT_RENDER_DIRECT3D11_DEBUG , #endif #if defined (SDL_HINT_RENDER_SCALE_QUALITY) SDL_HINT_RENDER_SCALE_QUALITY , #endif #if defined (SDL_HINT_RENDER_VSYNC) SDL_HINT_RENDER_VSYNC , #endif #if defined (SDL_HINT_VIDEO_ALLOW_SCREENSAVER) SDL_HINT_VIDEO_ALLOW_SCREENSAVER , #endif #if defined (SDL_HINT_VIDEO_X11_XVIDMODE) SDL_HINT_VIDEO_X11_XVIDMODE , #endif #if defined (SDL_HINT_VIDEO_X11_XINERAMA) SDL_HINT_VIDEO_X11_XINERAMA , #endif #if defined (SDL_HINT_VIDEO_X11_XRANDR) SDL_HINT_VIDEO_X11_XRANDR , #endif #if defined (SDL_HINT_GRAB_KEYBOARD) SDL_HINT_GRAB_KEYBOARD , #endif #if defined (SDL_HINT_MOUSE_RELATIVE_MODE_WARP) SDL_HINT_MOUSE_RELATIVE_MODE_WARP , #endif #if defined (SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS) SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS , #endif #if defined (SDL_HINT_IDLE_TIMER_DISABLED) SDL_HINT_IDLE_TIMER_DISABLED , #endif #if defined (SDL_HINT_ORIENTATIONS) SDL_HINT_ORIENTATIONS , #endif #if defined (SDL_HINT_ACCELEROMETER_AS_JOYSTICK) SDL_HINT_ACCELEROMETER_AS_JOYSTICK , #endif #if defined (SDL_HINT_XINPUT_ENABLED) SDL_HINT_XINPUT_ENABLED , #endif #if defined (SDL_HINT_GAMECONTROLLERCONFIG) SDL_HINT_GAMECONTROLLERCONFIG , #endif #if defined (SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS) SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS , #endif #if defined (SDL_HINT_ALLOW_TOPMOST) SDL_HINT_ALLOW_TOPMOST , #endif #if defined (SDL_HINT_TIMER_RESOLUTION) SDL_HINT_TIMER_RESOLUTION , #endif #if defined (SDL_HINT_VIDEO_HIGHDPI_DISABLED) SDL_HINT_VIDEO_HIGHDPI_DISABLED , #endif #if defined (SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK) SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK , #endif #if defined (SDL_HINT_VIDEO_WIN_D3DCOMPILER) SDL_HINT_VIDEO_WIN_D3DCOMPILER , #endif #if defined (SDL_HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT) SDL_HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT , #endif #if defined (SDL_HINT_WINRT_PRIVACY_POLICY_URL) SDL_HINT_WINRT_PRIVACY_POLICY_URL , #endif #if defined (SDL_HINT_WINRT_PRIVACY_POLICY_LABEL) SDL_HINT_WINRT_PRIVACY_POLICY_LABEL , #endif #if defined (SDL_HINT_WINRT_HANDLE_BACK_BUTTON) SDL_HINT_WINRT_HANDLE_BACK_BUTTON , #endif #if defined (SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES) SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, #endif NULL}; fprintf (st, " Currently Active SDL Hints:\n"); for (i=0; hints[i]; i++) { if (SDL_GetHint (hints[i])) fprintf (st, " %s = %s\n", hints[i], SDL_GetHint (hints[i])); } } #endif /* SDL_MAJOR_VERSION != 1 */ #if !defined (SDL_MAIN_AVAILABLE) if (!vid_active) SDL_Quit(); #endif return SCPE_OK; } static t_stat _show_stat; static FILE *_show_st; static UNIT *_show_uptr; static int32 _show_val; static CONST void *_show_desc; void vid_show_video_event (void) { _show_stat = _vid_show_video (_show_st, _show_uptr, _show_val, _show_desc); } t_stat vid_show_video (FILE* st, UNIT* uptr, int32 val, CONST void* desc) { SDL_Event user_event; _show_stat = -1; _show_st = st; _show_uptr = uptr; _show_val = val; _show_desc = desc; user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_SHOW; user_event.user.data1 = NULL; user_event.user.data2 = NULL; #if defined (SDL_MAIN_AVAILABLE) while (SDL_PushEvent (&user_event) < 0) sim_os_ms_sleep (10); #else vid_show_video_event (); #endif while (_show_stat == -1) SDL_Delay (20); return _show_stat; } static t_stat _vid_screenshot (const char *filename) { int stat; char *fullname = NULL; if (!vid_active) { sim_printf ("No video display is active\n"); return SCPE_UDIS | SCPE_NOMESSAGE; } fullname = (char *)malloc (strlen(filename) + 5); if (!filename) return SCPE_MEM; #if SDL_MAJOR_VERSION == 1 #if defined(HAVE_LIBPNG) if (!match_ext (filename, "bmp")) { sprintf (fullname, "%s%s", filename, match_ext (filename, "png") ? "" : ".png"); stat = SDL_SavePNG(vid_image, fullname); } else { sprintf (fullname, "%s", filename); stat = SDL_SaveBMP(vid_image, fullname); } #else sprintf (fullname, "%s%s", filename, match_ext (filename, "bmp") ? "" : ".bmp"); stat = SDL_SaveBMP(vid_image, fullname); #endif /* defined(HAVE_LIBPNG) */ #else /* SDL_MAJOR_VERSION != 1 */ if (1) { SDL_Surface *sshot = sim_end ? SDL_CreateRGBSurface(0, vid_width, vid_height, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000) : SDL_CreateRGBSurface(0, vid_width, vid_height, 32, 0x0000ff00, 0x000ff000, 0xff000000, 0x000000ff) ; SDL_RenderReadPixels(vid_renderer, NULL, SDL_PIXELFORMAT_ARGB8888, sshot->pixels, sshot->pitch); #if defined(HAVE_LIBPNG) if (!match_ext (filename, "bmp")) { sprintf (fullname, "%s%s", filename, match_ext (filename, "png") ? "" : ".png"); stat = SDL_SavePNG(sshot, fullname); } else { sprintf (fullname, "%s", filename); stat = SDL_SaveBMP(sshot, fullname); } #else sprintf (fullname, "%s%s", filename, match_ext (filename, "bmp") ? "" : ".bmp"); stat = SDL_SaveBMP(sshot, fullname); #endif /* defined(HAVE_LIBPNG) */ SDL_FreeSurface(sshot); } #endif if (stat) { sim_printf ("Error saving screenshot to %s: %s\n", fullname, SDL_GetError()); free (fullname); return SCPE_IOERR | SCPE_NOMESSAGE; } else { if (!sim_quiet) sim_printf ("Screenshot saved to %s\n", fullname); free (fullname); return SCPE_OK; } } static t_stat _screenshot_stat; static const char *_screenshot_filename; void vid_screenshot_event (void) { _screenshot_stat = _vid_screenshot (_screenshot_filename); } t_stat vid_screenshot (const char *filename) { SDL_Event user_event; _screenshot_stat = -1; _screenshot_filename = filename; user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_SCREENSHOT; user_event.user.data1 = NULL; user_event.user.data2 = NULL; #if defined (SDL_MAIN_AVAILABLE) while (SDL_PushEvent (&user_event) < 0) sim_os_ms_sleep (10); #else vid_screenshot_event (); #endif while (_screenshot_stat == -1) SDL_Delay (20); return _screenshot_stat; } #include <SDL_audio.h> #include <math.h> const int AMPLITUDE = 20000; const int SAMPLE_FREQUENCY = 11025; static int16 *vid_beep_data; static int vid_beep_offset; static int vid_beep_duration; static int vid_beep_samples; static void vid_audio_callback(void *ctx, Uint8 *stream, int length) { int16 *data = (int16 *)stream; int i, sum, remnant = ((vid_beep_samples - vid_beep_offset) * sizeof (*vid_beep_data)); if (length > remnant) { memset (stream + remnant, 0, length - remnant); length = remnant; if (remnant == 0) { SDL_PauseAudio(1); return; } } memcpy (stream, &vid_beep_data[vid_beep_offset], length); for (i=sum=0; i<length; i++) sum += stream[i]; vid_beep_offset += length / sizeof(*vid_beep_data); } static void vid_beep_setup (int duration_ms, int tone_frequency) { if (!vid_beep_data) { int i; SDL_AudioSpec desiredSpec; memset (&desiredSpec, 0, sizeof(desiredSpec)); desiredSpec.freq = SAMPLE_FREQUENCY; desiredSpec.format = AUDIO_S16SYS; desiredSpec.channels = 1; desiredSpec.samples = 2048; desiredSpec.callback = vid_audio_callback; SDL_OpenAudio(&desiredSpec, NULL); vid_beep_samples = (int)((SAMPLE_FREQUENCY * duration_ms) / 1000.0); vid_beep_duration = duration_ms; vid_beep_data = (int16 *)malloc (sizeof(*vid_beep_data) * vid_beep_samples); for (i=0; i<vid_beep_samples; i++) vid_beep_data[i] = (int16)(AMPLITUDE * sin(((double)(i * M_PI * tone_frequency)) / SAMPLE_FREQUENCY)); } } static void vid_beep_cleanup (void) { SDL_CloseAudio(); free (vid_beep_data); vid_beep_data = NULL; } void vid_beep_event (void) { vid_beep_offset = 0; /* reset to beginning of sample set */ SDL_PauseAudio (0); /* Play sound */ } void vid_beep (void) { SDL_Event user_event; user_event.type = SDL_USEREVENT; user_event.user.code = EVENT_BEEP; user_event.user.data1 = NULL; user_event.user.data2 = NULL; #if defined (SDL_MAIN_AVAILABLE) while (SDL_PushEvent (&user_event) < 0) sim_os_ms_sleep (10); #else vid_beep_event (); #endif SDL_Delay (vid_beep_duration + 100);/* Wait for sound to finnish */ } #else /* !(defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL)) */ /* Non-implemented versions */ uint32 vid_mono_palette[2]; /* Monochrome Color Map */ t_stat vid_open (DEVICE *dptr, const char *title, uint32 width, uint32 height, int flags) { return SCPE_NOFNC; } t_stat vid_close (void) { return SCPE_OK; } t_stat vid_poll_kb (SIM_KEY_EVENT *ev) { return SCPE_EOF; } t_stat vid_poll_mouse (SIM_MOUSE_EVENT *ev) { return SCPE_EOF; } void vid_draw (int32 x, int32 y, int32 w, int32 h, uint32 *buf) { return; } t_stat vid_set_cursor (t_bool visible, uint32 width, uint32 height, uint8 *data, uint8 *mask, uint32 hot_x, uint32 hot_y) { return SCPE_NOFNC; } void vid_set_cursor_position (int32 x, int32 y) { return; } void vid_refresh (void) { return; } void vid_beep (void) { return; } const char *vid_version (void) { return "No Video Support"; } t_stat vid_set_release_key (FILE* st, UNIT* uptr, int32 val, CONST void* desc) { return SCPE_NOFNC; } t_stat vid_show_release_key (FILE* st, UNIT* uptr, int32 val, CONST void* desc) { fprintf (st, "no release key"); return SCPE_OK; } t_stat vid_show_video (FILE* st, UNIT* uptr, int32 val, CONST void* desc) { fprintf (st, "video support unavailable"); return SCPE_OK; } t_stat vid_screenshot (const char *filename) { sim_printf ("video support unavailable\n"); return SCPE_NOFNC|SCPE_NOMESSAGE; } #endif /* defined(USE_SIM_VIDEO) */ |
Added src/SIMH/sim_video.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 | /* sim_video.c: Bitmap video output Copyright (c) 2011-2013, Matt Burke 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. 08-Nov-2013 MB Added globals for current mouse status 11-Jun-2013 MB First version */ #ifndef SIM_VIDEO_H_ #define SIM_VIDEO_H_ 0 #include "sim_defs.h" #ifdef __cplusplus extern "C" { #endif #define SIM_KEYPRESS_DOWN 0 /* key states */ #define SIM_KEYPRESS_UP 1 #define SIM_KEYPRESS_REPEAT 2 #define SIM_KEY_F1 0 /* key syms */ #define SIM_KEY_F2 1 #define SIM_KEY_F3 2 #define SIM_KEY_F4 3 #define SIM_KEY_F5 4 #define SIM_KEY_F6 5 #define SIM_KEY_F7 6 #define SIM_KEY_F8 7 #define SIM_KEY_F9 8 #define SIM_KEY_F10 9 #define SIM_KEY_F11 10 #define SIM_KEY_F12 11 #define SIM_KEY_0 12 #define SIM_KEY_1 13 #define SIM_KEY_2 14 #define SIM_KEY_3 15 #define SIM_KEY_4 16 #define SIM_KEY_5 17 #define SIM_KEY_6 18 #define SIM_KEY_7 19 #define SIM_KEY_8 20 #define SIM_KEY_9 21 #define SIM_KEY_A 22 #define SIM_KEY_B 23 #define SIM_KEY_C 24 #define SIM_KEY_D 25 #define SIM_KEY_E 26 #define SIM_KEY_F 27 #define SIM_KEY_G 28 #define SIM_KEY_H 29 #define SIM_KEY_I 30 #define SIM_KEY_J 31 #define SIM_KEY_K 32 #define SIM_KEY_L 33 #define SIM_KEY_M 34 #define SIM_KEY_N 35 #define SIM_KEY_O 36 #define SIM_KEY_P 37 #define SIM_KEY_Q 38 #define SIM_KEY_R 39 #define SIM_KEY_S 40 #define SIM_KEY_T 41 #define SIM_KEY_U 42 #define SIM_KEY_V 43 #define SIM_KEY_W 44 #define SIM_KEY_X 45 #define SIM_KEY_Y 46 #define SIM_KEY_Z 47 #define SIM_KEY_BACKQUOTE 48 #define SIM_KEY_MINUS 49 #define SIM_KEY_EQUALS 50 #define SIM_KEY_LEFT_BRACKET 51 #define SIM_KEY_RIGHT_BRACKET 52 #define SIM_KEY_SEMICOLON 53 #define SIM_KEY_SINGLE_QUOTE 54 #define SIM_KEY_BACKSLASH 55 #define SIM_KEY_LEFT_BACKSLASH 56 #define SIM_KEY_COMMA 57 #define SIM_KEY_PERIOD 58 #define SIM_KEY_SLASH 59 #define SIM_KEY_PRINT 60 #define SIM_KEY_SCRL_LOCK 61 #define SIM_KEY_PAUSE 62 #define SIM_KEY_ESC 63 #define SIM_KEY_BACKSPACE 64 #define SIM_KEY_TAB 65 #define SIM_KEY_ENTER 66 #define SIM_KEY_SPACE 67 #define SIM_KEY_INSERT 68 #define SIM_KEY_DELETE 69 #define SIM_KEY_HOME 70 #define SIM_KEY_END 71 #define SIM_KEY_PAGE_UP 72 #define SIM_KEY_PAGE_DOWN 73 #define SIM_KEY_UP 74 #define SIM_KEY_DOWN 75 #define SIM_KEY_LEFT 76 #define SIM_KEY_RIGHT 77 #define SIM_KEY_CAPS_LOCK 78 #define SIM_KEY_NUM_LOCK 79 #define SIM_KEY_ALT_L 80 #define SIM_KEY_ALT_R 81 #define SIM_KEY_CTRL_L 82 #define SIM_KEY_CTRL_R 83 #define SIM_KEY_SHIFT_L 84 #define SIM_KEY_SHIFT_R 85 #define SIM_KEY_WIN_L 86 #define SIM_KEY_WIN_R 87 #define SIM_KEY_MENU 88 #define SIM_KEY_KP_ADD 89 #define SIM_KEY_KP_SUBTRACT 90 #define SIM_KEY_KP_END 91 #define SIM_KEY_KP_DOWN 92 #define SIM_KEY_KP_PAGE_DOWN 93 #define SIM_KEY_KP_LEFT 94 #define SIM_KEY_KP_RIGHT 95 #define SIM_KEY_KP_HOME 96 #define SIM_KEY_KP_UP 97 #define SIM_KEY_KP_PAGE_UP 98 #define SIM_KEY_KP_INSERT 99 #define SIM_KEY_KP_DELETE 100 #define SIM_KEY_KP_5 101 #define SIM_KEY_KP_ENTER 102 #define SIM_KEY_KP_MULTIPLY 103 #define SIM_KEY_KP_DIVIDE 104 #define SIM_KEY_UNKNOWN 200 struct mouse_event { int32 x_rel; /* X axis relative motion */ int32 y_rel; /* Y axis relative motion */ int32 x_pos; /* X axis position */ int32 y_pos; /* Y axis position */ t_bool b1_state; /* state of button 1 */ t_bool b2_state; /* state of button 2 */ t_bool b3_state; /* state of button 3 */ }; struct key_event { uint32 key; /* key sym */ uint32 state; /* key state change */ }; typedef struct mouse_event SIM_MOUSE_EVENT; typedef struct key_event SIM_KEY_EVENT; t_stat vid_open (DEVICE *dptr, const char *title, uint32 width, uint32 height, int flags); #define SIM_VID_INPUTCAPTURED 1 /* Mouse and Keyboard input captured (calling */ /* code responsible for cursor display in video) */ typedef void (*VID_QUIT_CALLBACK)(void); t_stat vid_register_quit_callback (VID_QUIT_CALLBACK callback); t_stat vid_close (void); t_stat vid_poll_kb (SIM_KEY_EVENT *ev); t_stat vid_poll_mouse (SIM_MOUSE_EVENT *ev); void vid_draw (int32 x, int32 y, int32 w, int32 h, uint32 *buf); void vid_beep (void); void vid_refresh (void); const char *vid_version (void); const char *vid_key_name (int32 key); t_stat vid_set_cursor (t_bool visible, uint32 width, uint32 height, uint8 *data, uint8 *mask, uint32 hot_x, uint32 hot_y); t_stat vid_set_release_key (FILE* st, UNIT* uptr, int32 val, CONST void* desc); t_stat vid_show_release_key (FILE* st, UNIT* uptr, int32 val, CONST void* desc); t_stat vid_show_video (FILE* st, UNIT* uptr, int32 val, CONST void* desc); t_stat vid_show (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc); t_stat vid_screenshot (const char *filename); extern t_bool vid_active; extern uint32 vid_mono_palette[2]; void vid_set_cursor_position (int32 x, int32 y); /* cursor position (set by calling code) */ #define SIM_VID_DBG_MOUSE 0x01000000 #define SIM_VID_DBG_CURSOR 0x02000000 #define SIM_VID_DBG_KEY 0x04000000 #define SIM_VID_DBG_VIDEO 0x08000000 #ifdef __cplusplus } #endif #if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL) #include <SDL.h> #endif /* HAVE_LIBSDL */ #endif |
Changes to src/cc8/README.md.
︙ | ︙ | |||
46 47 48 49 50 51 52 | <a id="cross" name="posix"></a> # The Cross-Compiler The code for this is in the `cross` subdirectory, and is built along with the top-level PiDP-8/I software. When installed, it is in your `PATH` as `cc8`. | | > > > > > | < > > | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | <a id="cross" name="posix"></a> # The Cross-Compiler The code for this is in the `cross` subdirectory, and is built along with the top-level PiDP-8/I software. When installed, it is in your `PATH` as `cc8`. The CC8 cross-compiler is based upon Ron Cain’s famous Small-C compiler. This means it more or less adheres to the dialect of C as published in "The C Programming Language," by Kernighan and Ritche, first edition, 1978. The known limitations are [listed below](#cross-fl). The reader is directed to the extensive documentation of Small-C available on the web for further details. You may also find references for K&R C 1978 helpful. The key file is the PDP-8 code generator in `code8.c` which emits SABR — Symbolic Assembler for Binary Relocatable programmes — assembly code. SABR is normally used as the second pass of the OS/8 FORTRAN II system. When you use the cross-compiler on a POSIX type system such as the Raspbian PiDP-8/I environment, the resulting `*.sb` files will have |
︙ | ︙ | |||
249 250 251 252 253 254 255 256 257 258 259 260 261 262 | The features of the cross-compiler are basically that of Small-C itself, the primary difference being in the PDP-8 SABR code generator, which doesn't affect its C language support. A good approximation is K&R C (1978) minus: * `struct` and `union` * function pointers * `float` and `long` | > > | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | The features of the cross-compiler are basically that of Small-C itself, the primary difference being in the PDP-8 SABR code generator, which doesn't affect its C language support. A good approximation is K&R C (1978) minus: * most of the standard library * `struct` and `union` * function pointers * `float` and `long` |
︙ | ︙ | |||
342 343 344 345 346 347 348 | 1. The language is typeless in that everything is a 12 bit integer and any variable/array can interpreted as `int`, `char` or pointer. All variables and arrays must be declared as `int`. The return type may be left off of a function's definition; it is implicitly `int` in all cases, since `void` is not supported. | > > > > > > > > > > > > > > > > > > > > > | | > > > > > | 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 | 1. The language is typeless in that everything is a 12 bit integer and any variable/array can interpreted as `int`, `char` or pointer. All variables and arrays must be declared as `int`. The return type may be left off of a function's definition; it is implicitly `int` in all cases, since `void` is not supported. Further to this point, int the OS/8 version of CC8, it is optional to declare the types of the arguments to a function. For example, the following is likely to be rejected by a strictly conforming K&R C compiler, but it is legal in OS/8 CC8 because the types are already known, there being only one data tyype in OS/8 CC8: myfn(n) { /* do something with n; optionally return something */ } This declares a function taking an `int` called `n` and returning an `int`. Contrast the CC8 cross-compiler, which requires the function's argument type to be declared, if not the return type: myfn(n) int n; { /* do something with n; optionally return something */ } (The return type cannot be `void` since there is no `void` in K&R C as published in 1978, thus not in CC8, either.) 2. There must be an `int main()`, and it must be the last function in the single input C file. Since OS/8 has no way to pass command line arguments to a program — at least, not in a way that is compatible with the Unix style command lines expected by C — the `main()` function is never declared to take arguments. 3. We do not yet support separate compilation of multiple C modules that get linked together. You can produce relocatable libraries in OS/8 `*.RL` format and link them with the OS/8 LOADER, but because of the previous limitation, only one of these can be written in C. 4. Unlike the CC8 cross-compiler, the OS/8 compiler currently ignores |
︙ | ︙ |
Changes to src/cc8/cross/main.c.
︙ | ︙ | |||
160 161 162 163 164 165 166 167 168 | /* * parse top level declarations */ dodcls(stclass) int stclass; { blanks(); if (amatch("char", 4)) | > > > | | > | | 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 | /* * parse top level declarations */ dodcls(stclass) int stclass; { int rsl; blanks(); if (amatch("char", 4)) rsl=declglb(CCHAR, stclass); else if (amatch("int", 3)) rsl=declglb(CINT, stclass); else if (stclass == PUBLIC) return(0); else declglb(CINT, stclass); if (rsl) ns (); return(1); } /* * dump the literal pool */ |
︙ | ︙ |
Changes to src/cc8/cross/sym.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 | j = POINTER; else j = VARIABLE; if (!symname (sname)) illname (); if (findglb (sname)) multidef (sname); if (match ("[")) { k = needsub (); if (k || stor == EXTERN) j = ARRAY; else j = POINTER; } | > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | j = POINTER; else j = VARIABLE; if (!symname (sname)) illname (); if (findglb (sname)) multidef (sname); if (match ("(")) { lptr -= strlen(sname)+1; newfunc(); return 0; } if (match ("[")) { k = needsub (); if (k || stor == EXTERN) j = ARRAY; else j = POINTER; } |
︙ | ︙ |
Changes to src/cc8/examples/basic.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 | char B [BMAX]; /* command input buffer */ char F [2]; /* temporary search string */ char *m [LXMX]; /* pointers to lines of program. This is a real waste of space! */ char *p,*q,*x,*y,*z,*s,*d; | | | | | | | | | | | 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 | char B [BMAX]; /* command input buffer */ char F [2]; /* temporary search string */ char *m [LXMX]; /* pointers to lines of program. This is a real waste of space! */ char *p,*q,*x,*y,*z,*s,*d; int G( ) { /* get program line from buffer */ atoi(B,&l); y=m[l]; if(y){ if(strstr(B," ")) strcpy(y,B); else y=m[l]=0; return; } y=Lb; while(*y) y=y+DMAX; strcpy(y,B); m [l]=y; } /* end G */ /* recursive descent parser for arithmetic/logical expressions */ int S( ) { int o; o=J( ); switch(*p++){ case '=': return o==S( ); break; case '#': return o!=S( ); default: p--; return o; } } /* end S */ int J( ) { int o; o=K( ); switch(*p++){ case '<': return o<J( ); break; case '>': return o>J( ); default: p--; return o; } } /* end J */ int K( ) { int o; o=V( ); switch(*p++){ case '$': return o<=K( ); break; case '!': return o>=K( ); default: p--; return o; } } /* end K */ int V( ) { int o; o=W( ); switch(*p++){ case '+': return o+V( ); break; case '-': return o-V( ); default: p--; return o; } } /* end V */ int W( ) { int o; o=Y( ); switch(*p++){ case '*': return o*W( ); break; case '/': return o/W( ); default: p--; return o; } } /* end W */ int Y( ) { int o; if(*p=='-'){ p++; return -Y(); } q=p; if(*p>='0'&&*p<='9'){ while(*p>='0'&&*p<='9') p++; atoi(q,&o); return o; } if(*p=='('){ p++; o=S( ); p++; return o; } return P [*p++]; } /* end Y */ int bufclear() { memset(m,0,LXMX); memset(Lb,0,CBMX); } int main( ) { int tmp; /* temp var to fix bug 07Sep2005 Somos */ bufclear(); while(puts("Ok\r\n"),gets(B)) switch(*B){ case 'R': /* "RUN" command */ C=E; |
︙ | ︙ |
Changes to src/cc8/examples/calc.c.
1 2 3 | #include <init.h> #include <libc.h> | | | 1 2 3 4 5 6 7 8 9 10 11 | #include <init.h> #include <libc.h> int main() { int x, y, ans; int choice; int bfr[10]; /* CC8 doesn't let you initialize variables at declaration time. */ ans = 'Y'; |
︙ | ︙ |
Changes to src/cc8/examples/fib.c.
1 2 3 | #include <init.h> #include <libc.h> | | > | | | > | > > > > > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include <init.h> #include <libc.h> int fib(n) int n; { if (n < 2) return n; else return fib(n - 1) + fib(n - 2); } int main() { int i, rsl; i = 1; while (1) { rsl = fib(i); if (rsl < 0) { printf("Overflow:%d\r\n", i); break; } printf("Fib #%d = %d\r\n", i, rsl); i++; } } |
Changes to src/cc8/examples/ps.c.
1 2 3 | #include <init.h> #include <libc.h> | | | 1 2 3 4 5 6 7 8 9 10 11 | #include <init.h> #include <libc.h> int main() { int ar[20],i,j,n; n=14; for (i=1;i<n;i++) { ar[i]=1; for (j=i-1;j>1;j--) |
︙ | ︙ |
Changes to src/cc8/include/libc.h.
︙ | ︙ | |||
22 23 24 25 26 27 28 | * Please note: no function declarations are made, so make sure the * arg lists are correct in the calling code. * */ #define itoa libc0 #define puts libc1 | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | * Please note: no function declarations are made, so make sure the * arg lists are correct in the calling code. * */ #define itoa libc0 #define puts libc1 #define dispxy libc2 #define getc libc3 #define gets libc4 #define atoi libc5 #define sscanf vlibc6 #define xinit libc7 #define memcpy libc8 #define kbhit libc9 |
︙ | ︙ |
Deleted src/gpio-common.c.in.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/gpio-common.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/gpio-ils.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/gpio-nls.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added src/misc/ptp2txt.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | /* * Program to convert between POSIX ASCII text files * and the output of OS/8 PIP to the Paper Tape Punch. * The OS/8 paper tape punch format is: * * leader: a bunch of ASCII NUL chars to be ignored. * ASCII with the 8th bit set, CR+LF line endings. * trailer: a bunch of ASCII NUL chars to be ignored. * This program can be used as a filter from stdin to stdout or * it will create a new file with name ending in .txt if going to * POSIX text or .ptp if going to OS/8 PIP Paper Tape format. * If the program is called with the name "txt2ptp" then * LTCOUNT (default 100) bytes of leader is prepended to the * output file and LTCOUNT bytes of leader are appended. * The 8th bit of every output character is set, and LF-only * input is turned into CR+LF output. CR+LF input is passed * as-is. * If called by any other name, the ASCII NUL character is * ignored anywhere in the file, and the 8th bit is cleared. * Line endings are untouched in this case. * This program helps work around the issue that the * OS/8 Paper Tape reader handler assumes the last * character in the buffer is junk, so that when you send * a plain text file into PDP-8 SIMH OS/8 with the * ptr device, the last character is lost. */ /* * Author: Bill Cattey * License: The SIMH License: * Copyright © 2015-2017 * by Bill Cattey et. ux. William Cattey et. ux. Poetnerd * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be include * in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * Except as contained in this notice, the names of the authors * above shall not be used in advertising or otherwise to promote * the sale, use or other dealings in this Software without * prior written authorization from those authors. */ #include <errno.h> #include <fcntl.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libgen.h> #define BLOCK_SIZE 256 #define TO_PTP 1 #define TO_TXT 2 #define LTCHAR '\0' #define LTCOUNT 100 /* global variable: ltbuf */ int global_ltbuf[LTCOUNT]; void make_txt (FILE *fpin, FILE *fpout) { int inchar, outchar; int read_ct, n; char *obuffp; char ibuff[BLOCK_SIZE], obuff[BLOCK_SIZE]; while ((read_ct = fread (ibuff, sizeof(char), BLOCK_SIZE, fpin))) { obuffp = obuff; for (n = 0; n < read_ct; n++) { inchar = *(ibuff + n); if (inchar == LTCHAR) continue; *obuffp++ = inchar & 0177; } fwrite (obuff, sizeof(char), obuffp - obuff, fpout); } } /* We could just create an empty buffer and output it, but this is better if for some reason LTCHAR changes. */ void init_ltbuf () { int n; for (n = 0; n < LTCOUNT; n++) { global_ltbuf[n] = LTCHAR; } } void make_lt (FILE *fpout) { fwrite (global_ltbuf, sizeof(char), LTCOUNT, fpout); } void make_ptp (FILE *fpin, FILE *fpout) { int inchar, outchar, prior = '\0'; int read_ct, n; char *obuffp; char ibuff[BLOCK_SIZE]; /* Every \n might add a \r to the output. Worst case is obuff doubles in size. */ char obuff[2*BLOCK_SIZE]; make_lt (fpout); while ((read_ct = fread (ibuff, sizeof(char), BLOCK_SIZE, fpin))) { obuffp = obuff; for (n = 0; n < read_ct; n++) { inchar = *(ibuff + n); if (inchar == '\n' && prior != '\r') { *obuffp++ = (char)('\r' | 0200); } *obuffp++ = inchar | 0200; prior = inchar; } fwrite (obuff, sizeof(char), obuffp - obuff, fpout); } /* If we don't already have an EOF, add one. */ if (inchar != '\032') { fwrite ("\232", sizeof(char), 1, fpout); } make_lt (fpout); } void process_file (char *fname, int flag) { FILE *fpin, *fpout; char *ofname; char *fend; if (flag == TO_PTP) fend = ".ptp"; else fend = ".txt"; ofname = malloc (((strlen (fname) + strlen(fend)) * sizeof (char)) + 1); strcpy (ofname, fname); strcat (ofname, fend); /* printf ("Filename is: %s.\n", ofname); */ if ((fpin = fopen (fname, "r")) == NULL) { printf ("Open of input file %s failed with status %d. Skipping.\n", fname, errno); return; } if ((fpout = fopen (ofname, "w")) == NULL) { printf ("Open of output file %s failed with status %d. Skipping.\n", ofname, errno); return; } if (flag == TO_PTP) make_ptp (fpin, fpout); else make_txt (fpin, fpout); fclose (fpin); fclose (fpout); free (ofname); } int main (int argc, char *argv[]) { int i, flag; char *ltbuf; if (strcmp (basename (argv[0]), "txt2ptp") == 0) { /* printf ("Flag is TO_PTP"); */ flag = TO_PTP; init_ltbuf (); } else { flag = TO_TXT; } if (argc == 1) { if (flag == TO_PTP) make_ptp (stdin, stdout); else make_txt (stdin, stdout); } else { for (i = 1; i < argc; i++) { process_file (argv[i], flag); } } } |
Added src/misc/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 | /* * 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 <pidp8i.h> #define short_wait() sleep_us(100000) #define pgpio (&gpio) int main() { int i,j,k,switchscan[2], tmp; extern struct bcm2835_peripheral gpio; if (map_gpio_for_pidp8i (1) != 0) { printf("Failed to map the GPIO SoC peripheral into our VM space.\n"); return -1; } init_pidp8i_gpio(); // Read the 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/misc/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 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 | /* test.c - Front panel LED and switch testing program, built as pidp8i-test Copyright © 2016-2017 Paul R. Bernard 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. */ #define _POSIX_C_SOURCE 200809L #include <pidp8i.h> #include <assert.h> #include <ctype.h> #include <curses.h> #include <pthread.h> #include <signal.h> #include <stdarg.h> #include <stddef.h> #include <stdlib.h> #include <string.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 }; static int auto_advance = 1; #define KEY_CHECK \ switch (getch()) { \ case KEY_DOWN: \ case KEY_LEFT: auto_advance = 0; return step - 1; \ case KEY_UP: \ case KEY_RIGHT: auto_advance = 0; return step + 1; \ case 'r': \ case 'R': auto_advance = 1; return step + 1; \ case 'x': \ case 'X': \ case 3: exit (1); \ } #define DISPLAY_HOLD_ITERATIONS 20 #define DISPLAY_HOLD \ sleep_us (250 * 1000); \ if (auto_advance) { putchar('.'); } else { dhi = 0; } \ KEY_CHECK typedef int (*action_handler)(int, int); struct action { action_handler ph; int step; int arg; }; static void banner (const char* b, ...) { va_list ap; va_start (ap, b); if (!auto_advance) { clear(); refresh(); printf ("Manual mode: Press x or Ctrl-C to exit, " "arrows to step, r to resume."); } printf ("\r\n"); vprintf (b, ap); va_end (ap); } int all_leds_on (int step, int ignored) { puts ("\r"); banner ("Turn on ALL LEDs"); for (int row = 0; row < NLEDROWS; ++row) ledstatus[row] = 07777; for (int dhi = 0; dhi < DISPLAY_HOLD_ITERATIONS; ++dhi) { DISPLAY_HOLD; } return step + 1; } int all_leds_off (int step, int ignored) { puts ("\r"); banner ("Turn off ALL LEDs"); memset (ledstatus, 0, sizeof(ledstatus)); for (int dhi = 0; dhi < DISPLAY_HOLD_ITERATIONS; ++dhi) { DISPLAY_HOLD; } return step + 1; } int led_row_on (int step, int row) { if (row == 0) puts ("\r"); memset (ledstatus, 0, sizeof(ledstatus)); banner ("Turning on LED row %d", row + 1); for (int dhi = 0; dhi < DISPLAY_HOLD_ITERATIONS / 4; ++dhi) { ledstatus[row] = 07777; DISPLAY_HOLD; } ledstatus[row] = 0; return step + 1; } int led_col_on (int step, int col) { if (col == 0) puts ("\r"); memset (ledstatus, 0, sizeof(ledstatus)); banner ("Turning on LED col %d", col + 1); for (int dhi = 0; dhi < DISPLAY_HOLD_ITERATIONS / 4; ++dhi) { for (int row = 0; row < NLEDROWS; ++row) ledstatus[row] |= 1 << col; DISPLAY_HOLD; } ledstatus[col] = 0; return step + 1; } int switch_scan (int step, int ignored) { int path_idx = 0, led_row = 0, delay = 0; puts ("\r"); banner ("Reading the switches. Toggle any pattern desired. " "Ctrl-C to quit.\r\n"); memset (ledstatus, 0, sizeof(ledstatus)); for (int i = 0; i < NROWS; ++i) lastswitchstatus[i] = switchstatus[i]; for (;;) { if (delay++ >= 30) { delay = 0; ledstatus[led_row] = 0; ledstatus[led_row = (path[path_idx] >> 4) - 1] = 04000 >> ((path[path_idx] & 0x0F) - 1); sleep_us (1); KEY_CHECK; 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 (int i = 0; i < NROWS; ++i) { printf ("%04o ", ~switchstatus[i] & 07777); lastswitchstatus[i] = switchstatus[i]; } printf ("\r\n"); } sleep_us (1000); } return step + 1; } void start_gpio (void) { assert (sizeof (lastswitchstatus == switchstatus)); // Tell the GPIO thread we're updating the display via direct // ledstatus[] manipulation instead of set_pidp8i_leds calls. pidp8i_simple_gpio_mode = 1; // Create GPIO thread if (start_pidp8i_gpio_thread ("test program") != 0) { exit (EXIT_FAILURE); } } // Tell ncurses we want character-at-a-time input without echo, // non-blocking reads, and no input interpretation. void init_ncurses (void) { initscr (); nodelay (stdscr, TRUE); keypad (stdscr, TRUE); noecho (); cbreak (); clear (); refresh (); } void run_actions (void) { // Define action sequence struct action actions[] = { { all_leds_on, 0, 0 }, { all_leds_off, 1, 0 }, { led_row_on, 2, 0 }, { led_row_on, 3, 1 }, { led_row_on, 4, 2 }, { led_row_on, 5, 3 }, { led_row_on, 6, 4 }, { led_row_on, 7, 5 }, { led_row_on, 8, 6 }, { led_row_on, 9, 7 }, { led_col_on, 10, 0 }, { led_col_on, 11, 1 }, { led_col_on, 12, 2 }, { led_col_on, 13, 3 }, { led_col_on, 14, 4 }, { led_col_on, 15, 5 }, { led_col_on, 16, 6 }, { led_col_on, 17, 7 }, { led_col_on, 18, 8 }, { led_col_on, 19, 9 }, { led_col_on, 20, 10 }, { led_col_on, 21, 11 }, { switch_scan, 22, 0 }, }; const size_t num_actions = sizeof(actions) / sizeof(actions[0]); // Run actions int i = 0; while (i < num_actions) { i = (actions[i].ph)(i, actions[i].arg); if (i == num_actions) i = 0; if (i == -1) i = num_actions - 1; } } static void emergency_shut_down (int signum) { // Shut ncurses down before we call printf, so it's down at the // bottom. This duplicates part of the graceful shutdown path, // which is why it checks whether it's necessary. endwin (); printf ("\r\nExiting pidp8i-test, signal %d: %s\n\n", signum, strsignal (signum)); exit (2); // continues in graceful_shut_down } static void graceful_shut_down () { if (!isendwin()) endwin (); turn_off_pidp8i_leds (); } static void register_shut_down_handlers () { struct sigaction sa; memset (&sa, 0, sizeof (sa)); sa.sa_handler = emergency_shut_down; sigaction (SIGINT, &sa, 0); atexit (graceful_shut_down); } int main (int argc, char *argv[]) { start_gpio (); if ((argc < 2) || (strcmp (argv[1], "-v") != 0)) { register_shut_down_handlers (); init_ncurses (); run_actions (); } return 0; } |
Added src/pidp8i/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 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 | /* * 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 * * This is part of the GPIO thread, which communicates with the * simulator's main thread via *pdis_update and switchstatus[]. * All of this module's other external interfaces are only called * by the other gpio-* modules, from the GPIO thread. */ #include "pidp8i.h" #include <config.h> #include <pthread.h> #include <sys/file.h> #include <sys/time.h> #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef HAVE_TIME_H # include <time.h> #endif #define BLOCK_SIZE (4*1024) //// GLOBALS /////////////////////////////////////////////////////////// // 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; // Flag external programs can set to make us use the old-style (and // simpler, hence still supported) ledstatus[] interface for updating // the LED values when swapping displays internally. The external // program doesn't need to know anything about struct display. int pidp8i_simple_gpio_mode = 0; // GPIO peripheral info, initted in start_pidp8i_gpio_thread() struct bcm2835_peripheral gpio; #define pgpio (&gpio) #ifdef PCB_SERIAL_MOD_JLW struct bcm2835_peripheral gpio2; #endif // 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 Oscar Vermeulen's serial mod // was done, as that affects the free GPIOs for our use, and how the PCB // connects them to the LED matrix. James L-W's mod leaves GPIO pin // numbering unchanged relative to the stock circuit design. #ifdef PCB_SERIAL_MOD_OV uint8_t cols[NCOLS] = {13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}; #else uint8_t cols[NCOLS] = {13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 15, 14}; #endif uint8_t ledrows[NLEDROWS] = {20, 21, 22, 23, 24, 25, 26, 27}; uint8_t rows[NROWS] = {16, 17, 18}; // Current switch states, as reported by the debouncing algorithm. Set // from the GPIO thread to control the SIMH CPU thread. uint16_t switchstatus[NROWS]; // Double-buffered LED display brightness values. The update-to copy is // modified by the SIMH CPU thread as it executes instructions, and the // paint-from copy is read by the gpio-*.c module in its "set LEDs" // loop. When the GPIO thread is finished with the paint-from copy, it // zeroes it and swaps it for the current "update-to" copy, giving the // CPU thread a blank slate, and giving the GPIO thread a stable set of // LED "on" time values to work with. display display_bufs[2]; display* pdis_update = display_bufs + 0; // exported to SIMH CPU thread display* pdis_paint = display_bufs + 1; // exported to gpio-*.c // GPIO thread control variables manipulated by start/stop_*() below static pthread_t gpio_thread_info; static int terminate_gpio_thread = 0; static pthread_mutex_t gpio_start_mutex; // 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]; static int gss_initted = 0; static const ms_time_t debounce_ms = 50; // time switch state must remain stable // Flow-control switch states which are owned by -- that is, primarily // modified by -- the PDP8/pidp8i module, but we can't define these // there because we refer to them below, and not all programs that link // to us link to that module as well. For such programs, it's fine if // these two flags stay 0. int swStop = 0, swSingInst = 0; // Flag to override ILS mode, forcing fallback to NLS mode. Set when // the PDP-8 instruction decoding loop detects that we're using the // ratio form of SET THROTTLE, which prevents the use of ILS due to the // way instructions are executed in that mode. Defined here rather than // in gpio-ils.c because we don't want to make code that sets this // conditional based on whether ILS is in fact actually enabled. int suppressILS = 0; // Flag set when sim_instr() exits due to some SIMH event like Ctrl-E, // which lets us resume from our imposed "pause" display state. int resumeFromInstructionLoopExit = 0; //// MEMORY MAPPED GPIO FUNCTIONS ////////////////////////////////////// //// map_peripheral //////////////////////////////////////////////////// // Exposes the physical address defined in the passed structure static int map_peripheral(struct bcm2835_peripheral *p, int exclusive) { // Name of GPIO memory-mapped device static const char* gpio_mem_dev = "/dev/gpiomem"; if (access(gpio_mem_dev, F_OK) < 0) { // That dev node isn't even present, so it's probably not a Pi return -1; } // 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 (exclusive && (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; } //// unmap_peripheral ////////////////////////////////////////////////// // Unwind the map_peripheral() steps in reverse order void unmap_peripheral(struct bcm2835_peripheral *p) { 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; } } //// bcm_host_get_peripheral_address /////////////////////////////////// // Find Pi's GPIO base address static unsigned bcm_host_get_peripheral_address(void) { unsigned address = ~0; FILE *fp = fopen("/proc/device-tree/soc/ranges", "rb"); if (fp) { unsigned char buf[4]; fseek(fp, 4, SEEK_SET); if (fread(buf, 1, sizeof buf, fp) == sizeof buf) address = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0; fclose(fp); } return address == ~0 ? 0x20000000 : address; } //// DOUBLE BUFFERED DISPLAY MANIPULATION FUNCTIONS //////////////////// //// swap_displays //////////////////////////////////////////////////// // Clear the current "paint-from" display, then exchange the double- // buffered display pointers atomically, saving the current update-to // display pointer as our paint-from display pointer and re-pointing // the update-to pointer at the now-zeroed paint-from values. This // gives the CPU thread a blank slate to begin modifying while the GPIO // thread consumes the values provided by the CPU thread. #define SWAP(dir) \ __atomic_exchange_n (&pdis_update, display_bufs + dir, __ATOMIC_SEQ_CST) void swap_displays () { if (!swStop && !swSingInst) { if (pidp8i_simple_gpio_mode) { // We're linked to a program that wants to use the old // ledstatus[] interface for updating the display, so copy // its values into the paint-from display structure and // return. We don't need to touch the update-to display or // swap anything, because set_pidp8i_leds won't be called. static const int levels = 32; memcpy (pdis_paint->curr, ledstatus, sizeof (pdis_paint->curr)); pdis_paint->inst_count = levels; for (size_t row = 0; row < NLEDROWS; ++row) { size_t *prow = pdis_paint->on[row]; for (size_t col = 0, mask = 1; col < NCOLS; ++col, mask <<= 1) { prow[col] = !!(ledstatus[row] & mask) * levels; } } } else { // Clear old paint-from display memset (pdis_paint, 0, sizeof(display)); // Send old paint-from display to CPU as new update-to // display, and overwrite paint-from pointer with prior // update-to pointer. pdis_paint = pdis_update == display_bufs + 0 ? SWAP(1) : SWAP(0); } } // else, leave current LED values as-is so we don't go to a black // screen while in STOP mode, either from front panel or HLT } //// FINE GRAINED SLEEP FUNCTIONS ////////////////////////////////////// //// sleep_ns ////////////////////////////////////////////////////////// // Like sleep(2) except that it takes nanoseconds instead of seconds 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 } //// ms_time /////////////////////////////////////////////////////////// // 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 = tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0; if (pt) *pt = t; return t; } else { return na_ms; } } //// SWITCH DEBOUNCING and READING FUNCTIONS /////////////////////////// //// report_ss ///////////////////////////////////////////////////////// // 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 } //// debounce_switch /////////////////////////////////////////////////// // Given the state of the switch at (row,col), work out if this requires // a change in our exported switch state. static 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 } //// read_switches ///////////////////////////////////////////////////// // Iterate through the switch GPIO pins, passing them to the debouncing // mechanism above for eventual reporting to the PDP-8 CPU thread. void read_switches (ns_time_t delay) { // Save current ms-since-epoch for debouncer. No point making it // retrieve this value for each switch. ms_time_t now_ms; ms_time(&now_ms); // Flip columns to input. Since the internal pull-ups are enabled, // this pulls all switch GPIO pins high that aren't shorted to the // row line by the switch. for (size_t i = 0; i < NCOLS; ++i) { INP_GPIO(cols[i]); } // Read the switch rows for (size_t i = 0; i < NROWS; ++i) { // Put 0V out on the switch row so that closed switches will // drag its column line down; give it time to settle. OUT_GPIO(rows[i]); GPIO_CLR = 1 << rows[i]; sleep_ns (delay); // Read all the switches in this row for (size_t j = 0; j < NCOLS; ++j) { int ss = GPIO_READ(cols[j]); debounce_switch(i, j, !!ss, now_ms); } // Stop sinking current from this row of switches INP_GPIO(rows[i]); } fflush(stdout); gss_initted = 1; } //// UNGROUPED FUNCTIONS /////////////////////////////////////////////// //// pi_type /////////////////////////////////////////////////////////// // Return a short string succinctly describing the type of Raspberry Pi // we're running on, or "cake" if it's not a pi. static const char* pi_type() { static char ac[60] = { '\0' }; static const char* prefix = "Raspberry Pi "; FILE* fp = fopen("/proc/device-tree/model", "r"); if (fp && fgets(ac, sizeof(ac), fp) && (strlen(ac) > 20) && (strstr(ac, prefix) == ac)) { const char* kind = ac + strlen(prefix); int series = 1; if (kind[0] == 'M') { // It's one of the "plus" models. const char* pm = kind + strlen("Model "); char model = *pm; model = (isalpha(model) && isupper(model)) ? tolower(model) : 'x'; snprintf(ac, sizeof(ac), "pi%d%c", series, model); } else if (kind[0] == 'C') { // It's one of the compute modules. We don't actually // support these, but we need to report them in case // someone tries it. const char* ps = kind + strlen("Compute Module"); char series = *ps; if (series) { // It's should be one of the later Compute Module series. series = isdigit(ps[1]) ? ps[1] : 'x'; snprintf(ac, sizeof(ac), "picm%c", series); } else { // We're at the end of the string, so it's the original // Compute Module. return "picm1"; } } else if (kind[0] == 'Z') { // It's a Pi Zero return "pi0"; } else if ((series = atoi(kind)) > 1) { // Pi 2 and newer have a number after the "Pi" char* pm = strstr(kind, " Model "); snprintf(ac, sizeof(ac), "pi%d%c", series, pm ? tolower(pm[7]) : 'x'); } else { // Not a model string we can parse, but it's some kind of // RPi. Two 'x's stand for unknown series and model. return "pixx"; } } else { return "cake"; // not pi } return ac; } //// update_led_states ///////////////////////////////////////////////// // Generic front panel LED updater used by NLS full time and by ILS // while the CPU is in STOP mode. Just uses the paint-from display's // bitfields to turn the LEDs on full-brightness. void update_led_states (const us_time_t delay) { uint16_t *pcurr = pdis_paint->curr; #if 0 // debugging static time_t last = 0, now; if (time(&now) != last) { printf("\r\nLED: [PC:%04o] [MA:%04o] [MB:%04o] [AC:%04o] [MQ:%04o]", pcurr[0], pcurr[1], pcurr[2], pcurr[3], pcurr[4]); last = now; } #endif // Override Execute and Run LEDs if the CPU is currently stopped, // since we only get set_pidp8i_leds calls while the CPU's running. if (swStop || swSingInst) { pdis_paint->curr[5] &= ~(1 << 2); pdis_paint->curr[6] &= ~(1 << 7); } for (size_t row = 0; row < NLEDROWS; ++row) { for (size_t col = 0; col < NCOLS; ++col) { if ((pcurr[row] & (1 << col)) == 0) { GPIO_SET = 1 << cols[col]; } else { GPIO_CLR = 1 << cols[col]; } } // Toggle this LED row on INP_GPIO (ledrows[row]); GPIO_SET = 1 << ledrows[row]; OUT_GPIO (ledrows[row]); sleep_us (delay); // Toggle this LED row off GPIO_CLR = 1 << ledrows[row]; // superstition INP_GPIO (ledrows[row]); // Small delay to reduce UDN2981 ghosting sleep_us (10); } } //// turn_off_pidp8i_leds ////////////////////////////////////////////// // Set GPIO pins into a state that disconnects all the LEDs from power. // Doesn't pay any attention to the panel values. void turn_off_pidp8i_leds () { for (size_t i = 0; i < NCOLS; ++i) { OUT_GPIO(cols[i]); } for (size_t row = 0; row < NLEDROWS; ++row) { INP_GPIO (ledrows[row]); } } //// init_pidp8i_gpio ////////////////////////////////////////////////// // Initialize the GPIO pins to the initial states required by // gpio_thread(). It's a separate exported function so that scanswitch // can also use it. void init_pidp8i_gpio (void) { // initialise GPIO (all pins used as inputs, with pull-ups enabled on cols) int i; for (i = 0; i < NLEDROWS; i++) { // Define ledrows as input INP_GPIO(ledrows[i]); GPIO_CLR = 1 << ledrows[i]; // so go to Low when switched to output } for (i = 0; i < NCOLS; i++) { // Define cols as input 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_OV // Oscar Vermeulen's serial mod the PiDP-8/I board rearranges the // GPIO matrix to use Pi GPIO pins 2..13, freeing up the hardware // serial port on GPIO pins 14 & 15. GPIO_PULLCLK0 = 0x03ffc; #else // The standard PiDP-8/I board drive scheme uses Pi GPIO pins 4..15. // (James L-W's alternative serial mod also uses this scheme.) GPIO_PULLCLK0 = 0x0fff0; #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 } //// gpio_thread /////////////////////////////////////////////////////// // The GPIO thread entry point: initializes GPIO and then calls // the gpio_core () implementation linked to this program. static void *gpio_thread (void *terminate) { // Set thread to real time priority struct sched_param sp; sp.sched_priority = 4; // not high, just above the minimum of 1 int rt = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp) == 0; // Tell the user about our configuration, succinctly const char* pt = pi_type(); printf( "PiDP-8/I @VERSION@ [%s] [%cls] [%spcb] [%sgpio]" #ifdef DEBUG " [debug]" #endif "%s", pt, ILS_MODE ? 'i' : 'n', pt[0] == 'p' ? #ifdef PCB_SERIAL_MOD_OV "sermod" : #elif PCB_SERIAL_MOD_JLW "altser" : #else "std" : #endif "no", (pidp8i_gpio_present ? "" : "no"), (rt ? " [rt]" : "") ); // It's okay for our caller to resume executing, if it's locked // waiting for us to finish the initialization bits that can only // happen in this thread. pthread_mutex_unlock (&gpio_start_mutex); // If we didn't map the GPIO peripheral and get here, it was // optional, and we've done all we can without it. if (!pidp8i_gpio_present) return (void*)-1; // Set GPIO pins to initial states init_pidp8i_gpio (); // Hand off control to the gpio_core () variant linked to this // program: either the new incandescent lamp simulator or the old // stock version. extern void gpio_core (struct bcm2835_peripheral*, int* terminate); gpio_core (&gpio, (int*)terminate); // gpio_core () leaves all cols, rows, ledrows are set to input, and // it's safe to leave them in that state. No need to de-init GPIO. gss_initted = 0; return 0; } //// map_gpio_for_pidp8i /////////////////////////////////////////////// // Wrapper around map_peripheral() taking care of some higher level // details. This is the interface we expose to outsiders. int map_gpio_for_pidp8i (int must_map) { // Find GPIO address (it varies by Pi model) unsigned int gpio_base_addr = bcm_host_get_peripheral_address(); gpio.addr_p = gpio_base_addr + 0x200000; // Attempt to map the GPIO peripheral for our exclusive use. Some // callers care if this fails, and some don't. map_peripheral (&gpio, 1); if (must_map && !pidp8i_gpio_present) return EFAULT; #ifdef PCB_SERIAL_MOD_JLW // James L-W's alternative serial mods were declared to be done here // at configure time, so disable the hysteresis on the GPIO inputs. map_peripheral (&gpio2, 0); // assume success since prior mapping worked gpio2.addr_p = gpio_base_addr + 0x100000; gpio2.addr[0x0B] = (gpio2.addr[0x0B] & 0xF7) | (0x5A << 24); #endif return 0; } //// start/stop_pidp8i_gpio_thread ///////////////////////////////////// // Start and stop gpio_thread(). We export these functions rather than // export gpio_thread() directly so this module's users don't have to // know anything about pthreads and such. int start_pidp8i_gpio_thread (const char* must_map) { char errs[100]; if (map_gpio_for_pidp8i (must_map != 0) != 0) { return EFAULT; } // Until gpio_core () reads the switches for the first time, we need // to mark them as all-open, lest we have a race condition with the // simulator where it interprets the all-0 initial switchstatus[] // value as "all switches closed," which includes the shutdown seq! memset (switchstatus, 0xFF, sizeof (switchstatus)); // Create the startup sequencing mutex and lock it once to block the // GPIO thread after it's sufficiently initialized. int pcerr; if ( ((pcerr = pthread_mutex_init (&gpio_start_mutex, NULL)) != 0) || ((pcerr = pthread_mutex_lock (&gpio_start_mutex)) != 0)) { perror ("GPIO startup sequence mutex creation failed"); return errno; } // Create the actual GPIO handler thread pcerr = pthread_create (&gpio_thread_info, NULL, gpio_thread, &terminate_gpio_thread); if (pcerr != 0) { int errlen = snprintf (errs, sizeof(errs), "Error creating " "PiDP-8/I GPIO thread: %s\n", strerror (pcerr)); if (errlen > 0 && errlen < sizeof(errs)) write (2, errs, errlen); } else if (must_map && !pidp8i_gpio_present) { int errlen = snprintf (errs, sizeof(errs), "Cannot run the %s " "while another PiDP-8/I program runs.\r\n", must_map); if (errlen > 0 && errlen < sizeof(errs)) write (2, errs, errlen); pcerr = EACCES; } else { // Don't return until GPIO thread is sufficiently initted. // // There are two possible sequences: // // 1. We get here before the GPIO thread gets to its "unlock" // call, so our back-to-back locks in this thread stall this // thread until the GPIO thread gets to its unlock condition, // which removes the first lock taken above, and our second // unlock call below removes the second lock. // // 2. The GPIO thread hits its "unlock" call before we get here, // so it unlocks the lock we made above, so that these two // calls happen back to back, with no real effect. We got // here too late to cause any problem that this interlock // solves, so it just locks and immediately unlocks. pthread_mutex_lock (&gpio_start_mutex); pthread_mutex_unlock (&gpio_start_mutex); } return pcerr; } void stop_pidp8i_gpio_thread () { terminate_gpio_thread = 1; if (pthread_join (gpio_thread_info, NULL)) { printf("\r\nError joining multiplex thread\r\n"); } unmap_peripheral (&gpio); #ifdef PCB_SERIAL_MOD_JLW unmap_peripheral (&gpio2); #endif pthread_mutex_destroy (&gpio_start_mutex); } |
Added src/pidp8i/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 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 | /* * 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 // Number of LED and switch rows and columns on the PiDP-8/I PCB #define NCOLS 12 #define NLEDROWS 8 #define NROWS 3 // 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 uint64_t us_time_t; typedef uint64_t ms_time_t; typedef struct display { // Counters incremented each time the LED is known to be turned on, // in instructions since the last display paint. size_t on[NLEDROWS][NCOLS]; // Most recent state for each LED, for use by NLS full-time and by // ILS in STOP mode. (One bitfield per row.) uint16_t curr[NLEDROWS]; // Number of instructions executed since this display was cleared int inst_count; } display; extern display* pdis_update, *pdis_paint; // Compatibility interface for programs like src/test.c that depend on // being able to modify the LED values directly. #define ledstatus (pdis_update->curr) extern int pidp8i_simple_gpio_mode; // Simplified interface for single-threaded programs that don't use our // GPIO thread, but do want to share some our implementation. extern void init_pidp8i_gpio (void); extern int map_gpio_for_pidp8i (int must_map); extern uint16_t switchstatus[]; extern uint8_t cols[]; extern uint8_t ledrows[]; extern uint8_t rows[]; extern uint8_t pidp8i_gpio_present; extern int start_pidp8i_gpio_thread (const char* must_map); extern void stop_pidp8i_gpio_thread (); extern void turn_off_pidp8i_leds (); extern void update_led_states (const us_time_t delay); // Alternative to start_pidp8i_gpio_thread() for programs that run // single-threaded and do their own GPIO scanning, like scanswitch. extern void init_pidp8i_gpio (void); extern void unmap_peripheral(struct bcm2835_peripheral *p); extern void read_switches (ns_time_t delay); extern void swap_displays (); extern void sleep_ns(ns_time_t ns); #define sleep_us(us) sleep_ns(us * 1000) #define sleep_ms(ms) sleep_us(ms * 1000) extern ms_time_t ms_time(ms_time_t* pt); #endif // !defined(PIDP8I_GPIO_H) |
Added src/pidp8i/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 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 | /* * gpio-ils.c: implements gpio_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 <pidp8i.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 LED's "on" // time back to it as its new brightness, so that it takes several // iterations at that same "on" time for the LED to achieve that // brightness level. Because the delta is based on the prior value, we // get nonlinear asymptotic increase/decrease behavior. // // 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.012 #define FALLING_FACTOR 0.005 //// gpio_core //////////////////////////////////////////////////////// // The GPIO module's main loop core, called from thread entry point in // gpio-common.c. void gpio_core (struct bcm2835_peripheral* pgpio, int* terminate) { // The ILS version uses an iteration rate 60x faster than the NLS // version because we have to get through 32 PWM steps, each of // which takes roughly 2 * intervl µs. There's a bit of extra delay // over intervl in the NLS version, so the while loop iteration time // is about the same for both versions. const us_time_t intervl = 20; // Current brightness level for each LED. It goes from 0 to // MAX_BRIGHTNESS, but we keep it as a float because the decay // function smoothly ramps from the current value to the ever- // changing target value. float brightness[NLEDROWS][NCOLS]; memset(brightness, 0, sizeof (brightness)); // Brightness target for each LED, updated at the start of each PWM // cycle when we get fresh "on" counts from the CPU thread. uint8 br_targets[NLEDROWS][NCOLS]; memset(br_targets, 0, sizeof (br_targets)); // Current PWM brightness step uint8 step = MAX_BRIGHTNESS; while (*terminate == 0) { // Prepare for lighting LEDs by setting col pins to output for (size_t i = 0; i < NCOLS; ++i) OUT_GPIO(cols[i]); // Restart PWM cycle if prior one is complete if (step == MAX_BRIGHTNESS) { // Reset PWM step counter step = 0; // Go get the current LED "on" times, and give the SIMH // CPU thread a blank copy to begin updating. Because we're // in control of the swap timing, we don't need to copy the // pdis_paint pointer: it points to the same thing between // these swap_displays() calls. swap_displays(); // Recalculate the brightness target values based on the // "on" counts in *pdis_paint and the quantized brightness // level, which is based on the number of instructions // executed for this display update. // // Handle the cases where inst_count is < 32 specially // because we don't want all LEDs to go out when the // simulator is heavily throttled. const size_t inst_count = pdis_paint->inst_count; size_t br_quant = inst_count >= 32 ? (inst_count >> 5) : 1; for (int row = 0; row < NLEDROWS; ++row) { size_t *prow = pdis_paint->on[row]; for (int col = 0; col < NCOLS; ++col) { br_targets[row][col] = prow[col] / br_quant; } } // Hard-code the Fetch and Execute brightnesses; in running // mode, they're both on half the instruction time, so we // just set them to 50% brightness. Execute differs in STOP // mode, but that's handled in update_led_states () because // we fall back to NLS in STOP mode. br_targets[5][2] = br_targets[5][3] = MAX_BRIGHTNESS / 2; } ++step; // Update the brightness values. for (int row = 0; row < NLEDROWS; ++row) { size_t *prow = pdis_paint->on[row]; for (int col = 0; col < NCOLS; ++col) { uint8 br_target = br_targets[row][col]; float *p = brightness[row] + col; if (*p <= br_target) { *p += (br_target - *p) * RISING_FACTOR; } else { *p -= *p * FALLING_FACTOR; } } } // Light up LEDs extern int swStop, swSingInst, suppressILS; if (swStop || swSingInst || suppressILS) { // The CPU is in STOP mode or someone has suppressed the ILS, // so show the current LED states full-brightness using the // same mechanism NLS uses. Force a display swap if the next // loop iteration won't do it in case this isn't STOP mode. update_led_states (intervl * 60); if (step != (MAX_BRIGHTNESS - 1)) swap_displays(); } else { // Normal case: PWM display using the on-count values for (size_t row = 0; row < NLEDROWS; ++row) { // Output 0 (CLR) for LEDs in this row which should be on size_t *prow = pdis_paint->on[row]; for (size_t col = 0; col < NCOLS; ++col) { if (brightness[row][col] >= step) { GPIO_CLR = 1 << cols[col]; } else { GPIO_SET = 1 << cols[col]; } } // Toggle this LED row on INP_GPIO(ledrows[row]); GPIO_SET = 1 << ledrows[row]; OUT_GPIO(ledrows[row]); sleep_us(intervl); // Toggle this LED row off GPIO_CLR = 1 << ledrows[row]; // superstition INP_GPIO(ledrows[row]); sleep_ns(5); } } #if 0 // debugging static time_t last = 0, now; if (time(&now) != last) { float* p = brightness[0]; #define B(n) (p[n] / MAX_BRIGHTNESS * 100.0) printf("\r\nPC:" " [%3.0f%%][%3.0f%%][%3.0f%%]" " [%3.0f%%][%3.0f%%][%3.0f%%]" " [%3.0f%%][%3.0f%%][%3.0f%%]" " [%3.0f%%][%3.0f%%][%3.0f%%]", B(11), B(10), B(9), B(8), B(7), B(6), B(5), B(4), B(3), B(2), B(1), B(0)); last = now; } #endif // 625 = * 1000 / (100 / 60) where 60 is the difference in // iteration rate between ILS and NLS. See the same call // in gpio-nls.c. read_switches(intervl * 625); #if defined(HAVE_SCHED_YIELD) sched_yield(); #endif } } |
Added src/pidp8i/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 | /* * gpio-nls.c: implements gpio_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 "pidp8i.h" //// gpio_core //////////////////////////////////////////////////////// // The GPIO module's main loop core, called from thread entry point in // gpio-common.c. void gpio_core (struct bcm2835_peripheral* pgpio, int* terminate) { // Light each row of LEDs 1.2 ms. With 8 rows, that's an update // rate of ~100x per second. Not coincidentally, this is the human // persistence of vision limit: changes faster than this are // difficult for humans to perceive visually. const us_time_t intervl = 1200; // This is a simplified version of what's in the gpio-ils.c version // of this function, so if you want more comments, read them there. while (*terminate == 0) { for (size_t i = 0; i < NCOLS; ++i) OUT_GPIO(cols[i]); swap_displays (); update_led_states (intervl); read_switches (intervl * 1000 / 100); #if defined(HAVE_SCHED_YIELD) sched_yield (); #endif } } |
Added src/pidp8i/main.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 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 | /* 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 <PDP8/pdp8_defs.h> #include <assert.h> #include <dirent.h> // for USB stick searching #include <errno.h> #include <string.h> //// MODULE GLOBALS //////////////////////////////////////////////////// // handle_sing_step() sets this to nonzero and returns a value breaking // us out of the PDP-8 simulator's sim_instr() loop, which causes SCP to // call our build_pidp8i_scp_cmd(), which gives SCP a command to run: // either "exit" when it wants the simulator to stop (e.g the shutdown // and reboot combos) or "do $script" on IF + SING_STEP combo. // // We loop the flow control from this module out into the generic SIMH // code and then back in here so we don't have to export this global. // Basically, this module global lets us remember what handle_sing_step // wants SCP to do in the window between switch handling time and SCP // command handling time. static enum { CMD_NONE = 0, // "do nothing" idle case CMD_DO_BOOTSCRIPT_1, // SING_STEP + IF combos CMD_DO_BOOTSCRIPT_2, CMD_DO_BOOTSCRIPT_3, CMD_DO_BOOTSCRIPT_4, CMD_DO_BOOTSCRIPT_5, CMD_DO_BOOTSCRIPT_6, CMD_DO_BOOTSCRIPT_7, CMD_EXIT, } insert_scp_cmd = CMD_NONE; //// build_pidp8i_scp_cmd ////////////////////////////////////////////// // If insert_scp_cmd is nonzero, we return the corresponding SCP command // we want run to make the simulator do something else. char *build_pidp8i_scp_cmd (char *cbuf, size_t cbufsize) { if (insert_scp_cmd == CMD_NONE) { return 0; // nothing to do yet } else if ((insert_scp_cmd > 0) && (insert_scp_cmd <= 7)) { // We got one of the IF + SING_STEP combos, so restart the // simulator with the corresponding init script. char path[256]; snprintf (path, sizeof (path), "@BOOTDIR@/%d.script", insert_scp_cmd); insert_scp_cmd = CMD_NONE; // it's a one-shot if (access(path, R_OK) == 0) { snprintf (cbuf, cbufsize, "do %s", path); return cbuf; } else { // That boot script doesn't exist or isn't readable. // // Fall through to the "exit" command builder below because // we don't want to keep coming back in here at host CPU // speed, failing the same way and issuing the same error // until the slow human manages to flip the offending switch // back. This is especially annoying when the PiDP-8/I is // attached to a slow serial console. Ask me how I know. int access_errno = errno; // preserve it from getcwd() char cwd[256]; getcwd (cwd, sizeof (cwd)); printf ("Cannot read %s from %s: %s!\n", path, cwd, strerror (access_errno)); } } else if (insert_scp_cmd > CMD_EXIT) { printf ("Invalid PiDP-8/I SCP command value %d given!\n", insert_scp_cmd); } else { // C doesn't require that "if" statements handle all cases // statically, so do a runtime check to make sure we have // exhausted all the other cases above. // // We can get in here by means other than programmer error in // modifying the enum: some C compilers allow signed values // to be assigned to enums, so we could get in here on negative // values. We can't test for that above because one of the C // compilers we build under (clang on macOS 10.12+) won't allow // that short of nasty low-level hackery, so it complains if we // test for negative values, claiming it can never happen. assert (insert_scp_cmd == CMD_EXIT); } // If we get here, we got a nonzero command value but didn't get // into the happy path above, so die. return strncpy (cbuf, "exit", cbufsize); } //// set_pidp8i_led //////////////////////////////////////////////////// // Sets the current state for a single LED at the given row and column // on the PiDP-8/I PCB. Also increments the LED on-count value for // that LED. // // You may say, "You can't just use the C postincrement operator here! // Look at the assembly output! You must use an atomic increment for // this!" And indeed, there is a big difference between the two // methods: https://godbolt.org/g/0Qt0Ap // // The thing is, both structures referred to by pdis_* are fixed in RAM, // and the two threads involved are arranged in a strict producer-and- // consumer fashion, so it doesn't actually matter if pdis_update gets // swapped for pdis_paint while we're halfway through an increment: we // get a copy of the pointer to dereference here, so we'll finish our // increment within the same structure we started with, even if // pdis_update points at the other display structure before we leave. static inline void set_pidp8i_led (display *pd, size_t row, size_t col) { ++pd->on[row][col]; pd->curr[row] |= 1 << col; } //// set_pidp8i_row_leds /////////////////////////////////////////////// // Like set_pidp8i_led, except that it takes a 12-bit state value for // setting all LEDs on the given row. Because we copy the pdis_update // pointer before making changes, if the display swap happens while // we're working, we'll simply finish updating what has become the // paint-from display, which is what you want; you don't want the // updates spread over both displays. static inline void set_pidp8i_row_leds (display *pd, size_t row, uint16 state) { size_t *prow = pd->on[row]; pd->curr[row] = state; for (size_t col = 0, mask = 1; col < NCOLS; ++col, mask <<= 1) { if (state & mask) ++prow[col]; } } //// set_3_pidp8i_leds ///////////////////////////////////////////////// // Special case of set_pidp8i_row_leds for the DF and IF LEDs: we only // pay attention to bits 12, 13, and 14 of the given state value, // because SIMH's PDP-8 simulator shifts those 3 bits up there so it can // simply OR these 3-bit registers with PC to produce a 15-bit extended // address. // // We don't take a row parameter because we know which row they're on, // but we do take a column parameter so we can generalize for IF & DF. static inline void set_3_pidp8i_leds (display *pd, size_t col, uint16 state) { static const int row = 7; // DF and IF are on row 6 size_t *prow = pd->on[row]; size_t last_col = col + 3; pd->curr[row] |= state >> (12 - col); for (size_t mask = 1 << 12; col < last_col; ++col, mask <<= 1) { if (state & mask) ++prow[col]; } } //// set_5_pidp8i_leds ///////////////////////////////////////////////// // Like set_3... but for the 5-bit SC register. Because it's only used // for that purpose, we don't need the col parameter. static inline void set_5_pidp8i_leds (display *pd, uint16 state) { static const int row = 6; // SC is on row 6 size_t *prow = pd->on[row]; size_t last_col = 7; pd->curr[row] |= (state & 0x1f) << 2; for (size_t col = 2, mask = 1; col < last_col; ++col, mask <<= 1) { if (state & mask) ++prow[col]; } } //// get_pidp8i_initial_max_skips ////////////////////////////////////// // Return the number of times we should skip updating the front panel // LEDs the first time thru, to give the simulator time to settle. // If we don't do this, the front panel LEDs can start out dim and // slowly rise or they can overshoot and then take a while to recover // with the IPS. size_t get_pidp8i_initial_max_skips (size_t updates_per_sec) { DEVICE *pthrot = find_dev ("INT-THROTTLE"); if (pthrot) { extern int suppressILS; REG *ptyper = find_reg ("THROT_TYPE", NULL, pthrot); REG *pvalr = find_reg ("THROT_VAL", NULL, pthrot); if (ptyper && pvalr) { uint32 *ptype = ptyper->loc; uint32 *pval = pvalr->loc; size_t ips = 0; switch (*ptype) { case SIM_THROT_MCYC: ips = *pval * 1e6; break; case SIM_THROT_KCYC: ips = *pval * 1e3; break; case SIM_THROT_SPC: { suppressILS = 1; break; } } if (ips) { suppressILS = 0; printf("PiDP-8/I initial throttle = %zu IPS\r\n", ips); return ips / updates_per_sec; } } } // No better idea, so give a plausible value for an unthrottled Pi 1 return 200; } //// set_pidp8i_leds /////////////////////////////////////////////////// // Given all of the PDP-8's internal registers that affect the front // panel display, modify the GPIO thread's LED state values accordingly. // // Also update the LED brightness values based on those new states. void set_pidp8i_leds (uint32 sPC, uint32 sMA, uint16 sIR, int32 sLAC, int32 sMQ, int32 sIF, int32 sDF, int32 sSC, int32 int_req, int Pause) { // Bump the instruction count. This should always be equal to the // Fetch LED's value, but integers are too cheap to get cute here. // // Note that we only update pdis_update directly once in this whole // process. This is in case the display swap happens while we're // working: we want to finish work on the same display even though // it's now called the paint-from display, so it's consistent. display* pd = pdis_update; ++pd->inst_count; // Rows 0-4, easy cases: single-register LED strings. // // The only non-obvious one is that we use the PDP-8 simulator's IR // register value for the MB lights due to a quirk in the way the // register states are set at the time this function is called; MB // is not passed to us because its value at the call time is bogus. set_pidp8i_row_leds (pd, 0, sPC); set_pidp8i_row_leds (pd, 1, sMA); set_pidp8i_row_leds (pd, 2, sIR); set_pidp8i_row_leds (pd, 3, sLAC & 07777); set_pidp8i_row_leds (pd, 4, sMQ); #if 0 // debugging static time_t last = 0, now; if (time(&now) != last) { uint16* pcurr = pd->curr; printf("\r\nSET: [PC:%04o] [MA:%04o] [MB:%04o] [AC:%04o] [MQ:%04o]", pcurr[0], pcurr[1], pcurr[2], pcurr[3], pcurr[4]); last = now; } #endif // Row 5a: instruction type column, decoded from high octal // digit of IR value pd->curr[5] = 0; uint16 inst_type = sIR & 07000; switch (inst_type) { case 00000: set_pidp8i_led (pd, 5, 11); break; // 000 AND case 01000: set_pidp8i_led (pd, 5, 10); break; // 001 TAD case 02000: set_pidp8i_led (pd, 5, 9); break; // 010 DCA case 03000: set_pidp8i_led (pd, 5, 8); break; // 011 ISZ case 04000: set_pidp8i_led (pd, 5, 7); break; // 100 JMS case 05000: set_pidp8i_led (pd, 5, 6); break; // 101 JMP case 06000: set_pidp8i_led (pd, 5, 5); break; // 110 IOT case 07000: set_pidp8i_led (pd, 5, 4); break; // 111 OPR 1 & 2 } // Row 5b: set the Defer LED if... if ((inst_type <= 05000) && // it's a memory reference instruction (sIR & 00400)) { // and indirect addressing flag is set set_pidp8i_led (pd, 5, 1); } // Row 5c: The Fetch & Execute LEDs are pulsed once per instruction. // On real hardware, the pulses don't happen at exactly the same // time, but we can't simulate that because SIMH handles each CPU // instruction "whole." When running real code, all we care about // is that both LEDs are twiddled so rapidly that they both just // become a 50% blur, mimicking the hardware closely enough. // // The exception is that when the CPU is stopped, both LEDs are off, // because the pulses happen "outside" the STOP state: Fetch before // and Execute after resuming from STOP. extern int swStop, swSingInst; int running = !swStop && !swSingInst; if (running) { set_pidp8i_led (pd, 5, 2); // Execute set_pidp8i_led (pd, 5, 3); // Fetch } // Row 6a: Remaining LEDs in upper right block pd->curr[6] = 0; if (running) set_pidp8i_led (pd, 6, 7); // bump Run LED if (Pause) set_pidp8i_led (pd, 6, 8); // bump Pause LED if (int_req & INT_ION) set_pidp8i_led (pd, 6, 9); // bump ION LED // Row 6b: The Step Count LEDs are also on row 6 set_5_pidp8i_leds (pd, sSC); // Row 7: DF, IF, and Link. pd->curr[7] = 0; set_3_pidp8i_leds (pd, 9, sDF); set_3_pidp8i_leds (pd, 6, sIF); if (sLAC & 010000) set_pidp8i_led (pd, 7, 5); // If we're stopped or single-stepped, the display-swapping code // won't happen, so copy the above over to the paint-from version. extern int resumeFromInstructionLoopExit; if (!running || resumeFromInstructionLoopExit) { memcpy(pdis_paint, pdis_update, sizeof(struct display)); } } //// mount_usb_stick_file ////////////////////////////////////////////// // Search for a PDP-8 media image on a USB device mounted under /media // and attempt to ATTACH it to the simulator. static void mount_usb_stick_file (int devNo, char *devCode) { char sFoundFile[CBUFSIZE] = { '\0' }; char sDirName[CBUFSIZE]; // will be "/media/DIRNAME" etc char fileExtension[4]; // will be ".RX" etc int i, j; // Build expected file name extension from the first two characters of // the passed-in device code. fileExtension[0] = '.'; // extension starts with a . strncpy (fileExtension + 1, devCode, 2); // extension is PT, RX, RL etc fileExtension[3] = '\0'; // chop off device number #if 0 // debugging printf("\r\nMOUNT USB: [DEV:%d] [CODE:%s], [EXT:%s]", devNo, devCode, fileExtension); #endif // 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'; // Search all directories under /media DIR *pDir1 = opendir ("/media"); if (pDir1) { struct dirent* pDE1; while ((pDE1 = readdir (pDir1)) != 0) { if (pDE1->d_type != DT_DIR) continue; // Found a directory under /media. Search it for plausibly // named files given devCode. snprintf (sDirName, sizeof(sDirName), "/media/%s", pDE1->d_name); DIR *pDir2 = opendir (sDirName); if (pDir2) { struct dirent* pDE2; while ((pDE2 = readdir (pDir2)) != 0) { // search all files in directory if (pDE2->d_name[0] == '.') continue; // dotfiles clutter debug output char* pext = strstr (pDE2->d_name, fileExtension); if (pext && (pext == (pDE2->d_name + strlen (pDE2->d_name) - 3))) { snprintf (sFoundFile, sizeof (sFoundFile), "%s/%s", sDirName, pDE2->d_name); #if 0 // debugging printf("\r\nFound candidate file %s for dev %s, ext *%s...", sFoundFile, devCode, fileExtension); #endif for (j = 0; j < 7; ++j) { if (strncmp (mountedFiles[j], sFoundFile, CBUFSIZE) == 0) { #if 0 // debugging printf("\r\nAlready have %s mounted, slot %d; will not remount.", sFoundFile, j); #endif sFoundFile[0] = '\0'; // don't leave outer loop; keep looking break; } } if (j == 7) { // Media image file is not already mounted, so leave while // loop with path set to mount it break; } } #if 0 // debugging else { printf("\r\nFile %s on %s doesn't match *%s...", pDE2->d_name, sDirName, fileExtension); } #endif } // end while (pDE2...) closedir (pDir2); } // end if (pDir2) else { // USB auto-mounting either doesn't work here or uses // something other than the /media/DIR/FILE.EXT scheme // we expect. printf ("\r\nCannot open %s: %s\r\n", sDirName, strerror (errno)); return; } } // end while (pDE1...) closedir(pDir1); } // end if (pDir1) if (sFoundFile[0]) { // no file found, exit if (access (sFoundFile, R_OK) == 0) { char sAttachCmd[CBUFSIZE] = { '\0' }; snprintf (sAttachCmd, sizeof(sAttachCmd), "%s %s", devCode, sFoundFile); t_stat scpCode = attach_cmd ((int32) 0, sAttachCmd); if (scpCode == SCPE_OK) { // add file to mount list strncpy (mountedFiles[devNo], sFoundFile, CBUFSIZE); printf ("\r\nMounted %s %s\r\n", devCode, mountedFiles[devNo]); } else { // SIMH ATTACH command failed printf ("\r\nSIMH error mounting %s on %s: %s\r\n", sFoundFile, devCode, sim_error_text (scpCode)); } } 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, "rk1"); break; // second RK05 disk pack } 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) { printf ("\r\n\nRestarting with IF == %d...\r\n\r\n", swScript); insert_scp_cmd = swScript; 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"); insert_scp_cmd = CMD_EXIT; 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"); insert_scp_cmd = CMD_EXIT; 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... extern int swSingInst; if (((switchstatus[2] & SS2_S_INST) == 0) && (swSingInst == 0)) { // Put the processor in single-instruction 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; } // ...and SING_INST switch open extern int swStop; if (swSingInst && (switchstatus[2] & SS2_S_INST)) { swSingInst = 0; swStop = 1; // still stopped on leaving SING_INST mode } // Check for START switch press... static int swStart = 0; if (((switchstatus[2] & SS2_START) == 0) && (swStart == 0)) { // Reset the CPU. extern DEVICE cpu_dev; extern t_stat cpu_reset (DEVICE *); cpu_reset (&cpu_dev); // DEC's docs say there are a few additional things START does // that cpu_reset() doesn't do for us. // // Don't need to do anything with MA and IR, as SIMH does that // shortly after this function returns. *pLAC = *pMB = 0; // cpu_reset() does its thing to the saved_* register copies // in a few cases, but we need it to happen to the "real" // registers instead, since our STOP/START behavior doesn't // make use of saved_*. REG* pibr = find_reg ("IB", NULL, &cpu_dev); int32* pIB = pibr ? pibr->loc : 0 /* force segfault on err */ ; *pIB = *pIF; // Reset our switch flags, too swStop = 0; // START cancels STOP mode swSingInst = 0; // allow SING INST mode re-entry swStart = 1; // make it single-shot #if 0 // debugging printf("\r\nSTART: [DF:%o] [IF:%o] [IB:%o] [PC:%04o] " "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]", (*pDF >> 12), (*pIF >> 12), (*pIB >> 12), (*pPC & 07777), *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777); #endif } // ...and START switch release if (swStart && (switchstatus[2] & SS2_START)) { swStart = 0; } // Check for CONT switch press... static int swCont = 0; extern int resumeFromInstructionLoopExit; if ((((switchstatus[2] & SS2_CONT) == 0) && (swCont == 0)) || resumeFromInstructionLoopExit) { // The initial CONT press is special: how we handle it // depends on the processor's state. // // FIXME: Are we handling MB correctly? [973271ae36] swCont = 1; // make it single-shot resumeFromInstructionLoopExit = 0; if (swSingInst) { // On the initial CONT press while in SING_INST mode, run // one instruction only. return pft_normal; } else if (swStop) { // We were HLTed or STOPped, so CONT returns us to // free-running mode. swStop = 0; #if 0 // debugging printf("\r\nCONT: [DF:%o] [IF:%o] [PC:%04o] " "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]", (*pDF >> 12), (*pIF >> 12), (*pPC & 07777), *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777); #endif } // else, CONT has no effect in this state } // ...and CONT switch release if (swCont && (switchstatus[2] & SS2_CONT)) { swCont = 0; } // Check for LOAD_ADD switch press. The only reason we bother // making it single-shot is in case debugging is enabled. // Otherwise, it matters not how long the slow human holds this // swithc down, and thus how often we apply the values: all else // but our printf() here is idempotent. static int swLAdd = 0; if ((swLAdd == 0) && (switchstatus[2] & SS2_L_ADD) == 0) { // Copy SR into PC. Have to flip the bits because GPIO gives // 0 for a closed switch and 1 for open, opposite what we want. *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 did 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; #if 0 // debugging printf("\r\nL_ADD: [DF:%o] [IF:%o] [PC:%04o] " "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]", (*pDF >> 12), (*pIF >> 12), (*pPC & 07777), *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777); #endif swLAdd = 1; // make it single-shot } // ...and L_ADD switch release if (swLAdd && (switchstatus[2] & SS2_L_ADD)) { swLAdd = 0; } // Check for DEP switch press... static int swDep = 0; if (((switchstatus[2] & SS2_DEP) == 0) && (swDep == 0)) { uint16 sSR = (~switchstatus[0]) & 07777; // bit flip justified above *pPC = *pPC & 07777; // sometimes high bits get set; squish 'em #if 0 // debugging printf("\r\nDEP: [IF:%o] [PC:%04o] [SR:%04o]", (*pIF >> 12), *pPC, sSR); #endif /* ??? in 66 handbook: strictly speaking, SR goes into AC, then AC into MB. Does it clear AC afterwards? If not, needs fix */ pM[*pPC] = sSR; // FIXME: shouldn't we use IF/DF here? *pMB = sSR; *pMA = *pPC & 07777; // MA trails PC on FP; FIXME: OR in IF? *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 (!swStop && ((switchstatus[2] & SS2_STOP) == 0)) { swStop = 1; #if 0 // debugging printf("\r\nSTOP: [DF:%o] [IF:%o] [PC:%04o] " "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]", (*pDF >> 12), (*pIF >> 12), (*pPC & 07777), *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777); #endif } // If any of the above put us into STOP or SING_INST mode, go no // further. In particular, fetch no more instructions, and do not // touch PC! The only way to get un-stuck is CONT or STOP. return (swStop || swSingInst) ? pft_stop : pft_normal; } //// get_switch_register /////////////////////////////////////////////// // Return the current contents of the switch register int32 get_switch_register (void) { return switchstatus[0] ^ 07777; } |
Added src/pidp8i/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 | /* 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 "gpio-common.h" #include <stdint.h> #include <stdlib.h> typedef enum { pft_normal, pft_halt, pft_stop, } pidp8i_flow_t; extern char *build_pidp8i_scp_cmd (char* cbuf, size_t cbufsize); extern int32_t get_switch_register (void); extern size_t get_pidp8i_initial_max_skips (size_t updates_per_sec); extern pidp8i_flow_t handle_flow_control_switches (uint16_t* pM, uint32_t *pPC, uint32_t *pMA, int32_t *pMB, int32_t *pLAC, int32_t *pIF, int32_t *pDF, int32_t* pint_req); extern void set_pidp8i_leds (uint32_t sPC, uint32_t sMA, uint16_t sIR, int32_t sLAC, int32_t sMQ, int32_t sIF, int32_t sDF, int32_t sSC, int32_t int_req, int Pause); #endif // !defined(PIDP8I_H) |
Deleted src/ptp2txt.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/scanswitch.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/scp.c.
more than 10,000 changes
Deleted src/scp.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_console.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_console.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_defs.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_disk.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_disk.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_ether.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_ether.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_fio.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_fio.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_frontpanel.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_rev.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_serial.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_serial.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_sock.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_sock.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_tape.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_tape.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_timer.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_timer.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_tmxr.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_tmxr.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_video.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/sim_video.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/test.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to 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 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 | #!/bin/bash # 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. # Display the usage message function usage() { cat <<USAGE usage: $0 <verb> [tag] The available verbs are init, build, prepare, shrink, image, and finish. You may include a tag parameter with the 'image' and 'finish' verbs to override the default tag ('ils') used in image and zip file outputs. See RELEASE-PROCESS.md for more info. USAGE exit 1 } verb="$1" tag="$2" test -n "$verb" || usage test -z "$tag" && tag=ils nu=pidp8i nh=/home/$nu repo=pidp8i dldir="$HOME/tangentsoft.com/dl" os=stretch-lite img=$dldir/pidp8i-$(date +%Y.%m.%d)-$tag-$os.img greadlink=$(type -p greadlink || type -p readlink) this=$($greadlink -f $0) topdir="$($greadlink -f "$(dirname "$this")"/..)" # Give user a few seconds to read the final messages before rebooting or # powering off. function do_wait() { n=8 cat <<MSG $1 in $n seconds... MSG sleep $n } # Initial steps function do_init() { if [ "$USER" != "root" ] then echo "The init step has to be run as root. The explanation is" echo "given in the section for 'init' in RELEASE-PROCESS.md." echo exit 1 fi set -x apt-get update && apt-get -y upgrade || true apt-get install python-pip || true pip install pexpect test -f /usr/include/curses.h || apt-get -y install libncurses-dev if [ -z "$(type -p fossil)" ] then fb=/usr/local/bin/fossil src=https://tangentsoft.com/pidp8i/uv/fossil-raspbian-9.1-stretch wget -O $fb $src chmod +x $fb apt-get -y install libssl-dev # user might rebuild Fossil later fi if [ ! -e "$nh" ] then # First pass on a clean SD card: rename 'pi' user and group to # 'pidp8i' and rename its home directory to match. pkill -u pi usermod -l $nu -d $nh -m pi groupmod -n $nu pi fi reboot } # Clone repo and build the software under [new] pidp8i account function do_build() { if [ "$USER" != "$nu" ] then echo "The build step has to be run as $nu." echo exit 1 fi set -x 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 fi tools/mmake bin/teco-pi-demo -b # test and benchmark simulator sudo make install || true # don't care about return code do_wait "Rebooting" sudo reboot } # This script prepares the OS's configuration to a clean first boot state. function do_prepare() { if [ "$USER" != "$nu" ] then echo "The prepare step has to be run as $nu." echo exit 1 fi set -x history -c ; rm -f ~/.bash_history sudo systemctl stop pidp8i || true # avoid sim hogging CPU sudo systemctl enable ssh || true # disabled by default sudo shred -u /etc/ssh/*key* || true # allow multiple passes sudo dphys-swapfile uninstall || true sudo dd if=/dev/zero of=/junk bs=1M || true # it *will* error-out! sudo rm -f /junk encpass=$(openssl passwd -1 edsonDeCastro1968) sudo usermod -p $encpass pidp8i sudo passwd -e pidp8i cl=/boot/cmdline.txt if ! grep -Fq ' init=' $cl then sudo sed -i -e 's#$# init=/usr/lib/raspi-config/init_resize.sh#' $cl fi do_wait "Powering off" sudo poweroff } # Shrink the filesystem on the OS SD card we're about to image to just a # bit bigger than required to hold its present contents. # # 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 set -x umount /dev/sda2 || true # might auto-mount, might not e2fsck -f /dev/sda2 # resize2fs demands it # Pack it down tight blocks=$( resize2fs -M /dev/sda2 2>&1 | grep 'blocks long' | grep -wo '[0-9]\+' ) if [ "$blocks" -gt 0 ] then # And now give it a bit of breathing room parted /dev/sda resizepart 2 $(($blocks * 4096 + 10**8))b resize2fs /dev/sda2 poweroff 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() { dev=UNKNOWN ps=UNKNOWN hb=no rp=UNKNOWN while read line do case $line in /dev/*) dev=$(echo $line | cut -f1 -d' ') ;; 0:*) case $line in *FDisk_partition_scheme*) ps=fdisk ;; *) dev= ;; # can't be the OS SD card esac ;; 1:*) case $line in *Windows_FAT_32\ boot*) hb=yes ;; *) dev= ;; # can't be the OS SD card esac ;; 2:*) case $line in *Linux*) rp=Linux ; break ;; # found it! *) dev= ;; # can't be the OS SD card esac ;; esac done < <(diskutil list) if [ -z "$dev" ] then cat <<MSG Failed to find OS SD card! I learned the following, though: SD /dev node: $dev Partition scheme: $ps Has /boot: $hb /root partition: $rp MSG 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 rdev=${dev/disk/rdisk} # speeds zeroing and re-imaging mf=/tmp/MANIFEST.txt readme=/tmp/README.md cp "$topdir/doc/OS-images.md" $readme set -x diskutil unmountDisk $dev # it auto-mounted sudo time dd if=$rdev bs=1m of=$img sum=($(shasum -a 256 "$img")) bytes=($(wc -c $img)) cat > $mf <<MF SHA-256 hash and size of ${sum[1]}: Hash: ${sum[0]} Size: ${bytes[0]} MF imgdir="$(dirname "$img")" sed -i '' -e "s_$imgdir/__" "$mf" # nix local paths in manifest unix2dos $mf # might be opened on Windows time zip -9j $img.zip $img $mf $readme rm -f $mf $readme # Now re-image the card, starting with a zeroed card to ensure a # clean test. Ignore the end-of-device error from the zero step. sudo time dd if=/dev/zero of=$rdev bs=1m || true sudo time dd if=$img of=$rdev bs=1m diskutil unmountDisk $dev || true # Paragon ExtFS might be installed } # Clean up after the above function do_finish() { set -x rmtrash $img cd $dldir/.. make synch } # Main routine set -e case "$verb" in in*) do_init ;; bu*) do_build ;; pr*) do_prepare ;; sh*) do_shrink ;; im*) do_image ;; fi*) do_finish ;; *) usage ;; esac |
Changes to tools/mkadrules.
︙ | ︙ | |||
94 95 96 97 98 99 100 | my $odir = 'obj/' . $sdir; $odir =~ s{/src}{}; my $cflags = uc $sdir; $cflags =~ s{SRC}{}; $cflags =~ s{^/}{}; $cflags =~ s{/}{_}g; | | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | my $odir = 'obj/' . $sdir; $odir =~ s{/src}{}; my $cflags = uc $sdir; $cflags =~ s{SRC}{}; $cflags =~ s{^/}{}; $cflags =~ s{/}{_}g; $cflags = $cflags ? "${cflags}_CFLAGS" : 'SIM_CFLAGS'; $rule =~ s{obj/}{$odir/}g; $rule =~ s{src/}{$sdir/}g; $rule =~ s{CFLAGS}{$cflags}g; $rule =~ s{SRCDIR}{$srcdir}g; $rule =~ s{ \./}{ }g; print $ad $rule, "\n"; |
︙ | ︙ |
Changes to tools/simh-update.in.
︙ | ︙ | |||
27 28 29 30 31 32 33 | # 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@" | | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | # 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/SIMH" 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=) |
︙ | ︙ | |||
97 98 99 100 101 102 103 | say "$errmsg" say exit $code } trap 'error ${LINENO}' ERR # Deal with uncommitted changes in $SRCSUBDIR | | | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | say "$errmsg" say exit $code } trap 'error ${LINENO}' ERR # Deal with uncommitted changes in $SRCSUBDIR cd "$SRCSUBDIR/.." # we need the SIMH/ prefix to do this test properly! if [ $(fossil status | grep '^EDITED.*SIMH/' | wc -l) -gt 0 ] then if [ "$1" = "-f" ] then say "Tossing uncommitted changes in $SRCSUBDIR..." fossil revert $(fossil status | grep '^EDITED.*SIMH/' | 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 |
︙ | ︙ | |||
219 220 221 222 223 224 225 | 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 | | | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | 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/SIMH 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 |
︙ | ︙ |